]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
system compression: try to attach WOF if compression fails
[wimlib] / src / win32_apply.c
index 0367719558a6d9a747b1b284f71672b5d5f41d77..ce62d6af98b651b6e6b961b897799bff60a75fde 100644 (file)
@@ -352,7 +352,7 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
                                LOAD_TEXT_FILE_REMOVE_QUOTES |
                                        LOAD_TEXT_FILE_NO_WARNINGS,
                                mangle_pat);
-       BUILD_BUG_ON(OS_PREFERRED_PATH_SEPARATOR != WIM_PATH_SEPARATOR);
+       STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR);
        FREE(buf);
        if (ret) {
                FREE(s);
@@ -2264,11 +2264,11 @@ get_system_compression_format(int extract_flags)
        return FILE_PROVIDER_COMPRESSION_FORMAT_LZX;
 }
 
-static DWORD
+static NTSTATUS
 set_system_compression(HANDLE h, int format)
 {
-       DWORD bytes_returned;
-       DWORD err;
+       NTSTATUS status;
+       IO_STATUS_BLOCK iosb;
        struct {
                struct wof_external_info wof_info;
                struct file_provider_external_info file_info;
@@ -2283,16 +2283,52 @@ set_system_compression(HANDLE h, int format)
                },
        };
 
-       if (DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING, &in, sizeof(in),
-                           NULL, 0, &bytes_returned, NULL))
-               return 0;
+       /* We intentionally use NtFsControlFile() rather than DeviceIoControl()
+        * here because the "compressing this object would not save space"
+        * status code does not map to a valid Win32 error code on older
+        * versions of Windows (before Windows 10?).  This can be a problem if
+        * the WOFADK driver is being used rather than the regular WOF, since
+        * WOFADK can be used on older versions of Windows.  */
+       status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, &iosb,
+                                        FSCTL_SET_EXTERNAL_BACKING,
+                                        &in, sizeof(in), NULL, 0);
 
-       err = GetLastError();
+       if (status == 0xC000046F) /* "Compressing this object would not save space."  */
+               return STATUS_SUCCESS;
 
-       if (err == 344) /* "Compressing this object would not save space."  */
-               return 0;
+       return status;
+}
+
+static NTSTATUS
+set_system_compression_on_inode(struct wim_inode *inode, int format,
+                               struct win32_apply_ctx *ctx)
+{
+       bool retried = false;
+       NTSTATUS status;
+       HANDLE h;
+
+       /* Open the extracted file.  */
+       status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL,
+                            0, FILE_OPEN, 0,
+                            inode_first_extraction_dentry(inode), ctx);
 
-       return err;
+       if (!NT_SUCCESS(status))
+               return status;
+retry:
+       /* Compress the file.  If the attempt fails with "invalid device
+        * request", then attach wof.sys (or wofadk.sys) and retry.  */
+       status = set_system_compression(h, format);
+       if (unlikely(status == STATUS_INVALID_DEVICE_REQUEST && !retried)) {
+               wchar_t drive_path[7];
+               if (!win32_get_drive_path(ctx->common.target, drive_path) &&
+                   win32_try_to_attach_wof(drive_path + 4)) {
+                       retried = true;
+                       goto retry;
+               }
+       }
+
+       (*func_NtClose)(h);
+       return status;
 }
 
 /*
@@ -2321,9 +2357,7 @@ handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *
        for (u32 i = 0; i < blob->out_refcnt; i++) {
                struct wim_inode *inode = targets[i].inode;
                struct wim_inode_stream *strm = targets[i].stream;
-               HANDLE h;
                NTSTATUS status;
-               DWORD err;
 
                if (!stream_is_unnamed_data_stream(strm))
                        continue;
@@ -2331,18 +2365,11 @@ handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *
                if (will_externally_back_inode(inode, ctx, NULL, false) != 0)
                        continue;
 
-               status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL,
-                                    0, FILE_OPEN, 0,
-                                    inode_first_extraction_dentry(inode), ctx);
-
-               if (NT_SUCCESS(status)) {
-                       err = set_system_compression(h, format);
-                       (*func_NtClose)(h);
-               } else {
-                       err = (*func_RtlNtStatusToDosError)(status);
-               }
+               status = set_system_compression_on_inode(inode, format, ctx);
+               if (likely(NT_SUCCESS(status)))
+                       continue;
 
-               if (err == ERROR_INVALID_FUNCTION) {
+               if (status == STATUS_INVALID_DEVICE_REQUEST) {
                        WARNING(
          "The request to compress the extracted files using System Compression\n"
 "          will not be honored because the operating system or target volume\n"
@@ -2352,16 +2379,14 @@ handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *
                        return;
                }
 
-               if (err) {
-                       ctx->num_system_compression_failures++;
-                       if (ctx->num_system_compression_failures < 10) {
-                               win32_warning(err, L"\"%ls\": Failed to compress "
-                                             "extracted file using System Compression",
-                                             current_path(ctx));
-                       } else if (ctx->num_system_compression_failures == 10) {
-                               WARNING("Suppressing further warnings about "
-                                       "System Compression failures.");
-                       }
+               ctx->num_system_compression_failures++;
+               if (ctx->num_system_compression_failures < 10) {
+                       winnt_warning(status, L"\"%ls\": Failed to compress "
+                                     "extracted file using System Compression",
+                                     current_path(ctx));
+               } else if (ctx->num_system_compression_failures == 10) {
+                       WARNING("Suppressing further warnings about "
+                               "System Compression failures.");
                }
        }
 }