]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
win32_apply.c: better workaround for access denied bug when creating ADS
[wimlib] / src / win32_apply.c
index fbed6b9c685d479cca4ba69559d98bd6de8a7ee2..d9bf63c2fd305b93cf932b1bbff9d0dc996f8c83 100644 (file)
 #include "wimlib/apply.h"
 #include "wimlib/assert.h"
 #include "wimlib/blob_table.h"
-#include "wimlib/capture.h" /* for mangle_pat() and match_pattern_list()  */
 #include "wimlib/dentry.h"
 #include "wimlib/encoding.h"
 #include "wimlib/error.h"
 #include "wimlib/metadata.h"
+#include "wimlib/object_id.h"
 #include "wimlib/paths.h"
 #include "wimlib/pattern.h"
 #include "wimlib/reparse.h"
+#include "wimlib/scan.h" /* for mangle_pat() and match_pattern_list()  */
 #include "wimlib/textfile.h"
 #include "wimlib/xml.h"
 #include "wimlib/wimboot.h"
@@ -68,7 +69,7 @@ struct win32_apply_ctx {
        } wimboot;
 
        /* External backing information  */
-       struct string_set *prepopulate_pats;
+       struct string_list *prepopulate_pats;
        void *mem_prepopulate_pats;
        bool tried_to_load_prepopulate_list;
 
@@ -158,6 +159,9 @@ struct win32_apply_ctx {
         * [PrepopulateList].  */
        unsigned long num_system_compression_exclusions;
 
+       /* Number of files for which we couldn't set the object ID.  */
+       unsigned long num_object_id_failures;
+
        /* The Windows build number of the image being applied, or 0 if unknown.
         */
        u64 windows_build_number;
@@ -299,6 +303,9 @@ win32_get_supported_features(const wchar_t *target,
        if (short_names_supported)
                supported_features->short_names = 1;
 
+       if (vol_flags & FILE_SUPPORTS_OBJECT_IDS)
+               supported_features->object_ids = 1;
+
        supported_features->timestamps = 1;
 
        /* Note: Windows does not support case sensitive filenames!  At least
@@ -331,7 +338,7 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
        const struct blob_descriptor *blob;
        int ret;
        void *buf;
-       struct string_set *s;
+       struct string_list *strings;
        void *mem;
        struct text_file_section sec;
 
@@ -359,14 +366,14 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
        if (ret)
                return ret;
 
-       s = CALLOC(1, sizeof(struct string_set));
-       if (!s) {
+       strings = CALLOC(1, sizeof(struct string_list));
+       if (!strings) {
                FREE(buf);
                return WIMLIB_ERR_NOMEM;
        }
 
        sec.name = T("PrepopulateList");
-       sec.strings = s;
+       sec.strings = strings;
 
        ret = do_load_text_file(path, buf, blob->size, &mem, &sec, 1,
                                LOAD_TEXT_FILE_REMOVE_QUOTES |
@@ -375,10 +382,10 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
        STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR);
        FREE(buf);
        if (ret) {
-               FREE(s);
+               FREE(strings);
                return ret;
        }
-       ctx->prepopulate_pats = s;
+       ctx->prepopulate_pats = strings;
        ctx->mem_prepopulate_pats = mem;
        return 0;
 }
@@ -1003,8 +1010,7 @@ open_target_directory(struct win32_apply_ctx *ctx)
                              0,
                              FILE_SHARE_VALID_FLAGS,
                              FILE_OPEN_IF,
-                               FILE_DIRECTORY_FILE |
-                               FILE_OPEN_FOR_BACKUP_INTENT,
+                             FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT,
                              NULL,
                              0);
        if (!NT_SUCCESS(status)) {
@@ -1123,7 +1129,7 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
        status = winnt_fsctl(h, FSCTL_SET_COMPRESSION,
                             &compression_state, sizeof(USHORT), NULL, 0, NULL);
        if (NT_SUCCESS(status))
-               return status;
+               return 0;
 
        winnt_error(status, L"Can't %s compression attribute on \"%ls\"",
                    (compressed ? "set" : "clear"), current_path(ctx));
@@ -1465,10 +1471,11 @@ retry:
 /*
  * Create a nondirectory file or named data stream at the current path,
  * superseding any that already exists at that path.  If successful, return an
- * open handle to the file or named data stream.
+ * open handle to the file or named data stream with the requested permissions.
  */
 static int
-supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret)
+supersede_file_or_stream(struct win32_apply_ctx *ctx, DWORD perms,
+                        HANDLE *h_ret)
 {
        NTSTATUS status;
        bool retried = false;
@@ -1477,7 +1484,7 @@ supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret)
         * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be.  */
 retry:
        status = do_create_file(h_ret,
-                               GENERIC_READ | GENERIC_WRITE | DELETE,
+                               perms,
                                NULL,
                                FILE_ATTRIBUTE_SYSTEM,
                                FILE_CREATE,
@@ -1589,7 +1596,13 @@ create_empty_streams(const struct wim_dentry *dentry,
                        build_extraction_path_with_ads(dentry, ctx,
                                                       strm->stream_name,
                                                       utf16le_len_chars(strm->stream_name));
-                       ret = supersede_file_or_stream(ctx, &h);
+                       /*
+                        * Note: do not request any permissions on the handle.
+                        * Otherwise, we may encounter a Windows bug where the
+                        * parent directory DACL denies read access to the new
+                        * named data stream, even when using backup semantics!
+                        */
+                       ret = supersede_file_or_stream(ctx, 0, &h);
 
                        build_extraction_path(dentry, ctx);
 
@@ -1677,25 +1690,6 @@ retry:
                                             sizeof(basic_info),
                                             FileBasicInformation);
                }
-
-               /* Also try to remove the directory's DACL.  This isn't supposed
-                * to be necessary because we *always* use backup semantics.
-                * However, there is a case where NtCreateFile() fails with
-                * STATUS_ACCESS_DENIED when creating a named data stream that
-                * was just deleted, using a directory-relative open.  I have no
-                * idea why Windows is broken in this case.  */
-               if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) {
-                       static const SECURITY_DESCRIPTOR_RELATIVE desc = {
-                               .Revision = SECURITY_DESCRIPTOR_REVISION1,
-                               .Control = SE_SELF_RELATIVE | SE_DACL_PRESENT,
-                               .Owner = 0,
-                               .Group = 0,
-                               .Sacl = 0,
-                               .Dacl = 0,
-                       };
-                       NtSetSecurityObject(h, DACL_SECURITY_INFORMATION,
-                                           (void *)&desc);
-               }
        }
 
        if (!dentry_is_root(dentry)) {
@@ -1765,7 +1759,9 @@ create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
 
        build_extraction_path(dentry, ctx);
 
-       ret = supersede_file_or_stream(ctx, &h);
+       ret = supersede_file_or_stream(ctx,
+                                      GENERIC_READ | GENERIC_WRITE | DELETE,
+                                      &h);
        if (ret)
                goto out;
 
@@ -2430,7 +2426,7 @@ static wchar_t *bootloader_pattern_strings[] = {
        L"\\Windows\\System32\\CodeIntegrity\\driver.stl",
 };
 
-static const struct string_set bootloader_patterns = {
+static const struct string_list bootloader_patterns = {
        .strings = bootloader_pattern_strings,
        .num_strings = ARRAY_LEN(bootloader_pattern_strings),
 };
@@ -2660,6 +2656,46 @@ end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
         FILE_ATTRIBUTE_SPARSE_FILE     |       \
         FILE_ATTRIBUTE_COMPRESSED)
 
+static void
+set_object_id(HANDLE h, const struct wim_inode *inode,
+             struct win32_apply_ctx *ctx)
+{
+       const void *object_id;
+       u32 len;
+       NTSTATUS status;
+
+       if (!ctx->common.supported_features.object_ids)
+               return;
+
+       object_id = inode_get_object_id(inode, &len);
+       if (likely(object_id == NULL))  /* No object ID?  */
+               return;
+
+       status = winnt_fsctl(h, FSCTL_SET_OBJECT_ID,
+                            object_id, len, NULL, 0, NULL);
+       if (NT_SUCCESS(status))
+               return;
+
+       /* Object IDs must be unique within the filesystem.  A duplicate might
+        * occur if an image containing object IDs is applied twice to the same
+        * filesystem.  Arguably, the user should be warned in this case; but
+        * the reality seems to be that nothing important cares about object IDs
+        * except the Distributed Link Tracking Service... so for now these
+        * failures are just ignored.  */
+       if (status == STATUS_DUPLICATE_NAME ||
+           status == STATUS_OBJECT_NAME_COLLISION)
+               return;
+
+       ctx->num_object_id_failures++;
+       if (ctx->num_object_id_failures < 10) {
+               winnt_warning(status, L"Can't set object ID on \"%ls\"",
+                             current_path(ctx));
+       } else if (ctx->num_object_id_failures == 10) {
+               WARNING("Suppressing further warnings about failure to set "
+                       "object IDs.");
+       }
+}
+
 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
  * handle @h.  */
 static NTSTATUS
@@ -2804,7 +2840,12 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
        FILE_BASIC_INFORMATION info;
        NTSTATUS status;
 
-       /* Set security descriptor if present and not in NO_ACLS mode  */
+       /* Set the file's object ID if present and object IDs are supported by
+        * the filesystem.  */
+       set_object_id(h, inode, ctx);
+
+       /* Set the file's security descriptor if present and we're not in
+        * NO_ACLS mode  */
        if (inode_has_security_descriptor(inode) &&
            !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
        {