]> wimlib.net Git - wimlib/blobdiff - src/win32_capture.c
win32_capture: don't ask for permission to read data when not needed
[wimlib] / src / win32_capture.c
index d6be1124204312a73152fa87e376cf2263631646..0d4b783e4086b395d779fa8df64dde45acc9cb94 100644 (file)
@@ -240,39 +240,38 @@ get_windows_file_path(const struct windows_file *file)
 }
 
 /*
- * If cur_dir is not NULL, open an existing file relative to the already-open
- * directory cur_dir.
- *
- * Otherwise, open the file specified by @path, which must be a Windows NT
- * namespace path.
+ * Open the file named by the NT namespace path @path of length @path_nchars
+ * characters.  If @cur_dir is not NULL then the path is given relative to
+ * @cur_dir; otherwise the path is absolute.  @perms is the access mask of
+ * permissions to request on the handle.  If permission to read the data is
+ * requested, then SYNCHRONIZE is automatically added.
  */
 static NTSTATUS
 winnt_openat(HANDLE cur_dir, const wchar_t *path, size_t path_nchars,
             ACCESS_MASK perms, HANDLE *h_ret)
 {
-       UNICODE_STRING name;
-       OBJECT_ATTRIBUTES attr;
+       UNICODE_STRING name = {
+               .Length = path_nchars * sizeof(wchar_t),
+               .MaximumLength = path_nchars * sizeof(wchar_t),
+               .Buffer = (wchar_t *)path,
+       };
+       OBJECT_ATTRIBUTES attr = {
+               .Length = sizeof(attr),
+               .RootDirectory = cur_dir,
+               .ObjectName = &name,
+       };
        IO_STATUS_BLOCK iosb;
        NTSTATUS status;
+       ULONG options = FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT;
 
-       name.Length = path_nchars * sizeof(wchar_t);
-       name.MaximumLength = name.Length;
-       name.Buffer = (wchar_t *)path;
-
-       attr.Length = sizeof(attr);
-       attr.RootDirectory = cur_dir;
-       attr.ObjectName = &name;
-       attr.Attributes = 0;
-       attr.SecurityDescriptor = NULL;
-       attr.SecurityQualityOfService = NULL;
-
+       if (perms & (FILE_READ_DATA | FILE_LIST_DIRECTORY)) {
+               perms |= SYNCHRONIZE;
+               options |= FILE_SYNCHRONOUS_IO_NONALERT;
+               options |= FILE_SEQUENTIAL_ONLY;
+       }
 retry:
        status = (*func_NtOpenFile)(h_ret, perms, &attr, &iosb,
-                                   FILE_SHARE_VALID_FLAGS,
-                                   FILE_OPEN_REPARSE_POINT |
-                                           FILE_OPEN_FOR_BACKUP_INTENT |
-                                           FILE_SYNCHRONOUS_IO_NONALERT |
-                                           FILE_SEQUENTIAL_ONLY);
+                                   FILE_SHARE_VALID_FLAGS, options);
        if (!NT_SUCCESS(status)) {
                /* Try requesting fewer permissions  */
                if (status == STATUS_ACCESS_DENIED ||
@@ -856,7 +855,7 @@ winnt_rpfix_progress(struct capture_params *params, const wchar_t *path,
        wmemcpy(print_name0, link->print_name, print_name_nchars);
        print_name0[print_name_nchars] = L'\0';
 
-       params->progress.scan.cur_path = printable_path(path);
+       params->progress.scan.cur_path = path;
        params->progress.scan.symlink_target = print_name0;
        return do_capture_progress(params, scan_status, NULL);
 }
@@ -1550,7 +1549,6 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        int ret;
        NTSTATUS status;
        struct file_info file_info;
-       ACCESS_MASK requestedPerms;
        u64 sort_key;
 
        ret = try_exclude(full_path, ctx->params);
@@ -1559,15 +1557,16 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        if (unlikely(ret > 0)) /* Error? */
                goto out;
 
-       /* Open the file.  */
-       requestedPerms = FILE_READ_DATA |
-                        FILE_READ_ATTRIBUTES |
-                        READ_CONTROL |
-                        ACCESS_SYSTEM_SECURITY |
-                        SYNCHRONIZE;
-retry_open:
+       /* Open the file with permission to read metadata.  Although we will
+        * later need a handle with FILE_LIST_DIRECTORY permission (or,
+        * equivalently, FILE_READ_DATA; they're the same numeric value) if the
+        * file is a directory, it can significantly slow things down to request
+        * this permission on all nondirectories.  Perhaps it causes Windows to
+        * start prefetching the file contents...  */
        status = winnt_openat(cur_dir, relative_path, relative_path_nchars,
-                             requestedPerms, &h);
+                             FILE_READ_ATTRIBUTES | READ_CONTROL |
+                                       ACCESS_SYSTEM_SECURITY,
+                             &h);
        if (unlikely(!NT_SUCCESS(status))) {
                if (status == STATUS_DELETE_PENDING) {
                        WARNING("\"%ls\": Deletion pending; skipping file",
@@ -1575,12 +1574,6 @@ retry_open:
                        ret = 0;
                        goto out;
                }
-               if (status == STATUS_ACCESS_DENIED &&
-                   (requestedPerms & FILE_READ_DATA)) {
-                       /* This happens on encrypted files.  */
-                       requestedPerms &= ~FILE_READ_DATA;
-                       goto retry_open;
-               }
                if (status == STATUS_SHARING_VIOLATION) {
                        ERROR("Can't open \"%ls\":\n"
                              "        File is in use by another process! "
@@ -1607,15 +1600,6 @@ retry_open:
                goto out;
        }
 
-       if (unlikely(!(requestedPerms & FILE_READ_DATA)) &&
-           !(file_info.attributes & FILE_ATTRIBUTE_ENCRYPTED))
-       {
-               ERROR("\"%ls\": Permission to read data was denied",
-                     printable_path(full_path));
-               ret = WIMLIB_ERR_OPEN;
-               goto out;
-       }
-
        /* Create a WIM dentry with an associated inode, which may be shared.
         *
         * However, we need to explicitly check for directories and files with
@@ -1729,21 +1713,19 @@ retry_open:
 
                /* Directory: recurse to children.  */
 
-               if (unlikely(!h)) {
-                       /* Re-open handle that was closed to read raw encrypted
-                        * data.  */
-                       status = winnt_openat(cur_dir,
-                                             relative_path,
-                                             relative_path_nchars,
-                                             FILE_LIST_DIRECTORY | SYNCHRONIZE,
-                                             &h);
-                       if (!NT_SUCCESS(status)) {
-                               winnt_error(status,
-                                           L"\"%ls\": Can't re-open file",
-                                           printable_path(full_path));
-                               ret = WIMLIB_ERR_OPEN;
-                               goto out;
-                       }
+               /* Re-open the directory with FILE_LIST_DIRECTORY access.  */
+               if (h) {
+                       (*func_NtClose)(h);
+                       h = NULL;
+               }
+               status = winnt_openat(cur_dir, relative_path,
+                                     relative_path_nchars, FILE_LIST_DIRECTORY,
+                                     &h);
+               if (!NT_SUCCESS(status)) {
+                       winnt_error(status, L"\"%ls\": Can't open directory",
+                                   printable_path(full_path));
+                       ret = WIMLIB_ERR_OPEN;
+                       goto out;
                }
                ret = winnt_recurse_directory(h,
                                              full_path,
@@ -1755,7 +1737,7 @@ retry_open:
        }
 
 out_progress:
-       ctx->params->progress.scan.cur_path = printable_path(full_path);
+       ctx->params->progress.scan.cur_path = full_path;
        if (likely(root))
                ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
        else
@@ -2353,8 +2335,7 @@ load_files_from_mft(const wchar_t *path, struct ntfs_inode_map *inode_map)
        NTSTATUS status;
 
        status = winnt_open(path, wcslen(path),
-                           FILE_READ_DATA | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
-                           &h);
+                           FILE_READ_DATA | FILE_READ_ATTRIBUTES, &h);
        if (!NT_SUCCESS(status)) {
                ret = -1; /* Silently try standard recursive scan instead  */
                goto out;
@@ -2477,6 +2458,9 @@ security_map_lookup(struct security_map *map, u32 disk_security_id)
        struct security_map_node tmp;
        const struct avl_tree_node *res;
 
+       if (disk_security_id == 0)  /* No on-disk security ID; uncacheable  */
+               return -1;
+
        tmp.disk_security_id = disk_security_id;
        res = avl_tree_lookup_node(map->root, &tmp.index_node,
                                   _avl_cmp_security_map_nodes);
@@ -2491,6 +2475,9 @@ security_map_insert(struct security_map *map, u32 disk_security_id,
 {
        struct security_map_node *node;
 
+       if (disk_security_id == 0)  /* No on-disk security ID; uncacheable  */
+               return 0;
+
        node = MALLOC(sizeof(*node));
        if (!node)
                return WIMLIB_ERR_NOMEM;
@@ -2618,8 +2605,7 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret,
 
                        status = winnt_open(path, path_nchars,
                                            READ_CONTROL |
-                                               ACCESS_SYSTEM_SECURITY |
-                                               SYNCHRONIZE, &h);
+                                               ACCESS_SYSTEM_SECURITY, &h);
                        if (!NT_SUCCESS(status)) {
                                winnt_error(status, L"Can't open \"%ls\" to "
                                            "read security descriptor",
@@ -2643,22 +2629,22 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret,
        ns = FIRST_STREAM(ni);
        for (u32 i = 0; i < ni->num_streams; i++) {
                struct windows_file *windows_file;
-               size_t stream_name_nchars;
 
+               /* Reference the stream by path if it's a named data stream, or
+                * if the volume doesn't support "open by file ID", or if the
+                * application hasn't explicitly opted in to "open by file ID".
+                * Otherwise, only save the inode number (file ID).  */
                if (*ns->name ||
-                   !(ctx->vol_flags & FILE_SUPPORTS_OPEN_BY_FILE_ID))
+                   !(ctx->vol_flags & FILE_SUPPORTS_OPEN_BY_FILE_ID) ||
+                   !(ctx->params->add_flags & WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED))
                {
-                       /* Named data stream: reference by path  */
-                       stream_name_nchars = wcslen(ns->name);
                        windows_file = alloc_windows_file(path,
                                                          path_nchars,
                                                          ns->name,
-                                                         stream_name_nchars,
+                                                         wcslen(ns->name),
                                                          ctx->snapshot,
                                                          false);
                } else {
-                       /* Unamed data stream: reference by file ID (inode number)  */
-                       stream_name_nchars = 0;
                        windows_file = alloc_windows_file_for_file_id(ni->ino,
                                                                      path,
                                                                      ctx->params->capture_root_nchars + 1,
@@ -2714,7 +2700,7 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret,
        }
 
 out_progress:
-       ctx->params->progress.scan.cur_path = printable_path(path);
+       ctx->params->progress.scan.cur_path = path;
        if (likely(root))
                ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
        else
@@ -2833,9 +2819,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        if (ret)
                goto out;
 
-       status = winnt_open(path, ntpath_nchars,
-                           FILE_TRAVERSE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
-                           &h);
+       status = winnt_open(path, ntpath_nchars, FILE_READ_ATTRIBUTES, &h);
        if (!NT_SUCCESS(status)) {
                winnt_error(status, L"Can't open \"%ls\"", printable_path(path));
                if (status == STATUS_FVE_LOCKED_VOLUME)