]> wimlib.net Git - wimlib/blobdiff - src/win32_capture.c
win32_capture.c: Use volume ID as device number
[wimlib] / src / win32_capture.c
index 09dc3edc251a0d5407d4856202116425685d55d1..2df80f34a0d9059e9b988544fce5df2fbd7b0aa6 100644 (file)
@@ -45,7 +45,6 @@
 struct winnt_scan_stats {
        unsigned long num_get_sd_access_denied;
        unsigned long num_get_sacl_priv_notheld;
-       unsigned long num_long_path_warnings;
 };
 
 static inline const wchar_t *
@@ -84,9 +83,7 @@ winnt_openat(HANDLE cur_dir, const wchar_t *path, size_t path_nchars,
 
 retry:
        status = (*func_NtOpenFile)(h_ret, perms, &attr, &iosb,
-                                   FILE_SHARE_READ |
-                                           FILE_SHARE_WRITE |
-                                           FILE_SHARE_DELETE,
+                                   FILE_SHARE_VALID_FLAGS,
                                    FILE_OPEN_REPARSE_POINT |
                                            FILE_OPEN_FOR_BACKUP_INTENT |
                                            FILE_SYNCHRONOUS_IO_NONALERT |
@@ -409,11 +406,18 @@ winnt_recurse_directory(HANDLE h,
                                                           info->FileName[1] == L'.'))
                        {
                                wchar_t *p;
+                               wchar_t *filename;
                                struct wim_dentry *child;
 
                                p = full_path + full_path_nchars;
-                               *p++ = L'\\';
-                               p = wmempcpy(p, info->FileName,
+                               /* Only add a backslash if we don't already have
+                                * one.  This prevents a duplicate backslash
+                                * from being added when the path to the capture
+                                * dir had a trailing backslash.  */
+                               if (*(p - 1) != L'\\')
+                                       *p++ = L'\\';
+                               filename = p;
+                               p = wmempcpy(filename, info->FileName,
                                             info->FileNameLength / 2);
                                *p = '\0';
 
@@ -422,7 +426,7 @@ winnt_recurse_directory(HANDLE h,
                                                        h,
                                                        full_path,
                                                        p - full_path,
-                                                       full_path + full_path_nchars + 1,
+                                                       filename,
                                                        info->FileNameLength / 2,
                                                        params,
                                                        stats,
@@ -456,9 +460,6 @@ out_free_buf:
 
 /* Reparse point fixup status code  */
 enum rp_status {
-       /* Reparse point should be excluded from capture  */
-       RP_EXCLUDED     = -0,
-
        /* Reparse point will be captured literally (no fixup)  */
        RP_NOT_FIXED    = -1,
 
@@ -591,13 +592,30 @@ out_close_root_dir:
        return p;
 }
 
-static enum rp_status
+static int
+winnt_rpfix_progress(struct add_image_params *params, const wchar_t *path,
+                    const struct reparse_data *rpdata,
+                    enum wimlib_progress_msg msg)
+{
+       size_t print_name_nchars = rpdata->print_name_nbytes / sizeof(wchar_t);
+       wchar_t print_name0[print_name_nchars + 1];
+
+       wmemcpy(print_name0, rpdata->print_name, print_name_nchars);
+       print_name0[print_name_nchars] = L'\0';
+
+       params->progress.scan.cur_path = printable_path(path);
+       params->progress.scan.symlink_target = print_name0;
+       return do_capture_progress(params, msg, NULL);
+}
+
+static int
 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;
        const wchar_t *rel_target;
+       int ret;
 
        if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
                /* Couldn't even understand the reparse data.  Don't try the
@@ -636,18 +654,13 @@ winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
                                                    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;
+               /* Target points outside of the tree being captured.  Don't
+                * adjust it.  */
+               ret = winnt_rpfix_progress(params, path, &rpdata,
+                                          WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK);
+               if (ret)
+                       return ret;
+               return RP_NOT_FIXED;
        }
 
        if (rel_target == rpdata.substitute_name) {
@@ -686,6 +699,10 @@ winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
                if (make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p))
                        return RP_NOT_FIXED;
        }
+       ret = winnt_rpfix_progress(params, path, &rpdata,
+                                  WIMLIB_SCAN_DENTRY_FIXED_SYMLINK);
+       if (ret)
+               return ret;
        return RP_FIXED;
 }
 
@@ -700,7 +717,7 @@ winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
  *     Path to the reparse point file.
  * @params:
  *     Capture parameters.  add_flags, capture_root_ino, capture_root_dev,
- *     progress_func, and progress are used.
+ *     progfunc, progctx, and progress are used.
  * @rpbuf:
  *     Buffer of length at least REPARSE_POINT_MAX_SIZE bytes into which the
  *     reparse point buffer will be loaded.
@@ -708,7 +725,7 @@ winnt_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
  *     On success, the length of the reparse point buffer in bytes is written
  *     to this location.
  *
- * On success, returns a nonpositive `enum rp_status' value.
+ * On success, returns a negative `enum rp_status' value.
  * On failure, returns a positive error code.
  */
 static int
@@ -871,6 +888,9 @@ winnt_scan_stream(const wchar_t *path, size_t path_nchars,
                                                        sizeof(wchar_t));
                if (!ads_entry)
                        return WIMLIB_ERR_NOMEM;
+       } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+               /* Ignore unnamed data stream of reparse point  */
+               return 0;
        } else {
                ads_entry = NULL;
        }
@@ -1077,13 +1097,11 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        u16 rpbuflen;
        u16 not_rpfixed;
 
-       if (should_exclude_path(full_path + params->capture_root_nchars,
-                               full_path_nchars - params->capture_root_nchars,
-                               params->config))
-       {
-               ret = 0;
+       ret = try_exclude(full_path, full_path_nchars, params);
+       if (ret < 0) /* Excluded? */
                goto out_progress;
-       }
+       if (ret > 0) /* Error? */
+               goto out;
 
        /* Open the file.  */
        status = winnt_openat(cur_dir,
@@ -1096,11 +1114,22 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                        SYNCHRONIZE,
                              &h);
        if (unlikely(!NT_SUCCESS(status))) {
-               set_errno_from_nt_status(status);
-               ERROR_WITH_ERRNO("\"%ls\": Can't open file "
-                                "(status=0x%08"PRIx32")",
-                                printable_path(full_path), (u32)status);
-               ret = WIMLIB_ERR_OPEN;
+               if (status == STATUS_DELETE_PENDING) {
+                       WARNING("\"%ls\": Deletion pending; skipping file",
+                               printable_path(full_path));
+                       ret = 0;
+               } else {
+                       set_errno_from_nt_status(status);
+                       ERROR_WITH_ERRNO("\"%ls\": Can't open file "
+                                        "(status=0x%08"PRIx32")",
+                                        printable_path(full_path), (u32)status);
+                       if (status == STATUS_FVE_LOCKED_VOLUME)
+                               ret = WIMLIB_ERR_FVE_LOCKED_VOLUME;
+                       else
+                               ret = WIMLIB_ERR_OPEN;
+               }
+               /* XXX: Provide option to exclude files that fail with
+                * STATUS_SHARING_VIOLATION?  */
                goto out;
        }
 
@@ -1190,9 +1219,6 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                ret = winnt_get_reparse_data(h, full_path, params,
                                             rpbuf, &rpbuflen);
                switch (ret) {
-               case RP_EXCLUDED:
-                       ret = 0;
-                       goto out;
                case RP_FIXED:
                        not_rpfixed = 0;
                        break;
@@ -1211,13 +1237,17 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
         * However, we need to explicitly check for directories and files with
         * only 1 link and refuse to hard link them.  This is because Windows
         * has a bug where it can return duplicate File IDs for files and
-        * directories on the FAT filesystem. */
+        * directories on the FAT filesystem.
+        *
+        * Since we don't follow mount points on Windows, we don't need to query
+        * the volume ID per-file.  Just once, for the root, is enough.  But we
+        * can't simply pass 0, because then there could be inode collisions
+        * among multiple calls to win32_build_dentry_tree() that are scanning
+        * files on different volumes.  */
        ret = inode_table_new_dentry(params->inode_table,
                                     filename,
                                     file_info.InternalInformation.IndexNumber.QuadPart,
-                                    0, /* We don't follow mount points, so we
-                                          currently don't need to get the
-                                          volume ID / device number.  */
+                                    params->capture_root_dev,
                                     (file_info.StandardInformation.NumberOfLinks <= 1 ||
                                        (file_info.BasicInformation.FileAttributes &
                                         FILE_ATTRIBUTE_DIRECTORY)),
@@ -1242,7 +1272,6 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        if (inode->i_nlink > 1) {
                /* Shared inode (hard link); skip reading per-inode information.
                 */
-               ret = 0;
                goto out_progress;
        }
 
@@ -1331,9 +1360,9 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
 out_progress:
        params->progress.scan.cur_path = printable_path(full_path);
        if (likely(root))
-               do_capture_progress(params, WIMLIB_SCAN_DENTRY_OK, inode);
+               ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_OK, inode);
        else
-               do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
+               ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
 out:
        if (likely(h != INVALID_HANDLE_VALUE))
                (*func_NtClose)(h);
@@ -1375,10 +1404,10 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
                        struct add_image_params *params)
 {
        wchar_t *path;
-       DWORD dret;
-       size_t path_nchars;
        int ret;
+       UNICODE_STRING ntpath;
        struct winnt_scan_stats stats;
+       size_t ntpath_nchars;
 
        /* WARNING: There is no check for overflow later when this buffer is
         * being used!  But it's as long as the maximum path length understood
@@ -1387,33 +1416,36 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        if (!path)
                return WIMLIB_ERR_NOMEM;
 
-       /* Translate into full path.  */
-       dret = GetFullPathName(root_disk_path, WINDOWS_NT_MAX_PATH - 3,
-                              &path[4], NULL);
-
-       if (unlikely(dret == 0 || dret >= WINDOWS_NT_MAX_PATH - 3)) {
-               ERROR("Can't get full path name for \"%ls\"", root_disk_path);
-               return WIMLIB_ERR_UNSUPPORTED;
-       }
-
-       /* Add \??\ prefix to form the NT namespace path.  */
-       wmemcpy(path, L"\\??\\", 4);
-       path_nchars = dret + 4;
+       ret = win32_path_to_nt_path(root_disk_path, &ntpath);
+       if (ret)
+               goto out_free_path;
 
-       /* Strip trailing slashes.  If we don't do this, we may create a path
-       * with multiple consecutive backslashes, which for some reason causes
-       * Windows to report that the file cannot be found.  */
-       while (unlikely(path[path_nchars - 1] == L'\\' &&
-                       path[path_nchars - 2] != L':'))
-               path[--path_nchars] = L'\0';
+       if (ntpath.Length < 4 * sizeof(wchar_t) ||
+           ntpath.Length > WINDOWS_NT_MAX_PATH * sizeof(wchar_t) ||
+           wmemcmp(ntpath.Buffer, L"\\??\\", 4))
+       {
+               ERROR("\"%ls\": unrecognized path format", root_disk_path);
+               ret = WIMLIB_ERR_INVALID_PARAM;
+       } else {
+               ntpath_nchars = ntpath.Length / sizeof(wchar_t);
+               wmemcpy(path, ntpath.Buffer, ntpath_nchars);
+               path[ntpath_nchars] = L'\0';
 
-       params->capture_root_nchars = path_nchars;
+               params->capture_root_nchars = ntpath_nchars;
+               if (path[ntpath_nchars - 1] == L'\\')
+                       params->capture_root_nchars--;
+               ret = 0;
+       }
+       HeapFree(GetProcessHeap(), 0, ntpath.Buffer);
+       if (ret)
+               goto out_free_path;
 
        memset(&stats, 0, sizeof(stats));
 
        ret = winnt_build_dentry_tree_recursive(root_ret, NULL,
-                                               path, path_nchars, L"", 0,
-                                               params, &stats, 0);
+                                               path, ntpath_nchars,
+                                               L"", 0, params, &stats, 0);
+out_free_path:
        FREE(path);
        if (ret == 0)
                winnt_do_scan_warnings(root_disk_path, &stats);