]> wimlib.net Git - wimlib/commitdiff
Rewrite code for capture rpfix
authorEric Biggers <ebiggers3@gmail.com>
Fri, 16 May 2014 01:13:34 +0000 (20:13 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Fri, 16 May 2014 01:23:44 +0000 (20:23 -0500)
The previous code had many potential problems on the Windows side.
Targets of Windows native symbolic links (except those with
SYMBOLIC_LINK_RELATIVE set) and junction points are stored in the reparse
points as NT namespace paths.  Therefore they can validly include
prefixes like '\??\', '\DosDevices\', or '\Device\HardDiskVolume1'.  They
potentially do not use drive letters at all, and they may be over
MAX_PATH characters.  Such paths should be accessed using the native API
(NtOpenFile), not Win32 (CreateFile).  This commit changes the code to do
so.

include/wimlib/reparse.h
include/wimlib/win32.h
src/reparse.c
src/unix_capture.c
src/win32_capture.c
src/win32_replacements.c

index f37e2f4d2d4f1b352f4413aabe1e95dc4e385e20..04974a1280415f7fdd64694a77d17720f0b46684 100644 (file)
@@ -77,8 +77,4 @@ wim_inode_set_symlink(struct wim_inode *inode, const char *target,
                      struct wim_lookup_table *lookup_table);
 #endif
 
-extern tchar *
-capture_fixup_absolute_symlink(tchar *dest,
-                              u64 capture_root_ino, u64 capture_root_dev);
-
 #endif /* _WIMLIB_REPARSE_H */
index 67220786a249a031c037b7e58267566bbc143274..9d47ec6cc87990b594be2d2a9975ec9ff127e32a 100644 (file)
@@ -47,9 +47,6 @@ win32_truncate_replacement(const tchar *path, off_t size);
 extern int
 win32_strerror_r_replacement(int errnum, tchar *buf, size_t buflen);
 
-extern int
-win32_get_file_and_vol_ids(const wchar_t *path, u64 *ino_ret, u64 *dev_ret);
-
 extern ssize_t
 pread(int fd, void *buf, size_t count, off_t offset);
 
index 2bc421d2a57b424f3e8bea4d82295523132d0355..28976927c0abdbeeda87f954cef5f35e748cd877 100644 (file)
 #include "wimlib/assert.h"
 #include "wimlib/compiler.h"
 #include "wimlib/endianness.h"
-#include "wimlib/dentry.h"
 #include "wimlib/encoding.h"
 #include "wimlib/error.h"
+#include "wimlib/inode.h"
 #include "wimlib/lookup_table.h"
 #include "wimlib/reparse.h"
 #include "wimlib/resource.h"
 
-#ifdef __WIN32__
-#  include "wimlib/win32.h" /* for win32_get_file_and_vol_ids() */
-#endif
-
 #ifdef HAVE_ALLOCA_H
 #  include <alloca.h>
 #endif
@@ -321,7 +317,7 @@ wim_inode_get_reparse_data(const struct wim_inode * restrict inode,
 }
 
 /* UNIX version of getting and setting the data in reparse points */
-#if !defined(__WIN32__)
+#ifndef __WIN32__
 
 /*
  * Get the UNIX-style symlink target from the WIM inode for a reparse point.
@@ -523,95 +519,4 @@ wim_inode_set_symlink(struct wim_inode *inode,
        return ret;
 }
 
-#include <sys/stat.h>
-
-static int
-unix_get_ino_and_dev(const char *path, u64 *ino_ret, u64 *dev_ret)
-{
-       struct stat stbuf;
-       if (stat(path, &stbuf)) {
-               if (errno != ENOENT)
-                       WARNING_WITH_ERRNO("Failed to stat \"%s\"", path);
-               /* Treat as a link pointing outside the capture root (it
-                * most likely is). */
-               return WIMLIB_ERR_STAT;
-       } else {
-               *ino_ret = stbuf.st_ino;
-               *dev_ret = stbuf.st_dev;
-               return 0;
-       }
-}
-
-#endif /* !defined(__WIN32__) */
-
-/* is_rp_path_separator() - characters treated as path separators in absolute
- * symbolic link targets */
-
-#ifdef __WIN32__
-#  define is_rp_path_separator(c) ((c) == L'\\' || (c) == L'/')
-#  define os_get_ino_and_dev win32_get_file_and_vol_ids
-#else
-#  define is_rp_path_separator(c) ((c) == '/')
-#  define os_get_ino_and_dev unix_get_ino_and_dev
-#endif
-
-/* Fix up absolute symbolic link targets--- mostly shared between UNIX and
- * Windows */
-tchar *
-capture_fixup_absolute_symlink(tchar *dest,
-                              u64 capture_root_ino, u64 capture_root_dev)
-{
-       tchar *p = dest;
-
-#ifdef __WIN32__
-       /* Skip drive letter */
-       if (!is_rp_path_separator(*dest))
-               p += 2;
-#endif
-
-       DEBUG("Fixing symlink or junction \"%"TS"\"", dest);
-       for (;;) {
-               tchar save;
-               int ret;
-               u64 ino;
-               u64 dev;
-
-               while (is_rp_path_separator(*p))
-                       p++;
-
-               save = *p;
-               *p = T('\0');
-               ret = os_get_ino_and_dev(dest, &ino, &dev);
-               *p = save;
-
-               if (ret) /* stat() failed before we got to the capture root---
-                           assume the link points outside it. */
-                       return NULL;
-
-               if (ino == capture_root_ino && dev == capture_root_dev) {
-                       /* Link points inside capture root.  Return abbreviated
-                        * path. */
-                       if (*p == T('\0'))
-                               *(p - 1) = OS_PREFERRED_PATH_SEPARATOR;
-                       while (p - 1 >= dest && is_rp_path_separator(*(p - 1)))
-                               p--;
-               #ifdef __WIN32__
-                       if (!is_rp_path_separator(dest[0])) {
-                               *--p = dest[1];
-                               *--p = dest[0];
-                       }
-               #endif
-                       wimlib_assert(p >= dest);
-                       return p;
-               }
-
-               if (*p == T('\0')) {
-                       /* Link points outside capture root. */
-                       return NULL;
-               }
-
-               do {
-                       p++;
-               } while (!is_rp_path_separator(*p) && *p != T('\0'));
-       }
-}
+#endif /* !__WIN32__ */
index a76fbc37f7689b7c329f55a821783152e31bd451..c6619b00d1e402acf84d1dea3df0eb0d88a0e7e6 100644 (file)
@@ -201,6 +201,54 @@ unix_scan_directory(struct wim_dentry *dir_dentry,
        return ret;
 }
 
+/* Given an absolute symbolic link target @dest (UNIX-style, beginning
+ * with '/'), determine whether it points into the directory specified by
+ * @ino and @dev.  If so, return the target modified to be "absolute"
+ * relative to this directory.  Otherwise, return NULL.  */
+static char *
+unix_fixup_abslink(char *dest, u64 ino, u64 dev)
+{
+       char *p = dest;
+
+       do {
+               char save;
+               struct stat stbuf;
+               int ret;
+
+               /* Skip non-slashes.  */
+               while (*p && *p != '/')
+                       p++;
+
+               /* Skip slashes.  */
+               while (*p && *p == '/')
+                       p++;
+
+               /* Get inode and device for this prefix.  */
+               save = *p;
+               *p = '\0';
+               ret = stat(dest, &stbuf);
+               *p = save;
+
+               if (ret) {
+                       /* stat() failed.  Assume the link points outside the
+                        * directory tree being captured.  */
+                       break;
+               }
+
+               if (stbuf.st_ino == ino && stbuf.st_dev == dev) {
+                       /* Link points inside directory tree being captured.
+                        * Return abbreviated path.  */
+                       *--p = '/';
+                       while (p > dest && *(p - 1) == '/')
+                               p--;
+                       return p;
+               }
+       } while (*p);
+
+       /* Link does not point inside directory tree being captured.  */
+       return NULL;
+}
+
 static int
 unix_scan_symlink(struct wim_dentry **root_p, const char *full_path,
                  int dirfd, const char *relpath,
@@ -232,9 +280,9 @@ unix_scan_symlink(struct wim_dentry **root_p, const char *full_path,
        if ((params->add_flags & WIMLIB_ADD_FLAG_RPFIX) &&
             dest[0] == '/')
        {
-               dest = capture_fixup_absolute_symlink(dest,
-                                                     params->capture_root_ino,
-                                                     params->capture_root_dev);
+               dest = unix_fixup_abslink(dest,
+                                         params->capture_root_ino,
+                                         params->capture_root_dev);
                if (!dest) {
                        /* RPFIX (reparse point fixup) mode:  Ignore
                         * absolute symbolic link that points out of the
index ace087da33abed497ce67d6942e9e2b73a020440..09dc3edc251a0d5407d4856202116425685d55d1 100644 (file)
@@ -454,137 +454,239 @@ out_free_buf:
        return ret;
 }
 
-/* Reparse point fixup status code */
+/* Reparse point fixup status code  */
 enum rp_status {
-       /* Reparse point corresponded to an absolute symbolic link or junction
-        * point that pointed outside the directory tree being captured, and
-        * therefore was excluded. */
-       RP_EXCLUDED       = 0x0,
-
-       /* Reparse point was not fixed as it was either a relative symbolic
-        * link, a mount point, or something else we could not understand. */
-       RP_NOT_FIXED      = 0x1,
-
-       /* Reparse point corresponded to an absolute symbolic link or junction
-        * point that pointed inside the directory tree being captured, where
-        * the target was specified by a "full" \??\ prefixed path, and
-        * therefore was fixed to be relative to the root of the directory tree
-        * being captured. */
-       RP_FIXED_FULLPATH = 0x2,
-
-       /* Same as RP_FIXED_FULLPATH, except the absolute link target did not
-        * have the \??\ prefix.  It may have begun with a drive letter though.
-        * */
-       RP_FIXED_ABSPATH  = 0x4,
-
-       /* Either RP_FIXED_FULLPATH or RP_FIXED_ABSPATH. */
-       RP_FIXED          = RP_FIXED_FULLPATH | RP_FIXED_ABSPATH,
+       /* Reparse point should be excluded from capture  */
+       RP_EXCLUDED     = -0,
+
+       /* Reparse point will be captured literally (no fixup)  */
+       RP_NOT_FIXED    = -1,
+
+       /* Reparse point will be captured with fixup  */
+       RP_FIXED        = -2,
 };
 
-/* Given the "substitute name" target of a Windows reparse point, try doing a
- * fixup where we change it to be absolute relative to the root of the directory
- * tree being captured.
+static bool
+file_has_ino_and_dev(HANDLE h, u64 ino, u64 dev)
+{
+       NTSTATUS status;
+       IO_STATUS_BLOCK iosb;
+       FILE_INTERNAL_INFORMATION int_info;
+       FILE_FS_VOLUME_INFORMATION vol_info;
+
+       status = (*func_NtQueryInformationFile)(h, &iosb,
+                                               &int_info, sizeof(int_info),
+                                               FileInternalInformation);
+       if (!NT_SUCCESS(status))
+               return false;
+
+       if (int_info.IndexNumber.QuadPart != ino)
+               return false;
+
+       status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
+                                                     &vol_info, sizeof(vol_info),
+                                                     FileFsVolumeInformation);
+       if (!(NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW))
+               return false;
+
+       if (iosb.Information <
+            offsetof(FILE_FS_VOLUME_INFORMATION, VolumeSerialNumber) +
+            sizeof(vol_info.VolumeSerialNumber))
+               return false;
+
+       return (vol_info.VolumeSerialNumber == dev);
+}
+
+/*
+ * Given an (expected) NT namespace symbolic link or junction target @target of
+ * length @target_nbytes, determine if a prefix of the target points to a file
+ * identified by @capture_root_ino and @capture_root_dev.
+ *
+ * If yes, return a pointer to the portion of the link following this prefix.
  *
- * Note that this is only executed when WIMLIB_ADD_FLAG_RPFIX has been
- * set.
+ * If no, return NULL.
  *
- * @capture_root_ino and @capture_root_dev indicate the inode number and device
- * of the root of the directory tree being captured.  They are meant to identify
- * this directory (as an alternative to its actual path, which could potentially
- * be reached via multiple destinations due to other symbolic links).  This may
- * not work properly on FAT, which doesn't seem to supply proper inode numbers
- * or file IDs.  However, FAT doesn't support reparse points so this function
- * wouldn't even be called anyway.
+ * If the link target does not appear to be a valid NT namespace path, return
+ * @target itself.
  */
-static enum rp_status
-winnt_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
-                                u64 capture_root_ino, u64 capture_root_dev,
-                                u32 rptag)
+static const wchar_t *
+winnt_get_root_relative_target(const wchar_t *target, size_t target_nbytes,
+                              u64 capture_root_ino, u64 capture_root_dev)
 {
-       u16 target_nchars = *target_nbytes_p / 2;
-       size_t stripped_chars;
-       wchar_t *orig_target;
-       int ret;
+       UNICODE_STRING name;
+       OBJECT_ATTRIBUTES attr;
+       IO_STATUS_BLOCK iosb;
+       NTSTATUS status;
+       const wchar_t *target_end;
+       const wchar_t *p;
 
-       ret = parse_substitute_name(target, *target_nbytes_p, rptag);
-       if (ret < 0)
-               return RP_NOT_FIXED;
-       stripped_chars = ret;
-       if (stripped_chars)
-               stripped_chars -= 2;
-       target[target_nchars] = L'\0';
-       orig_target = target;
-       target = capture_fixup_absolute_symlink(target + stripped_chars,
-                                               capture_root_ino, capture_root_dev);
-       if (!target)
-               return RP_EXCLUDED;
-       target_nchars = wcslen(target);
-       wmemmove(orig_target + stripped_chars, target, target_nchars + 1);
-       *target_nbytes_p = (target_nchars + stripped_chars) * sizeof(wchar_t);
-       if (stripped_chars)
-               return RP_FIXED_FULLPATH;
-       else
-               return RP_FIXED_ABSPATH;
+       target_end = target + (target_nbytes / sizeof(wchar_t));
+
+       /* Empty path??? */
+       if (target_end == target)
+               return target;
+
+       /* No leading slash???  */
+       if (target[0] != L'\\')
+               return target;
+
+       /* UNC path???  */
+       if ((target_end - target) >= 2 &&
+           target[0] == L'\\' && target[1] == L'\\')
+               return target;
+
+       attr.Length = sizeof(attr);
+       attr.RootDirectory = NULL;
+       attr.ObjectName = &name;
+       attr.Attributes = 0;
+       attr.SecurityDescriptor = NULL;
+       attr.SecurityQualityOfService = NULL;
+
+       name.Buffer = (wchar_t *)target;
+       name.Length = 0;
+       p = target;
+       do {
+               HANDLE h;
+               const wchar_t *orig_p = p;
+
+               /* Skip non-backslashes  */
+               while (p != target_end && *p != L'\\')
+                       p++;
+
+               /* Skip backslashes  */
+               while (p != target_end && *p == L'\\')
+                       p++;
+
+               /* Append path component  */
+               name.Length += (p - orig_p) * sizeof(wchar_t);
+               name.MaximumLength = name.Length;
+
+               /* Try opening the file  */
+               status = (*func_NtOpenFile) (&h,
+                                            FILE_READ_ATTRIBUTES | FILE_TRAVERSE,
+                                            &attr,
+                                            &iosb,
+                                            FILE_SHARE_VALID_FLAGS,
+                                            FILE_OPEN_FOR_BACKUP_INTENT);
+
+               if (NT_SUCCESS(status)) {
+                       /* Reset root directory  */
+                       if (attr.RootDirectory)
+                               (*func_NtClose)(attr.RootDirectory);
+                       attr.RootDirectory = h;
+                       name.Buffer = (wchar_t *)p;
+                       name.Length = 0;
+
+                       if (file_has_ino_and_dev(h, capture_root_ino,
+                                                capture_root_dev))
+                               goto out_close_root_dir;
+               }
+       } while (p != target_end);
+
+       p = NULL;
+
+out_close_root_dir:
+       if (attr.RootDirectory)
+               (*func_NtClose)(attr.RootDirectory);
+       return p;
 }
 
-/* Returns: `enum rp_status' value on success; negative WIMLIB_ERR_* value on
- * failure. */
-static int
-winnt_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
-                       u64 capture_root_ino, u64 capture_root_dev,
-                       const wchar_t *path, struct add_image_params *params)
+static enum rp_status
+winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
+               u64 capture_root_ino, u64 capture_root_dev,
+               const wchar_t *path, struct add_image_params *params)
 {
        struct reparse_data rpdata;
-       int ret;
-       enum rp_status rp_status;
+       const wchar_t *rel_target;
 
-       ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata);
-       if (ret)
-               return -ret;
-
-       rp_status = winnt_capture_maybe_rpfix_target(rpdata.substitute_name,
-                                                    &rpdata.substitute_name_nbytes,
-                                                    capture_root_ino,
-                                                    capture_root_dev,
-                                                    le32_to_cpu(*(le32*)rpbuf));
-       if (rp_status & RP_FIXED) {
-               wimlib_assert(rpdata.substitute_name_nbytes % 2 == 0);
-               utf16lechar substitute_name_copy[rpdata.substitute_name_nbytes / 2];
-               wmemcpy(substitute_name_copy, rpdata.substitute_name,
-                       rpdata.substitute_name_nbytes / 2);
-               rpdata.substitute_name = substitute_name_copy;
-               rpdata.print_name = substitute_name_copy;
-               rpdata.print_name_nbytes = rpdata.substitute_name_nbytes;
-               if (rp_status == RP_FIXED_FULLPATH) {
-                       /* "full path", meaning \??\ prefixed.  We should not
-                        * include this prefix in the print name, as it is
-                        * apparently meant for the filesystem driver only. */
-                       rpdata.print_name += 4;
-                       rpdata.print_name_nbytes -= 8;
-               }
-               ret = make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
-               if (ret == 0)
-                       ret = rp_status;
-               else
-                       ret = -ret;
-       } else {
-               if (rp_status == RP_EXCLUDED) {
-                       /* Ignoring absolute symbolic link or junction point
-                        * that points out of the tree to be captured.  */
-                       size_t print_name_nchars = rpdata.print_name_nbytes / 2;
-                       wchar_t print_name0[print_name_nchars + 1];
-                       print_name0[print_name_nchars] = L'\0';
-                       wmemcpy(print_name0, rpdata.print_name, print_name_nchars);
-
-                       params->progress.scan.cur_path = printable_path(path);
-                       params->progress.scan.symlink_target = print_name0;
-                       do_capture_progress(params,
-                                           WIMLIB_SCAN_DENTRY_EXCLUDED_SYMLINK,
-                                           NULL);
-               }
-               ret = rp_status;
+       if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
+               /* Couldn't even understand the reparse data.  Don't try the
+                * fixup.  */
+               return RP_NOT_FIXED;
        }
-       return ret;
+
+       /*
+        * Don't do reparse point fixups on relative symbolic links.
+        *
+        * On Windows, a relative symbolic link is supposed to be identifiable
+        * by having reparse tag WIM_IO_REPARSE_TAG_SYMLINK and flags
+        * SYMBOLIC_LINK_RELATIVE.  We will use this information, although this
+        * may not always do what the user expects, since drive-relative
+        * symbolic links such as "\Users\Public" have SYMBOLIC_LINK_RELATIVE
+        * set, in addition to truely relative symbolic links such as "Users" or
+        * "Users\Public".  However, WIMGAPI (as of Windows 8.1) has this same
+        * behavior.
+        *
+        * Otherwise, as far as I can tell, the targets of symbolic links that
+        * are NOT relative, as well as junctions (note: a mountpoint is the
+        * sames thing as a junction), must be NT namespace paths, for example:
+        *
+        *     - \??\e:\Users\Public
+        *     - \DosDevices\e:\Users\Public
+        *     - \Device\HardDiskVolume4\Users\Public
+        *     - \??\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public
+        *     - \DosDevices\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public
+        */
+       if (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
+           (rpdata.rpflags & SYMBOLIC_LINK_RELATIVE))
+               return RP_NOT_FIXED;
+
+       rel_target = winnt_get_root_relative_target(rpdata.substitute_name,
+                                                   rpdata.substitute_name_nbytes,
+                                                   capture_root_ino,
+                                                   capture_root_dev);
+       if (!rel_target) {
+               /* Target points outside of the tree being captured.  Exclude
+                * this reparse point from the capture (but inform the library
+                * user).  */
+               size_t print_name_nchars = rpdata.print_name_nbytes / sizeof(wchar_t);
+               wchar_t print_name0[print_name_nchars + 1];
+               print_name0[print_name_nchars] = L'\0';
+               wmemcpy(print_name0, rpdata.print_name, print_name_nchars);
+
+               params->progress.scan.cur_path = printable_path(path);
+               params->progress.scan.symlink_target = print_name0;
+               do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED_SYMLINK, NULL);
+               return RP_EXCLUDED;
+       }
+
+       if (rel_target == rpdata.substitute_name) {
+               /* Weird target --- keep the reparse point and don't mess with
+                * it.  */
+               return RP_NOT_FIXED;
+       }
+
+       /* We have an absolute target pointing within the directory being
+        * captured, @rel_target is the suffix of the link target that is the
+        * part relative to the directory being captured.
+        *
+        * We will cut off the prefix before this part (which is the path to the
+        * directory being captured) and add a dummy prefix.  Since the process
+        * will need to be reversed when applying the image, it shouldn't matter
+        * what exactly the prefix is, as long as it looks like an absolute
+        * path.
+        */
+
+       {
+               size_t rel_target_nbytes =
+                       rpdata.substitute_name_nbytes - ((const u8 *)rel_target -
+                                                        (const u8 *)rpdata.substitute_name);
+               size_t rel_target_nchars = rel_target_nbytes / sizeof(wchar_t);
+
+               wchar_t tmp[rel_target_nchars + 7];
+
+               wmemcpy(tmp, L"\\??\\X:\\", 7);
+               wmemcpy(tmp + 7, rel_target, rel_target_nchars);
+
+               rpdata.substitute_name = tmp;
+               rpdata.substitute_name_nbytes = rel_target_nbytes + (7 * sizeof(wchar_t));
+               rpdata.print_name = tmp + 4;
+               rpdata.print_name_nbytes = rel_target_nbytes + (3 * sizeof(wchar_t));
+
+               if (make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p))
+                       return RP_NOT_FIXED;
+       }
+       return RP_FIXED;
 }
 
 /*
@@ -592,66 +694,57 @@ winnt_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
  * fixing the targets of absolute symbolic links and junction points to be
  * relative to the root of capture.
  *
- * @h:     Open handle to the reparse point.
- * @path:   Path to the reparse point file.
- * @params: Additional parameters, including whether to do reparse point fixups
- *          or not.
- * @rpbuf:  Buffer of length at least REPARSE_POINT_MAX_SIZE bytes into which
- *          the reparse point buffer will be loaded.
- * @rpbuflen_ret:  On success, the length of the reparse point buffer in bytes
- *                 is written to this location.
- *
- * Returns:
- *     On success, returns an `enum rp_status' value that indicates if and/or
- *     how the reparse point fixup was done.
+ * @h:
+ *     Open handle to the reparse point file.
+ * @path:
+ *     Path to the reparse point file.
+ * @params:
+ *     Capture parameters.  add_flags, capture_root_ino, capture_root_dev,
+ *     progress_func, and progress are used.
+ * @rpbuf:
+ *     Buffer of length at least REPARSE_POINT_MAX_SIZE bytes into which the
+ *     reparse point buffer will be loaded.
+ * @rpbuflen_ret:
+ *     On success, the length of the reparse point buffer in bytes is written
+ *     to this location.
  *
- *     On failure, returns a negative value that is a negated WIMLIB_ERR_*
- *     code.
+ * On success, returns a nonpositive `enum rp_status' value.
+ * On failure, returns a positive error code.
  */
 static int
 winnt_get_reparse_data(HANDLE h, const wchar_t *path,
                       struct add_image_params *params,
                       u8 *rpbuf, u16 *rpbuflen_ret)
 {
-       DWORD bytesReturned;
+       DWORD bytes_returned;
        u32 reparse_tag;
        int ret;
        u16 rpbuflen;
 
        if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT,
-                            NULL, /* "Not used with this operation; set to NULL" */
-                            0, /* "Not used with this operation; set to 0" */
-                            rpbuf, /* "A pointer to a buffer that
-                                                  receives the reparse point data */
-                            REPARSE_POINT_MAX_SIZE, /* "The size of the output
-                                                       buffer, in bytes */
-                            &bytesReturned,
-                            NULL))
+                            NULL, 0, rpbuf, REPARSE_POINT_MAX_SIZE,
+                            &bytes_returned, NULL))
        {
                set_errno_from_GetLastError();
-               return -WIMLIB_ERR_READ;
+               return WIMLIB_ERR_READ;
        }
 
-       if (bytesReturned < 8 || bytesReturned > REPARSE_POINT_MAX_SIZE) {
+       if (unlikely(bytes_returned < 8)) {
                errno = EINVAL;
-               return -WIMLIB_ERR_INVALID_REPARSE_DATA;
+               return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
 
-       rpbuflen = bytesReturned;
+       rpbuflen = bytes_returned;
        reparse_tag = le32_to_cpu(*(le32*)rpbuf);
+       ret = RP_NOT_FIXED;
        if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
            (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
             reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
        {
-               /* Try doing reparse point fixup */
-               ret = winnt_capture_try_rpfix(rpbuf,
-                                             &rpbuflen,
-                                             params->capture_root_ino,
-                                             params->capture_root_dev,
-                                             path,
-                                             params);
-       } else {
-               ret = RP_NOT_FIXED;
+               ret = winnt_try_rpfix(rpbuf, &rpbuflen,
+                                     params->capture_root_ino,
+                                     params->capture_root_dev,
+                                     path, params);
        }
        *rpbuflen_ret = rpbuflen;
        return ret;
@@ -1096,19 +1189,20 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                rpbuf = alloca(REPARSE_POINT_MAX_SIZE);
                ret = winnt_get_reparse_data(h, full_path, params,
                                             rpbuf, &rpbuflen);
-               if (ret < 0) {
-                       /* WIMLIB_ERR_* (inverted) */
-                       ret = -ret;
-                       ERROR_WITH_ERRNO("\"%ls\": Can't get reparse data",
-                                        printable_path(full_path));
-                       goto out;
-               } else if (ret & RP_FIXED) {
-                       not_rpfixed = 0;
-               } else if (ret == RP_EXCLUDED) {
+               switch (ret) {
+               case RP_EXCLUDED:
                        ret = 0;
                        goto out;
-               } else {
+               case RP_FIXED:
+                       not_rpfixed = 0;
+                       break;
+               case RP_NOT_FIXED:
                        not_rpfixed = 1;
+                       break;
+               default:
+                       ERROR_WITH_ERRNO("\"%ls\": Can't get reparse data",
+                                        printable_path(full_path));
+                       goto out;
                }
        }
 
index 39f031bb4d372f496e76c9b3d53716a586701d89..25a3ae08c96e0a2ea720720fe2f08013ecfe8c40 100644 (file)
@@ -269,36 +269,6 @@ pwrite(int fd, const void *buf, size_t count, off_t offset)
        return do_pread_or_pwrite(fd, (void*)buf, count, offset, true);
 }
 
-int
-win32_get_file_and_vol_ids(const wchar_t *path, u64 *ino_ret, u64 *dev_ret)
-{
-       HANDLE h;
-       BY_HANDLE_FILE_INFORMATION file_info;
-       int ret;
-       DWORD err;
-
-       h = win32_open_existing_file(path, FILE_READ_ATTRIBUTES);
-       if (h == INVALID_HANDLE_VALUE) {
-               ret = WIMLIB_ERR_OPEN;
-               goto out;
-       }
-
-       if (!GetFileInformationByHandle(h, &file_info)) {
-               ret = WIMLIB_ERR_STAT;
-       } else {
-               *ino_ret = ((u64)file_info.nFileIndexHigh << 32) |
-                           (u64)file_info.nFileIndexLow;
-               *dev_ret = file_info.dwVolumeSerialNumber;
-               ret = 0;
-       }
-       err = GetLastError();
-       CloseHandle(h);
-       SetLastError(err);
-out:
-       set_errno_from_GetLastError();
-       return ret;
-}
-
 /* Replacement for glob() in Windows native builds that operates on wide
  * characters.  */
 int