]> wimlib.net Git - wimlib/commitdiff
WIMBoot: Update WimOverlay.dat directly when WOF not running
authorEric Biggers <ebiggers3@gmail.com>
Sat, 26 Apr 2014 21:11:26 +0000 (16:11 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 26 Apr 2014 21:11:26 +0000 (16:11 -0500)
include/wimlib/apply.h
include/wimlib/resource.h
include/wimlib/wimboot.h
include/wimlib/win32_common.h
include/wimlib/wof.h
src/resource.c
src/wimboot.c
src/win32_apply.c
src/win32_common.c

index 7b4ffe3ea7defe663f6ddd7a65c0b3598c42d7cc..24648c9b3a378c19581881be787ffeb5f0d45574 100644 (file)
@@ -227,7 +227,7 @@ struct apply_ctx {
        tchar *tmpfile_name;
        u64 num_streams_remaining;
        uint64_t next_progress;
-       intptr_t private[8];
+       intptr_t private[10];
 };
 
 #ifdef __WIN32__
index 38ce568c6a210b6c1bd0866c14f90987ea5ddb63..2eab7bee0cd0a04c7f3c03834cbfa1f058dff628 100644 (file)
@@ -203,6 +203,10 @@ extern int
 wim_reshdr_to_data(const struct wim_reshdr *reshdr,
                   WIMStruct *wim, void **buf_ret);
 
+extern int
+wim_reshdr_to_hash(const struct wim_reshdr *reshdr, WIMStruct *wim,
+                  u8 hash[SHA1_HASH_SIZE]);
+
 extern int
 skip_wim_stream(struct wim_lookup_table_entry *lte);
 
index 35d96263a976d76dc973f3d38282434b0294da16..fd178c61707c379eb1aaac06fba2324fa8df837c 100644 (file)
@@ -1,14 +1,24 @@
 #ifndef _WIMBOOT_H_
 #define _WIMBOOT_H_
 
+#include "wimlib/sha1.h"
 #include "wimlib/types.h"
+#include "wimlib/header.h"
+
+struct wim_lookup_table_entry;
 
 extern int
-wimboot_set_pointer(const wchar_t *path, u64 data_source_id,
-                   const u8 hash[20]);
+wimboot_alloc_data_source_id(const wchar_t *wim_path,
+                            const u8 guid[WIM_GID_LEN], int image,
+                            const wchar_t *target, u64 *data_source_id_ret,
+                            bool *wof_running_ret);
 
 extern int
-wimboot_alloc_data_source_id(const wchar_t *wim_path, int image,
-                            const wchar_t *target, u64 *data_source_id_ret);
+wimboot_set_pointer(const wchar_t *path,
+                   const struct wim_lookup_table_entry *lte,
+                   u64 data_source_id,
+                   const u8 lookup_table_hash[SHA1_HASH_SIZE],
+                   bool wof_running);
+
 
 #endif /* _WIMBOOT_H_ */
index 019dd7df742b7d95bbb20c2794094f257c1f7050..fa10469bc777c0cd0cef4aedf49c3204878b932f 100644 (file)
@@ -84,6 +84,9 @@ extern NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle,
                                                   PSECURITY_DESCRIPTOR SecurityDescriptor);
 
 extern DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status);
+
+extern NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
+                       (PCUNICODE_STRING VolumeRootPath);
 #endif
 
 
index 5158985061b8697a2399c96416e121799150cc8a..b2cdd2722d3be535b5e5b619c77f0edb46f29038 100644 (file)
@@ -10,6 +10,7 @@
 #define _WOF_H_
 
 #include "wimlib/types.h"
+#include "wimlib/compiler.h"
 
 #define WOF_CURRENT_VERSION    1
 #define WOF_PROVIDER_WIM       1
@@ -27,56 +28,234 @@ struct wof_external_info {
        u32 provider;
 };
 
-/* WOF reparse points can't be directly manipulated on Windows; setting the
- * reparse data doesn't seem to work, and the WOF driver hides the reparse
- * points so their data can't be read from Windows 8.1 and later.  Use the
- * ioctls (FSCTL_SET_EXTERNAL_BACKING, FSCTL_GET_EXTERNAL_BACKING,
- * FSCTL_DELETE_EXTERNAL_BACKING) instead.  */
-#if 0
+/* On Windows, WOF reparse points can only be directly created when the WOF
+ * driver is NOT running on the volume.  Otherwise, the WOF ioctl
+ * (FSCTL_SET_EXTERNAL_BACKING, FSCTL_GET_EXTERNAL_BACKING,
+ * FSCTL_DELETE_EXTERNAL_BACKING) must be used instead.  */
+
 /*
  * Format of the reparse data of WoF (Windows Overlay File System Filter)
  * reparse points.  These include WIMBoot "pointer files".
  *
+ * This is not documented by Microsoft!!!
+ *
  * Notes:
+ *     - 'struct wim_provider_rpdata' must be preceded by
+ *       'struct wof_external_info'.
  *     - Reparse tag is 0x80000017
  *     - Don't make these if the file has no unnamed data stream, has an empty
  *       unnamed data stream, or already is a reparse point.
  *     - There is nowhere to put named data streams.  They have to copied
  *       literally to the reparse point file.
  */
-struct wof_rpdata_disk {
-       struct wof_external_info info;
-       union {
-               struct {
-                       /* (Guess) Version of this structure --- set to 2.  */
-                       u64 version;
-
-                       /* Integer ID that identifies the WIM.  */
-                       u64 data_source_id;
-
-                       /* SHA1 message digest of the file's unnamed data
-                        * stream.  */
-                       u8 stream_sha1[20];
-
-                       /* SHA1 message digest of the WIM's lookup table.  */
-                       u8 wim_lookup_table_sha1[20];
-
-                       /* Uncompressed size of the file's unnamed data stream,
-                        * in bytes.  */
-                       u64 stream_uncompressed_size;
-
-                       /* Compressed size of the file's unnamed data stream, in
-                        * bytes.  If stream is stored uncompressed, set this
-                        * the same as the uncompressed size.  */
-                       u64 stream_compressed_size;
-
-                       /* Byte offset of the file's unnamed data stream in the
-                        * WIM.  */
-                       u64 stream_offset_in_wim;
-               } wim;
-       } provider_data;
-};
-#endif
+struct wim_provider_rpdata {
+       /* Set to 2.  Uncertain meaning.  */
+       le32 version;
+
+       /* 0 when WIM provider active, otherwise
+        * WIM_PROVIDER_EXTERNAL_FLAG_NOT_ACTIVE or
+        * WIM_PROVIDER_EXTERNAL_FLAG_SUSPENDED.  */
+       le32 flags;
+
+       /* Integer ID that identifies the WIM.  */
+       le64 data_source_id;
+
+       /* SHA1 message digest of the file's unnamed data stream.  */
+       u8 resource_hash[20];
+
+       /* SHA1 message digest of the WIM's lookup table.  */
+       u8 wim_lookup_table_hash[20];
+
+       /* Uncompressed size of the file's unnamed data stream, in bytes.  */
+       le64 stream_uncompressed_size;
+
+       /* Compressed size of the file's unnamed data stream, in bytes.  If
+        * stream is stored uncompressed, set this the same as the uncompressed
+        * size.  */
+       le64 stream_compressed_size;
+
+       /* Byte offset of the file's unnamed data stream in the WIM.  */
+       le64 stream_offset_in_wim;
+} _packed_attribute;
+
+/* WIM-specific information about a WIM data source  */
+struct WimOverlay_dat_entry_1 {
+
+       /* Identifier for the WIM data source, (normally allocated by
+        * FSCTL_ADD_OVERLAY).  Every 'WimOverlay_dat_entry_1' should have a
+        * different value for this.  */
+       le64 data_source_id;
+
+       /* Byte offset, from the beginning of the file, of the corresponding
+        * 'struct WimOverlay_dat_entry_2' for this WIM data source.  */
+       le32 entry_2_offset;
+
+       /* Size, in bytes, of the corresponding 'struct WimOverlay_dat_entry_2
+        * for this WIM data source, including wim_file_name and its null
+        * terminator.  */
+       le32 entry_2_length;
+
+       /* Type of the WIM file: WIM_BOOT_OS_WIM or WIM_BOOT_NOT_OS_WIM.  */
+       le32 wim_type;
+
+       /* Index of the image in the WIM to use??? (This doesn't really make
+        * sense, since WIM files combine streams for all images into a single
+        * table.  Set to 1 if unsure...)  */
+       le32 wim_index;
+
+       /* GUID of the WIM file (copied from the WIM header, offset +0x18).  */
+       u8 guid[16];
+} _packed_attribute;
+
+/*
+ * Format of file: "\System Volume Information\WimOverlay.dat"
+ *
+ * Not documented by Microsoft.
+ *
+ * The file consists of a 'struct WimOverlay_dat_header' followed by one or more
+ * 'struct WimOverlay_dat_entry_1', followed by the same number of 'struct
+ * WimOverlay_dat_entry_2'.  Note that 'struct WimOverlay_dat_entry_1' is of
+ * fixed length, whereas 'struct WimOverlay_dat_entry_2' is of variable length.
+ */
+struct WimOverlay_dat_header {
+       /* Set to WIMOVERLAY_DAT_MAGIC  */
+       le32 magic;
+#define WIMOVERLAY_DAT_MAGIC 0x66436F57
+
+       /* Set to 1 (WIM_PROVIDER_CURRENT_VERSION)  */
+       le32 wim_provider_version;
+
+       /* Set to 0x00000028  */
+       le32 unknown_0x08;
+
+       /* Set to number of WIMs registered;
+        * also the number of 'struct WimOverlay_dat_entry_1' that follow.  */
+       le32 num_entries_1;
+
+       /* Set to number of WIMs registered;
+        * also the number of 'struct WimOverlay_dat_entry_2' that follow.  */
+       le32 num_entries_2;
+
+       /* Set to 0  */
+       le32 unknown_0x14;
+
+       struct WimOverlay_dat_entry_1 entry_1s[];
+} _packed_attribute;
+
+/* Location information about a WIM data source  */
+struct WimOverlay_dat_entry_2 {
+       /* Set to 0  */
+       le32 unknown_0x00;
+
+       /* Set to 0  */
+       le32 unknown_0x04;
+
+       /* Size, in bytes, of this 'struct WimOverlay_dat_entry_2', including
+        * wim_file_name and its null terminator.  */
+       le32 entry_2_length;
+
+       /* Set to 0  */
+       le32 unknown_0x0C;
+
+       /* Set to 5  */
+       le32 unknown_0x10;
+
+       struct {
+               /* Set to 1  */
+               le32 unknown_0x14;
+
+               /* Size of this inner structure, in bytes.  */
+               le32 inner_struct_size;
+
+               /* Set to 5  */
+               le32 unknown_0x1C;
+
+               /* Set to 6  */
+               le32 unknown_0x20;
+
+               /* Set to 0  */
+               le32 unknown_0x24;
+
+               /* Set to 0x48  */
+               le32 unknown_0x28;
+
+               /* Set to 0  */
+               le32 unknown_0x2C;
+
+               /*************************
+                * Partition information
+                ************************/
+
+               /* Partition identifier  */
+               union {
+                       /* (For MBR-formatted disks)  */
+                       struct {
+                               /* Offset, in bytes, of the MBR partition, from
+                                * the beginning of the disk.  */
+                               le64 part_start_offset;
+
+                               /* Set to 0  */
+                               le64 padding;
+                       } mbr;
+
+                       /* (For GPT-formatted disks)  */
+                       struct {
+                               /* Unique GUID of the GPT partition  */
+                               u8 part_unique_guid[16];
+                       } gpt;
+               } partition;
+
+               /* Set to 0  */
+               le32 unknown_0x40;
+
+               /***********************
+                * Disk information
+                **********************/
+
+               /* 1 for MBR, 0 for GPT  */
+               le32 partition_table_type;
+       #define WIMOVERLAY_PARTITION_TYPE_MBR 1
+       #define WIMOVERLAY_PARTITION_TYPE_GPT 0
+
+               /* Disk identifier  */
+               union {
+                       /* (For MBR-formatted disks)  */
+                       struct {
+                               /* 4-byte ID of the MBR disk  */
+                               le32 disk_id;
+
+                               /* Set to 0  */
+                               le32 padding[3];
+                       } mbr;
+
+                       /* (For GPT-formatted disks)  */
+                       struct {
+                               /* GUID of the GPT disk  */
+                               u8 disk_guid[16];
+                       } gpt;
+               } disk;
+
+               /* Set to 0.  (This is the right size for some sort of optional
+                * GUID...)  */
+               le32 unknown_0x58[4];
+
+               /**************************
+                * Location in filesystem
+                *************************/
+
+               /* Null-terminated path to WIM file.  Begins with \ but does
+                * *not* include drive letter!  */
+               utf16lechar wim_file_name[];
+       } _packed_attribute;
+} _packed_attribute;
+
+static inline void
+wof_check_structs(void)
+{
+       BUILD_BUG_ON(sizeof(struct WimOverlay_dat_header) != 24);
+       BUILD_BUG_ON(sizeof(struct WimOverlay_dat_entry_1) != 40);
+       BUILD_BUG_ON(sizeof(struct WimOverlay_dat_entry_2) != 104);
+}
 
 /*****************************************************************************
  *
index b11e157aa4436faed51fa9896ff4777f324d23f9..b45db5973a9e261dfb316e0c355dbee04ce5f130 100644 (file)
@@ -878,6 +878,34 @@ wim_reshdr_to_data(const struct wim_reshdr *reshdr, WIMStruct *wim, void **buf_r
        return wim_resource_spec_to_data(&rspec, buf_ret);
 }
 
+int
+wim_reshdr_to_hash(const struct wim_reshdr *reshdr, WIMStruct *wim,
+                  u8 hash[SHA1_HASH_SIZE])
+{
+       struct wim_resource_spec rspec;
+       int ret;
+       struct wim_lookup_table_entry *lte;
+
+       wim_res_hdr_to_spec(reshdr, wim, &rspec);
+
+       lte = new_lookup_table_entry();
+       if (lte == NULL)
+               return WIMLIB_ERR_NOMEM;
+
+       lte_bind_wim_resource_spec(lte, &rspec);
+       lte->flags = rspec.flags;
+       lte->size = rspec.uncompressed_size;
+       lte->offset_in_res = 0;
+       lte->unhashed = 1;
+
+       ret = sha1_stream(lte);
+
+       lte_unbind_wim_resource_spec(lte);
+       copy_hash(hash, lte->hash);
+       free_lookup_table_entry(lte);
+       return ret;
+}
+
 struct streamifier_context {
        struct read_stream_list_callbacks cbs;
        struct wim_lookup_table_entry *cur_stream;
index 9270d41587d74c49b2af2c234bb4e32c4cea700b..7ee67f96b3c1a4d825dc236d4b2db58b2db46f45 100644 (file)
 #include "wimlib/win32.h"
 #include "wimlib/assert.h"
 #include "wimlib/error.h"
+#include "wimlib/lookup_table.h"
 #include "wimlib/util.h"
 #include "wimlib/wimboot.h"
 #include "wimlib/wof.h"
 
+static HANDLE
+open_file(const wchar_t *device_name, DWORD desiredAccess)
+{
+       return CreateFile(device_name, desiredAccess,
+                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                         NULL, OPEN_EXISTING,
+                         FILE_FLAG_BACKUP_SEMANTICS, NULL);
+}
+
+static BOOL
+query_device(HANDLE h, DWORD code, void *out, DWORD out_size)
+{
+       DWORD bytes_returned;
+       return DeviceIoControl(h, code, NULL, 0, out, out_size,
+                              &bytes_returned, NULL);
+}
+
+/*
+ * Gets partition and drive information for the specified path.
+ *
+ * @path
+ *     Absolute path which must begin with a drive letter.  For example, if the
+ *     path is D:\install.wim, this function will query information about the
+ *     D: volume.
+ * @part_info_ret
+ *     Partition info is returned here.
+ * @drive_info_ret
+ *     Drive info is returned here.  The contained partition info will not be
+ *     valid.
+ *
+ * Returns 0 on success, or a positive error code on failure.
+ */
+static int
+query_partition_and_disk_info(const wchar_t *path,
+                             PARTITION_INFORMATION_EX *part_info_ret,
+                             DRIVE_LAYOUT_INFORMATION_EX *drive_info_ret)
+{
+       HANDLE h;
+       wchar_t vol_name[] = L"\\\\.\\X:";
+       wchar_t disk_name[] = L"\\\\?\\PhysicalDriveXXXXXXXXXX";
+
+       PARTITION_INFORMATION_EX part_info;
+       size_t extents_size = sizeof(VOLUME_DISK_EXTENTS) + 4 * sizeof(DISK_EXTENT);
+       VOLUME_DISK_EXTENTS *extents = alloca(extents_size);
+       size_t drive_info_size = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
+                                       8 * sizeof(PARTITION_INFORMATION_EX);
+       DRIVE_LAYOUT_INFORMATION_EX *drive_info = alloca(drive_info_size);
+
+       wimlib_assert(path[0] != L'\0' && path[1] == L':');
+
+       *(wcschr(vol_name, L'X')) = path[0];
+
+       h = open_file(vol_name, GENERIC_READ);
+       if (h == INVALID_HANDLE_VALUE) {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("\"%ls\": Can't open volume device", vol_name);
+               return WIMLIB_ERR_OPEN;
+       }
+
+       if (!query_device(h, IOCTL_DISK_GET_PARTITION_INFO_EX,
+                         &part_info, sizeof(part_info)))
+       {
+               ERROR("\"%ls\": Can't get partition info (err=0x%08"PRIx32")",
+                     vol_name, (u32)GetLastError());
+               CloseHandle(h);
+               return WIMLIB_ERR_READ;
+       }
+
+       if (!query_device(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
+                         extents, extents_size))
+       {
+               ERROR("\"%ls\": Can't get volume extent info (err=0x%08"PRIx32")",
+                     vol_name, (u32)GetLastError());
+               CloseHandle(h);
+               return WIMLIB_ERR_READ;
+       }
+
+       CloseHandle(h);
+
+       if (extents->NumberOfDiskExtents != 1) {
+               ERROR("\"%ls\": This volume has %"PRIu32" disk extents, "
+                     "but this code is untested for more than 1",
+                     vol_name, (u32)extents->NumberOfDiskExtents);
+               return WIMLIB_ERR_UNSUPPORTED;
+       }
+
+       wsprintf(wcschr(disk_name, L'X'), L"%"PRIu32,
+                extents->Extents[0].DiskNumber);
+
+       h = open_file(disk_name, GENERIC_READ);
+       if (h == INVALID_HANDLE_VALUE) {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("\"%ls\": Can't open disk device", disk_name);
+               return WIMLIB_ERR_OPEN;
+       }
+
+       if (!query_device(h, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
+                         drive_info, drive_info_size))
+       {
+               ERROR("\"%ls\": Can't get disk info (err=0x%08"PRIx32")",
+                     disk_name, (u32)GetLastError());
+               CloseHandle(h);
+               return WIMLIB_ERR_READ;
+       }
+
+       CloseHandle(h);
+
+       if (drive_info->PartitionStyle != part_info.PartitionStyle) {
+               ERROR("\"%ls\", \"%ls\": Inconsistent partition table type!",
+                     vol_name, disk_name);
+               return WIMLIB_ERR_UNSUPPORTED;
+       }
+
+       if (part_info.PartitionStyle == PARTITION_STYLE_GPT) {
+               BUILD_BUG_ON(sizeof(part_info.Gpt.PartitionId) !=
+                            sizeof(drive_info->Gpt.DiskId));
+               if (!memcmp(&part_info.Gpt.PartitionId,
+                           &drive_info->Gpt.DiskId,
+                           sizeof(drive_info->Gpt.DiskId)))
+               {
+                       ERROR("\"%ls\", \"%ls\": Partition GUID is the "
+                             "same as the disk GUID???", vol_name, disk_name);
+                       return WIMLIB_ERR_UNSUPPORTED;
+               }
+       }
+
+       if (part_info.PartitionStyle != PARTITION_STYLE_MBR &&
+           part_info.PartitionStyle != PARTITION_STYLE_GPT)
+       {
+               ERROR("\"%ls\": Unknown partition style 0x%08"PRIx32,
+                     vol_name, (u32)part_info.PartitionStyle);
+               return WIMLIB_ERR_UNSUPPORTED;
+       }
+
+       *part_info_ret = part_info;
+       *drive_info_ret = *drive_info;
+       return 0;
+}
+
+/*
+ * Allocate a new WIM data source ID.
+ *
+ * @old_hdr
+ *     Previous WimOverlay.dat contents, or NULL if file did not exist.
+ *
+ * Returns the new data source ID.
+ */
+static u64
+alloc_new_data_source_id(const struct WimOverlay_dat_header *old_hdr)
+{
+       if (!old_hdr)
+               return 0;
+
+       for (u64 id = 0; ; id++) {
+               for (u32 i = 0; i < old_hdr->num_entries_1; i++)
+                       if (id == old_hdr->entry_1s[i].data_source_id)
+                               goto next;
+               return id;
+       next:
+               ;
+       }
+}
+
+/*
+ * Calculate the size of WimOverlay.dat with one entry added.
+ *
+ * @old_hdr
+ *     Previous WimOverlay.dat contents, or NULL if file did not exist.
+ * @new_entry_2_size
+ *     Size of entry_2 being added.
+ * @size_ret
+ *     Size will be returned here.
+ *
+ * Returns 0 on success, or WIMLIB_ERR_UNSUPPORTED if size overflows 32 bits.
+ */
+static int
+calculate_wimoverlay_dat_size(const struct WimOverlay_dat_header *old_hdr,
+                             u32 new_entry_2_size, u32 *size_ret)
+{
+       u64 size_64;
+       u32 size;
+
+       size_64 = sizeof(struct WimOverlay_dat_header);
+       if (old_hdr) {
+               for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
+                       size_64 += sizeof(struct WimOverlay_dat_entry_1);
+                       size_64 += old_hdr->entry_1s[i].entry_2_length;
+               }
+       }
+       size_64 += sizeof(struct WimOverlay_dat_entry_1);
+       size_64 += new_entry_2_size;
+
+       size = size_64;
+       if (size_64 != size)
+               return WIMLIB_ERR_UNSUPPORTED;
+
+       *size_ret = size;
+       return 0;
+}
+
+/*
+ * Writes @size bytes of @contents to the named file @path.
+ *
+ * Returns 0 on success; WIMLIB_ERR_OPEN or WIMLIB_ERR_WRITE on failure.
+ */
+static int
+write_wimoverlay_dat(const wchar_t *path, const void *contents, u32 size)
+{
+       HANDLE h;
+       DWORD bytes_written;
+
+       h = CreateFile(path, GENERIC_WRITE, 0, NULL,
+                      CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+       if (h == INVALID_HANDLE_VALUE) {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("\"%ls\": Can't open file for writing", path);
+               return WIMLIB_ERR_OPEN;
+       }
+
+       if (!WriteFile(h, contents, size, &bytes_written, NULL) ||
+           bytes_written != size)
+       {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("\"%ls\": Can't write file", path);
+               CloseHandle(h);
+               return WIMLIB_ERR_WRITE;
+       }
+
+       if (!CloseHandle(h)) {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("\"%ls\": Can't close handle", path);
+               return WIMLIB_ERR_WRITE;
+       }
+
+       return 0;
+}
+
+/*
+ * Generates the contents of WimOverlay.dat in memory, with one entry added.
+ *
+ * @buf
+ *     Buffer large enough to hold the new contents.
+ * @old_hdr
+ *     Old contents of WimOverlay.dat, or NULL if it did not exist.
+ * @wim_path
+ *     Absolute path to the WIM file.  It must begin with a drive letter; for
+ *     example, D:\install.wim.
+ * @wim_guid
+ *     GUID of the WIM, from the WIM header.
+ * @image
+ *     Number of the image in the WIM to specify in the new entry.
+ * @new_data_source_id
+ *     Data source ID to use for the new entry.
+ * @part_info
+ *     Partition information for the WIM file.
+ * @disk_info
+ *     Disk information for the WIM file.
+ * @new_entry_2_size
+ *     Size, in bytes, of the new location information structure ('struct
+ *     WimOverlay_dat_entry_2').
+ *
+ * Returns a pointer one past the last byte of @buf filled in.
+ */
+static u8 *
+fill_in_wimoverlay_dat(u8 *buf,
+                      const struct WimOverlay_dat_header *old_hdr,
+                      const wchar_t *wim_path,
+                      const u8 wim_guid[WIM_GID_LEN],
+                      int image,
+                      u64 new_data_source_id,
+                      const PARTITION_INFORMATION_EX *part_info,
+                      const DRIVE_LAYOUT_INFORMATION_EX *disk_info,
+                      u32 new_entry_2_size)
+{
+       struct WimOverlay_dat_header *new_hdr;
+       struct WimOverlay_dat_entry_1 *new_entry_1;
+       struct WimOverlay_dat_entry_2 *new_entry_2;
+       u32 entry_2_offset;
+       u8 *p = buf;
+
+       new_hdr = (struct WimOverlay_dat_header *)p;
+
+       /* Fill in new header  */
+       new_hdr->magic = WIMOVERLAY_DAT_MAGIC;
+       new_hdr->wim_provider_version = WIM_PROVIDER_CURRENT_VERSION;
+       new_hdr->unknown_0x08 = 0x00000028;
+       new_hdr->num_entries_1 = (old_hdr ? old_hdr->num_entries_1 : 0) + 1;
+       new_hdr->num_entries_2 = (old_hdr ? old_hdr->num_entries_2 : 0) + 1;
+       new_hdr->unknown_0x14 = 0x00000000;
+
+       p += sizeof(struct WimOverlay_dat_header);
+
+       /* Copy WIM-specific information for old entries  */
+       entry_2_offset = sizeof(struct WimOverlay_dat_header) +
+                       (new_hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1));
+       if (old_hdr) {
+               for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
+                       new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
+
+                       p = mempcpy(p, &old_hdr->entry_1s[i],
+                                   sizeof(struct WimOverlay_dat_entry_1));
+
+                       new_entry_1->entry_2_offset = entry_2_offset;
+                       entry_2_offset += new_entry_1->entry_2_length;
+               }
+       }
+
+       /* Generate WIM-specific information for new entry  */
+       new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
+
+       new_entry_1->data_source_id = new_data_source_id;
+       new_entry_1->entry_2_offset = entry_2_offset;
+       new_entry_1->entry_2_length = new_entry_2_size;
+       new_entry_1->wim_type = WIM_BOOT_NOT_OS_WIM;
+       new_entry_1->wim_index = image;
+       BUILD_BUG_ON(sizeof(new_entry_1->guid) != WIM_GID_LEN);
+       memcpy(new_entry_1->guid, wim_guid, WIM_GID_LEN);
+
+       p += sizeof(struct WimOverlay_dat_entry_1);
+
+       /* Copy WIM location information for old entries  */
+       if (old_hdr) {
+               for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
+                       wimlib_assert(new_hdr->entry_1s[i].entry_2_offset == p - buf);
+                       wimlib_assert(old_hdr->entry_1s[i].entry_2_length ==
+                                     new_hdr->entry_1s[i].entry_2_length);
+                       p = mempcpy(p,
+                                   ((const u8 *)old_hdr + old_hdr->entry_1s[i].entry_2_offset),
+                                   old_hdr->entry_1s[i].entry_2_length);
+               }
+       }
+
+       /* Generate WIM location information for new entry  */
+       new_entry_2 = (struct WimOverlay_dat_entry_2 *)p;
+
+       new_entry_2->unknown_0x00 = 0x00000000;
+       new_entry_2->unknown_0x04 = 0x00000000;
+       new_entry_2->entry_2_length = new_entry_2_size;
+       new_entry_2->unknown_0x0C = 0x00000000;
+       new_entry_2->unknown_0x10 = 0x00000005;
+       new_entry_2->unknown_0x14 = 0x00000001;
+       new_entry_2->inner_struct_size = new_entry_2_size - 0x14;
+       new_entry_2->unknown_0x1C = 0x00000005;
+       new_entry_2->unknown_0x20 = 0x00000006;
+       new_entry_2->unknown_0x24 = 0x00000000;
+       new_entry_2->unknown_0x28 = 0x00000048;
+       new_entry_2->unknown_0x2C = 0x00000000;
+       new_entry_2->unknown_0x40 = 0x00000000;
+
+       if (part_info->PartitionStyle == PARTITION_STYLE_MBR) {
+               new_entry_2->partition.mbr.part_start_offset = part_info->StartingOffset.QuadPart;
+               new_entry_2->partition.mbr.padding = 0;
+               new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_MBR;
+               new_entry_2->disk.mbr.disk_id = disk_info->Mbr.Signature;
+               new_entry_2->disk.mbr.padding[0] = 0x00000000;
+               new_entry_2->disk.mbr.padding[1] = 0x00000000;
+               new_entry_2->disk.mbr.padding[2] = 0x00000000;
+       } else {
+               BUILD_BUG_ON(sizeof(new_entry_2->partition.gpt.part_unique_guid) !=
+                            sizeof(part_info->Gpt.PartitionId));
+               memcpy(new_entry_2->partition.gpt.part_unique_guid,
+                      &part_info->Gpt.PartitionId,
+                      sizeof(part_info->Gpt.PartitionId));
+               new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_GPT;
+
+               BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
+                            sizeof(disk_info->Gpt.DiskId));
+               memcpy(new_entry_2->disk.gpt.disk_guid,
+                      &disk_info->Gpt.DiskId,
+                      sizeof(disk_info->Gpt.DiskId));
+
+               BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
+                            sizeof(new_entry_2->partition.gpt.part_unique_guid));
+       }
+       new_entry_2->unknown_0x58[0] = 0x00000000;
+       new_entry_2->unknown_0x58[1] = 0x00000000;
+       new_entry_2->unknown_0x58[2] = 0x00000000;
+       new_entry_2->unknown_0x58[3] = 0x00000000;
+
+       wimlib_assert(wim_path[2] == L'\\');
+       return mempcpy(new_entry_2->wim_file_name,
+                      wim_path + 2,
+                      new_entry_2_size - sizeof(struct WimOverlay_dat_entry_2));
+}
+
+/*
+ * Prepares the new contents of WimOverlay.dat in memory, with one entry added.
+ *
+ * @old_hdr
+ *     Old contents of WimOverlay.dat, or NULL if it did not exist.
+ * @wim_path
+ *     Absolute path to the WIM file.  It must begin with a drive letter; for
+ *     example, D:\install.wim.
+ * @wim_guid
+ *     GUID of the WIM, from the WIM header.
+ * @image
+ *     Number of the image in the WIM to specify in the new entry.
+ * @new_contents_ret
+ *     Location into which to return the new contents as a malloc()ed buffer on
+ *     success.
+ * @new_contents_size_ret
+ *     Location into which to return the size, in bytes, of the new contents on
+ *     success.
+ * @new_data_source_id_ret
+ *     Location into which to return the data source ID of the new entry on
+ *     success.
+ *
+ * Returns 0 on success, or a positive error code on failure.
+ */
+static int
+prepare_wimoverlay_dat(const struct WimOverlay_dat_header *old_hdr,
+                      const wchar_t *wim_path,
+                      const u8 wim_guid[WIM_GID_LEN],
+                      int image,
+                      void **new_contents_ret,
+                      u32 *new_contents_size_ret,
+                      u64 *new_data_source_id_ret)
+{
+       int ret;
+       PARTITION_INFORMATION_EX part_info;
+       DRIVE_LAYOUT_INFORMATION_EX disk_info;
+       u64 new_data_source_id;
+       u32 new_entry_2_size;
+       u32 new_contents_size;
+       u8 *buf;
+       u8 *end;
+
+       ret = query_partition_and_disk_info(wim_path, &part_info, &disk_info);
+       if (ret)
+               return ret;
+
+       new_data_source_id = alloc_new_data_source_id(old_hdr);
+
+       new_entry_2_size = sizeof(struct WimOverlay_dat_entry_2) +
+                               ((wcslen(wim_path) - 2 + 1) * sizeof(wchar_t));
+       ret = calculate_wimoverlay_dat_size(old_hdr, new_entry_2_size,
+                                           &new_contents_size);
+       if (ret)
+               return ret;
+
+       buf = MALLOC(new_contents_size);
+       if (!buf)
+               return WIMLIB_ERR_NOMEM;
+
+       end = fill_in_wimoverlay_dat(buf, old_hdr, wim_path, wim_guid, image,
+                                    new_data_source_id,
+                                    &part_info, &disk_info, new_entry_2_size);
+
+       wimlib_assert(end - buf == new_contents_size);
+
+       *new_contents_ret = buf;
+       *new_contents_size_ret = new_contents_size;
+       *new_data_source_id_ret = new_data_source_id;
+       return 0;
+}
+
+/*
+ * Reads and validates a WimOverlay.dat file.
+ *
+ * @path
+ *     Path to the WimOverlay.dat file, such as
+ *     C:\System Volume Information\WimOverlay.dat
+ * @contents_ret
+ *     Location into which to return the contents as a malloc()ed buffer on
+ *     success.  This can be cast to 'struct WimOverlay_dat_header', and its
+ *     contents are guaranteed to be valid.  Alternatively, if the file does
+ *     not exist, NULL will be returned here.
+ *
+ * Returns 0 on success, or a positive error code on failure.
+ */
+static int
+read_wimoverlay_dat(const wchar_t *path, void **contents_ret)
+{
+       HANDLE h;
+       BY_HANDLE_FILE_INFORMATION info;
+       int ret;
+       void *contents;
+       const struct WimOverlay_dat_header *hdr;
+       DWORD bytes_read;
+       bool already_retried = false;
+
+retry:
+       h = open_file(path, GENERIC_READ);
+       if (h == INVALID_HANDLE_VALUE) {
+               DWORD err = GetLastError();
+               if (err == ERROR_FILE_NOT_FOUND) {
+                       *contents_ret = NULL;
+                       return 0;
+               }
+               if (err == ERROR_PATH_NOT_FOUND &&
+                   func_RtlCreateSystemVolumeInformationFolder)
+               {
+                       wchar_t volume_root_path[] = L"\\??\\X:\\";
+
+                       *(wcschr(volume_root_path, L'X')) = path[0];
+
+                       UNICODE_STRING str = {
+                               .Length = sizeof(volume_root_path) - sizeof(wchar_t),
+                               .MaximumLength = sizeof(volume_root_path),
+                               .Buffer = volume_root_path,
+                       };
+                       NTSTATUS status;
+                       DWORD err2;
+
+                       status = (*func_RtlCreateSystemVolumeInformationFolder)(&str);
+
+                       err2 = (*func_RtlNtStatusToDosError)(status);
+                       if (err2 == ERROR_SUCCESS) {
+                               if (!already_retried) {
+                                       already_retried = true;
+                                       goto retry;
+                               }
+                       } else {
+                               err = err2;
+                       }
+               }
+               set_errno_from_win32_error(err);
+               ERROR_WITH_ERRNO("\"%ls\": Can't open for reading", path);
+               return WIMLIB_ERR_OPEN;
+       }
+       if (!GetFileInformationByHandle(h, &info)) {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("\"%ls\": Can't query metadata", path);
+               CloseHandle(h);
+               return WIMLIB_ERR_STAT;
+       }
+
+       contents = NULL;
+       if (!info.nFileSizeHigh)
+               contents = MALLOC(info.nFileSizeLow);
+       if (!contents) {
+               ERROR("\"%ls\": File is too large to fit into memory", path);
+               CloseHandle(h);
+               return WIMLIB_ERR_NOMEM;
+       }
+
+       if (!ReadFile(h, contents, info.nFileSizeLow, &bytes_read, NULL) ||
+           bytes_read != info.nFileSizeLow)
+       {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("\"%ls\": Can't read data", path);
+               CloseHandle(h);
+               ret = WIMLIB_ERR_READ;
+               goto out_free_contents;
+       }
+
+       CloseHandle(h);
+
+       if (info.nFileSizeLow < sizeof(struct WimOverlay_dat_header)) {
+               ERROR("\"%ls\": File is unexpectedly small (only %"PRIu32" bytes)",
+                     path, (u32)info.nFileSizeLow);
+               ret = WIMLIB_ERR_UNSUPPORTED;
+               goto out_free_contents;
+       }
+
+       hdr = (const struct WimOverlay_dat_header *)contents;
+
+       if (hdr->magic != WIMOVERLAY_DAT_MAGIC ||
+           hdr->wim_provider_version != WIM_PROVIDER_CURRENT_VERSION ||
+           hdr->unknown_0x08 != 0x00000028 ||
+           (hdr->num_entries_1 != hdr->num_entries_2) ||
+           hdr->unknown_0x14 != 0x00000000)
+       {
+               ERROR("\"%ls\": Header contains unexpected data:", path);
+               if (wimlib_print_errors) {
+                       print_byte_field((const u8 *)hdr,
+                                        sizeof(struct WimOverlay_dat_header),
+                                        stderr);
+                       fputc('\n', stderr);
+               }
+               ret = WIMLIB_ERR_UNSUPPORTED;
+               goto out_free_contents;
+       }
+
+       if ((u64)hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1) >
+           info.nFileSizeLow - sizeof(struct WimOverlay_dat_header))
+       {
+               ERROR("\"%ls\": File is unexpectedly small "
+                     "(only %"PRIu32" bytes, but has %"PRIu32" entries)",
+                     path, (u32)info.nFileSizeLow, hdr->num_entries_1);
+               ret = WIMLIB_ERR_UNSUPPORTED;
+               goto out_free_contents;
+       }
+
+       for (u32 i = 0; i < hdr->num_entries_1; i++) {
+               const struct WimOverlay_dat_entry_1 *entry_1;
+               const struct WimOverlay_dat_entry_2 *entry_2;
+               u32 wim_file_name_length;
+
+               entry_1 = &hdr->entry_1s[i];
+
+               if (((u64)entry_1->entry_2_offset +
+                    (u64)entry_1->entry_2_length) >
+                   info.nFileSizeLow)
+               {
+                       ERROR("\"%ls\": entry %"PRIu32" (2) "
+                             "(data source ID 0x%016"PRIx64") "
+                             "overflows file",
+                             path, i, entry_1->data_source_id);
+                       ret = WIMLIB_ERR_UNSUPPORTED;
+                       goto out_free_contents;
+               }
+               if (entry_1->entry_2_length < sizeof(struct WimOverlay_dat_entry_2)) {
+                       ERROR("\"%ls\": entry %"PRIu32" (2) "
+                             "(data source ID 0x%016"PRIx64") "
+                             "is too short",
+                             path, i, entry_1->data_source_id);
+                       ret = WIMLIB_ERR_UNSUPPORTED;
+                       goto out_free_contents;
+               }
+
+               if (entry_1->entry_2_offset % 2 != 0) {
+                       ERROR("\"%ls\": entry %"PRIu32" (2) "
+                             "(data source ID 0x%016"PRIx64") "
+                             "is misaligned",
+                             path, i, entry_1->data_source_id);
+                       ret = WIMLIB_ERR_UNSUPPORTED;
+                       goto out_free_contents;
+               }
+
+               entry_2 = (const struct WimOverlay_dat_entry_2 *)
+                               ((const u8 *)hdr + entry_1->entry_2_offset);
+
+               wim_file_name_length = entry_1->entry_2_length -
+                                       sizeof(struct WimOverlay_dat_entry_2);
+               if ((wim_file_name_length < 2 * sizeof(wchar_t)) ||
+                   (wim_file_name_length % sizeof(wchar_t) != 0) ||
+                   (wmemchr(entry_2->wim_file_name, L'\0',
+                            wim_file_name_length / sizeof(wchar_t))
+                    != &entry_2->wim_file_name[wim_file_name_length /
+                                               sizeof(wchar_t) - 1]))
+               {
+                       ERROR("\"%ls\": entry %"PRIu32" (2) "
+                             "(data source ID 0x%016"PRIx64") "
+                             "has invalid WIM file name",
+                             path, i, entry_1->data_source_id);
+                       if (wimlib_print_errors) {
+                               print_byte_field((const u8 *)entry_2->wim_file_name,
+                                                wim_file_name_length, stderr);
+                               fputc('\n', stderr);
+                       }
+                       ret = WIMLIB_ERR_UNSUPPORTED;
+                       goto out_free_contents;
+               }
+
+               if (entry_2->unknown_0x00 != 0x00000000 ||
+                   entry_2->unknown_0x04 != 0x00000000 ||
+                   entry_2->unknown_0x0C != 0x00000000 ||
+                   entry_2->entry_2_length != entry_1->entry_2_length ||
+                   entry_2->unknown_0x10 != 0x00000005 ||
+                   entry_2->unknown_0x14 != 0x00000001 ||
+                   entry_2->inner_struct_size != entry_1->entry_2_length - 0x14 ||
+                   entry_2->unknown_0x1C != 0x00000005 ||
+                   entry_2->unknown_0x20 != 0x00000006 ||
+                   entry_2->unknown_0x24 != 0x00000000 ||
+                   entry_2->unknown_0x28 != 0x00000048 ||
+                   entry_2->unknown_0x2C != 0x00000000 ||
+                   entry_2->unknown_0x40 != 0x00000000 ||
+                   (entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_GPT &&
+                    entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_MBR) ||
+                   (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_MBR &&
+                    entry_2->partition.mbr.padding != 0) ||
+                   (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_GPT &&
+                    entry_2->partition.mbr.padding == 0) ||
+                   entry_2->unknown_0x58[0] != 0x00000000 ||
+                   entry_2->unknown_0x58[1] != 0x00000000 ||
+                   entry_2->unknown_0x58[2] != 0x00000000 ||
+                   entry_2->unknown_0x58[3] != 0x00000000)
+               {
+                       ERROR("\"%ls\": entry %"PRIu32" (2) "
+                             "(data source ID 0x%016"PRIx64") "
+                             "contains unexpected data!",
+                             path, i, entry_1->data_source_id);
+                       if (wimlib_print_errors) {
+                               print_byte_field((const u8 *)entry_2,
+                                                entry_1->entry_2_length,
+                                                stderr);
+                               fputc('\n', stderr);
+                       }
+                       ret = WIMLIB_ERR_UNSUPPORTED;
+                       goto out_free_contents;
+               }
+       }
+
+       *contents_ret = contents;
+       return 0;
+
+out_free_contents:
+       FREE(contents);
+       return ret;
+}
+
+/*
+ * Update WimOverlay.dat manually in order to add a WIM data source to the
+ * target volume.
+ *
+ * THIS CODE IS EXPERIMENTAL AS I HAD TO REVERSE ENGINEER THE FILE FORMAT!
+ *
+ * @path
+ *     Target drive.  Must be a letter followed by a colon (e.g. D:).
+ * @wim_path
+ *     Absolute path to the WIM file.  It must begin with a drive letter; for
+ *     example, D:\install.wim.
+ * @wim_guid
+ *     GUID of the WIM, from the WIM header.
+ * @image
+ *     Number of the image in the WIM to specify in the new entry.
+ * @data_source_id_ret
+ *     On success, the allocated data source ID is returned here.
+ */
+static int
+update_wimoverlay_manually(const wchar_t *drive, const wchar_t *wim_path,
+                          const u8 wim_guid[WIM_GID_LEN],
+                          int image, u64 *data_source_id_ret)
+{
+       wchar_t path_main[] = L"A:\\System Volume Information\\WimOverlay.dat";
+       wchar_t path_backup[] = L"A:\\System Volume Information\\WimOverlay.backup";
+       wchar_t path_wimlib_backup[] = L"A:\\System Volume Information\\WimOverlay.wimlib_backup";
+       wchar_t path_new[] = L"A:\\System Volume Information\\WimOverlay.wimlib_new";
+       void *old_contents;
+       void *new_contents;
+       u32 new_contents_size;
+       u64 new_data_source_id;
+       int ret;
+
+       wimlib_assert(drive[0] != L'\0' &&
+                     drive[1] == L':' &&
+                     drive[2] == L'\0');
+
+       path_main[0]          = drive[0];
+       path_backup[0]        = drive[0];
+       path_wimlib_backup[0] = drive[0];
+       path_new[0]           = drive[0];
+
+       ret = read_wimoverlay_dat(path_main, &old_contents);
+       if (ret)
+               goto out;
+
+       ret = prepare_wimoverlay_dat(old_contents, wim_path, wim_guid, image,
+                                    &new_contents, &new_contents_size,
+                                    &new_data_source_id);
+       FREE(old_contents);
+       if (ret)
+               goto out;
+
+       /* Write WimOverlay.wimlib_new  */
+       ret = write_wimoverlay_dat(path_new,
+                                  new_contents, new_contents_size);
+       if (ret)
+               goto out_free_new_contents;
+
+       /* Write WimOverlay.backup  */
+       ret = write_wimoverlay_dat(path_backup,
+                                  new_contents, new_contents_size);
+       if (ret)
+               goto out_free_new_contents;
+
+       if (old_contents) {
+               /* Rename WimOverlay.dat => WimOverlay.wimlib_backup  */
+               ret = win32_rename_replacement(path_main, path_wimlib_backup);
+               if (ret) {
+                       ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
+                                        path_main, path_wimlib_backup);
+                       goto out_free_new_contents;
+               }
+       }
+
+       /* Rename WimOverlay.wimlib_new => WimOverlay.dat  */
+       ret = win32_rename_replacement(path_new, path_main);
+       if (ret) {
+               ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
+                                path_new, path_main);
+       }
+out_free_new_contents:
+       FREE(new_contents);
+out:
+       if (ret == WIMLIB_ERR_UNSUPPORTED) {
+               ERROR("Please report to developer ("PACKAGE_BUGREPORT").\n"
+                     "        If possible send the file \"%ls\".\n\n", path_main);
+       }
+       if (ret == 0)
+               *data_source_id_ret = new_data_source_id;
+       return ret;
+}
+
 static int
 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
 {
@@ -111,8 +897,10 @@ try_to_attach_wof(const wchar_t *drive)
  * @wim_path
  *     Absolute path to the WIM file.  This must include a drive letter and use
  *     backslash path separators.
+ * @wim_guid
+ *     GUID of the WIM, from the WIM header.
  * @image
- *     Index of the image in the WIM being applied.
+ *     Number of the image in the WIM being applied.
  * @target
  *     Path to the target drive.
  * @data_source_id_ret
@@ -122,8 +910,10 @@ try_to_attach_wof(const wchar_t *drive)
  * Returns 0 on success, or a positive error code on failure.
  */
 int
-wimboot_alloc_data_source_id(const wchar_t *wim_path, int image,
-                            const wchar_t *target, u64 *data_source_id_ret)
+wimboot_alloc_data_source_id(const wchar_t *wim_path,
+                            const u8 wim_guid[WIM_GID_LEN],
+                            int image, const wchar_t *target,
+                            u64 *data_source_id_ret, bool *wof_running_ret)
 {
        tchar drive_path[7];
        size_t wim_path_nchars;
@@ -175,9 +965,7 @@ wimboot_alloc_data_source_id(const wchar_t *wim_path, int image,
        wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
 
 retry_ioctl:
-       h = CreateFile(drive_path, GENERIC_WRITE,
-                      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                      NULL, OPEN_EXISTING, 0, NULL);
+       h = open_file(drive_path, GENERIC_WRITE);
 
        if (h == INVALID_HANDLE_VALUE) {
                set_errno_from_GetLastError();
@@ -200,17 +988,13 @@ retry_ioctl:
                                if (try_to_attach_wof(drive_path + 4))
                                        goto retry_ioctl;
                        }
-                       ERROR("The version of Windows you are running does not appear to support\n"
-                             "        the Windows Overlay File System Filter Driver.  Therefore, wimlib\n"
-                             "        cannot apply WIMBoot information.  Please run from Windows 8.1\n"
-                             "        Update 1 or later.");
                        ret = WIMLIB_ERR_UNSUPPORTED;
                        goto out_close_handle;
                } else {
                        set_errno_from_win32_error(err);
                        ERROR_WITH_ERRNO("Failed to add overlay source \"%ls\" "
                                         "to volume \"%ls\" (err=0x%08"PRIx32")",
-                                        wim_path, drive_path + 4, (uint32_t)err);
+                                        wim_path, drive_path + 4, (u32)err);
                        ret = WIMLIB_ERR_WIMBOOT;
                        goto out_close_handle;
                }
@@ -225,6 +1009,7 @@ retry_ioctl:
                goto out_close_handle;
        }
 
+       *wof_running_ret = true;
        *data_source_id_ret = data_source_id;
        ret = 0;
 
@@ -233,6 +1018,21 @@ out_close_handle:
 out_free_in:
        FREE(in);
 out:
+       if (ret == WIMLIB_ERR_UNSUPPORTED) {
+       #if 0
+               WARNING(
+               "The version of Windows you are running does not appear to support\n"
+               "        the Windows Overlay File System Filter Driver.  This is normally\n"
+               "        available on Windows 8.1 Update 1 or later.  Therefore, wimlib\n"
+               "        will attempt to update the WimOverlay.dat file directly.\n");
+       #else
+               WARNING("WOF driver is not available; updating WimOverlay.dat directly.");
+       #endif
+               ret = update_wimoverlay_manually(drive_path + 4, wim_path,
+                                                wim_guid, image,
+                                                data_source_id_ret);
+               *wof_running_ret = false;
+       }
        return ret;
 }
 
@@ -240,35 +1040,36 @@ out:
 /*
  * Set WIMBoot information on the specified file.
  *
+ * This turns it into a reparse point that redirects accesses to it, to the
+ * corresponding resource in the WIM archive.
+ *
  * @path
  *     Path to extracted file (already created).
+ * @lte
+ *     Unnamed data stream of the file.
  * @data_source_id
- *     Identifier for backing WIM file.
- * @hash
- *     SHA-1 message digest of the file's unnamed data stream.
+ *     Allocated identifier for the WIM data source on the destination volume.
+ * @lookup_table_hash
+ *     SHA-1 message digest of the WIM's lookup table.
+ * @wof_running
+ *     %true if the WOF driver appears to be available and working; %false if
+ *     not.
  *
  * Returns 0 on success, or a positive error code on failure.
  */
 int
-wimboot_set_pointer(const wchar_t *path, u64 data_source_id,
-                   const u8 hash[20])
+wimboot_set_pointer(const wchar_t *path,
+                   const struct wim_lookup_table_entry *lte,
+                   u64 data_source_id,
+                   const u8 lookup_table_hash[SHA1_HASH_SIZE],
+                   bool wof_running)
 {
-       struct {
-               struct wof_external_info wof_info;
-               struct wim_provider_external_info wim_info;
-       } in;
        HANDLE h;
        DWORD bytes_returned;
        int ret;
 
-       in.wof_info.version = WOF_CURRENT_VERSION;
-       in.wof_info.provider = WOF_PROVIDER_WIM;
-
-       in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
-       in.wim_info.flags = 0;
-       in.wim_info.data_source_id = data_source_id;
-       memcpy(in.wim_info.resource_hash, hash, 20);
 
+       /* Open the file  */
        h = win32_open_existing_file(path, GENERIC_WRITE);
        if (h == INVALID_HANDLE_VALUE) {
                set_errno_from_GetLastError();
@@ -276,21 +1077,99 @@ wimboot_set_pointer(const wchar_t *path, u64 data_source_id,
                goto out;
        }
 
-       if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
-                            &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
+       if (wof_running) {
+               /* The WOF driver is running.  We can create the reparse point
+                * using FSCTL_SET_EXTERNAL_BACKING.  */
+
+               struct {
+                       struct wof_external_info wof_info;
+                       struct wim_provider_external_info wim_info;
+               } in;
+
+               in.wof_info.version = WOF_CURRENT_VERSION;
+               in.wof_info.provider = WOF_PROVIDER_WIM;
+
+               in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
+               in.wim_info.flags = 0;
+               in.wim_info.data_source_id = data_source_id;
+               copy_hash(in.wim_info.resource_hash, lte->hash);
+
+               /* lookup_table_hash is not necessary  */
+
+               if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
+                                    &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
+                       goto fail;
+       } else {
+
+               /* The WOF driver is running.  We need to create the reparse
+                * point manually.  */
+
+               struct {
+                       struct {
+                               le32 rptag;
+                               le16 rpdatalen;
+                               le16 rpreserved;
+                       } hdr;
+                       struct wof_external_info wof_info;
+                       struct wim_provider_rpdata wim_info;
+               } in;
+
+               BUILD_BUG_ON(sizeof(in) != 8 +
+                            sizeof(struct wof_external_info) +
+                            sizeof(struct wim_provider_rpdata));
+
+               in.hdr.rptag = WIMLIB_REPARSE_TAG_WOF;
+               in.hdr.rpdatalen = sizeof(in) - sizeof(in.hdr);
+               in.hdr.rpreserved = 0;
+
+               in.wof_info.version = WOF_CURRENT_VERSION;
+               in.wof_info.provider = WOF_PROVIDER_WIM;
+
+               in.wim_info.version = 2;
+               in.wim_info.flags = 0;
+               in.wim_info.data_source_id = data_source_id;
+               copy_hash(in.wim_info.resource_hash, lte->hash);
+               copy_hash(in.wim_info.wim_lookup_table_hash, lookup_table_hash);
+               in.wim_info.stream_uncompressed_size = lte->size;
+               in.wim_info.stream_compressed_size = lte->rspec->size_in_wim;
+               in.wim_info.stream_offset_in_wim = lte->rspec->offset_in_wim;
+
+               if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
+                                    &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
+                       goto fail;
+
+               /* We also need to create an unnamed data stream of the correct
+                * size.  Otherwise the file shows up as zero length.  It can be
+                * a sparse stream containing all zeroes; its contents
+                * are unimportant.  */
+               if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
+                                    &bytes_returned, NULL))
+                       goto fail;
+
+               if (!SetFilePointerEx(h,
+                                     (LARGE_INTEGER){ .QuadPart = lte->size},
+                                     NULL, FILE_BEGIN))
+                       goto fail;
+
+               if (!SetEndOfFile(h))
+                       goto fail;
+       }
+
+       ret = 0;
+out_close_handle:
+       CloseHandle(h);
+out:
+       return ret;
+
+fail:
        {
                DWORD err = GetLastError();
                set_errno_from_win32_error(err);
                ERROR_WITH_ERRNO("\"%ls\": Couldn't set WIMBoot pointer data "
-                                "(err=0x%08"PRIx32")", path, (uint32_t)err);
+                                "(err=0x%08"PRIx32")", path, (u32)err);
                ret = WIMLIB_ERR_WIMBOOT;
                goto out_close_handle;
        }
-       ret = 0;
-out_close_handle:
-       CloseHandle(h);
-out:
-       return ret;
 }
 
 #endif /* __WIN32__ */
index c182862ea42af8e4483a687df00d665222184216..48ff5d87e01efa609745090195cd0903546a75ca 100644 (file)
 #include "wimlib/error.h"
 #include "wimlib/lookup_table.h"
 #include "wimlib/paths.h"
+#include "wimlib/resource.h"
 #include "wimlib/textfile.h"
 #include "wimlib/xml.h"
 #include "wimlib/wim.h"
 #include "wimlib/wimboot.h"
 
-static void
-ctx_save_data_source_id(struct apply_ctx *ctx, u64 data_source_id)
-{
-       ctx->private[0] = data_source_id & 0xFFFFFFFF;
-       ctx->private[1] = data_source_id >> 32;
-}
-
-static u64
-ctx_get_data_source_id(const struct apply_ctx *ctx)
-{
-       return (u32)ctx->private[0] | ((u64)(u32)ctx->private[1] << 32);
-}
-
-static void
-set_prepopulate_pats(struct apply_ctx *ctx, struct string_set *s)
-{
-       ctx->private[2] = (intptr_t)s;
-}
+struct win32_apply_private_data {
+       u64 data_source_id;
+       struct string_set *prepopulate_pats;
+       void *mem_prepopulate_pats;
+       u8 wim_lookup_table_hash[SHA1_HASH_SIZE];
+       bool wof_running;
+};
 
-static struct string_set *
-get_prepopulate_pats(struct apply_ctx *ctx)
+static struct win32_apply_private_data *
+get_private_data(struct apply_ctx *ctx)
 {
-       return (struct string_set *)(ctx->private[2]);
+       BUILD_BUG_ON(sizeof(ctx->private) < sizeof(struct win32_apply_private_data));
+       return (struct win32_apply_private_data *)(ctx->private);
 }
 
 static void
-free_prepopulate_pats(struct apply_ctx *ctx)
+free_prepopulate_pats(struct win32_apply_private_data *dat)
 {
-       struct string_set *s;
-
-       s = get_prepopulate_pats(ctx);
-       if (s) {
-               FREE(s->strings);
-               FREE(s);
+       if (dat->prepopulate_pats) {
+               FREE(dat->prepopulate_pats->strings);
+               FREE(dat->prepopulate_pats);
+               dat->prepopulate_pats = NULL;
        }
-       set_prepopulate_pats(ctx, NULL);
 
-       FREE((void *)ctx->private[3]);
-       ctx->private[3] = (intptr_t)NULL;
+       if (dat->mem_prepopulate_pats) {
+               FREE(dat->mem_prepopulate_pats);
+               dat->mem_prepopulate_pats = NULL;
+       }
 }
 
 static int
@@ -94,6 +84,7 @@ load_prepopulate_pats(struct apply_ctx *ctx)
        void *buf;
        void *mem;
        struct text_file_section sec;
+       struct win32_apply_private_data *dat = get_private_data(ctx);
 
        dentry = get_dentry(ctx->wim, path, WIMLIB_CASE_INSENSITIVE);
        if (!dentry ||
@@ -128,19 +119,18 @@ load_prepopulate_pats(struct apply_ctx *ctx)
                FREE(s);
                return ret;
        }
-       set_prepopulate_pats(ctx, s);
-       ctx->private[3] = (intptr_t)mem;
+       dat->prepopulate_pats = s;
+       dat->mem_prepopulate_pats = mem;
        return 0;
 }
 
 static bool
-in_prepopulate_list(struct wim_dentry *dentry,
-                   struct apply_ctx *ctx)
+in_prepopulate_list(struct wim_dentry *dentry, struct apply_ctx *ctx)
 {
        struct string_set *pats;
        const tchar *path;
 
-       pats = get_prepopulate_pats(ctx);
+       pats = get_private_data(ctx)->prepopulate_pats;
        if (!pats)
                return false;
        path = dentry_full_path(dentry);
@@ -150,16 +140,23 @@ in_prepopulate_list(struct wim_dentry *dentry,
        return match_pattern(path, path_basename(path), pats);
 }
 
+static int
+hash_lookup_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE])
+{
+       return wim_reshdr_to_hash(&wim->hdr.lookup_table_reshdr, wim, hash);
+}
+
 static int
 win32_start_extract(const wchar_t *path, struct apply_ctx *ctx)
 {
        int ret;
        unsigned vol_flags;
        bool supports_SetFileShortName;
+       struct win32_apply_private_data *dat = get_private_data(ctx);
 
        ret = win32_get_vol_flags(path, &vol_flags, &supports_SetFileShortName);
        if (ret)
-               return ret;
+               goto err;
 
        ctx->supported_features.archive_files = 1;
        ctx->supported_features.hidden_files = 1;
@@ -200,32 +197,38 @@ win32_start_extract(const wchar_t *path, struct apply_ctx *ctx)
 
                ret = load_prepopulate_pats(ctx);
                if (ret == WIMLIB_ERR_NOMEM)
-                       return ret;
-
-               u64 data_source_id;
+                       goto err;
 
                if (!wim_info_get_wimboot(ctx->wim->wim_info,
                                          ctx->wim->current_image))
                        WARNING("Image is not marked as WIMBoot compatible!");
 
+
+               ret = hash_lookup_table(ctx->wim, dat->wim_lookup_table_hash);
+               if (ret)
+                       goto err;
+
                ret = wimboot_alloc_data_source_id(ctx->wim->filename,
+                                                  ctx->wim->hdr.guid,
                                                   ctx->wim->current_image,
-                                                  path, &data_source_id);
-               if (ret) {
-                       free_prepopulate_pats(ctx);
-                       return ret;
-               }
-
-               ctx_save_data_source_id(ctx, data_source_id);
+                                                  path,
+                                                  &dat->data_source_id,
+                                                  &dat->wof_running);
+               if (ret)
+                       goto err;
        }
 
        return 0;
+
+err:
+       free_prepopulate_pats(dat);
+       return ret;
 }
 
 static int
 win32_finish_extract(struct apply_ctx *ctx)
 {
-       free_prepopulate_pats(ctx);
+       free_prepopulate_pats(get_private_data(ctx));
        return 0;
 }
 
@@ -418,11 +421,16 @@ win32_extract_unnamed_stream(file_spec_t file,
            && lte
            && lte->resource_location == RESOURCE_IN_WIM
            && lte->rspec->wim == ctx->wim
+           && lte->size == lte->rspec->uncompressed_size
            && !in_prepopulate_list(dentry, ctx))
        {
-               return wimboot_set_pointer(file.path,
-                                          ctx_get_data_source_id(ctx),
-                                          lte->hash);
+               const struct win32_apply_private_data *dat;
+
+               dat = get_private_data(ctx);
+               return wimboot_set_pointer(file.path, lte,
+                                          dat->data_source_id,
+                                          dat->wim_lookup_table_hash,
+                                          dat->wof_running);
        }
 
        return win32_extract_stream(file.path, NULL, 0, lte, ctx);
index bcd5c55037b8ab12edf0001eb8689be78511f833..67d208d23256c5daa347dfb0cb59cf7c65890f4b 100644 (file)
@@ -563,6 +563,9 @@ NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle,
                                            SECURITY_INFORMATION SecurityInformation,
                                            PSECURITY_DESCRIPTOR SecurityDescriptor);
 
+NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
+               (PCUNICODE_STRING VolumeRootPath);
+
 #endif /* WITH_NTDLL */
 
 static OSVERSIONINFO windows_version_info = {
@@ -640,6 +643,8 @@ win32_global_init(int init_flags)
 
                        func_NtSetSecurityObject    =
                                (void*)GetProcAddress(hNtdll, "NtSetSecurityObject");
+                       func_RtlCreateSystemVolumeInformationFolder =
+                               (void*)GetProcAddress(hNtdll, "RtlCreateSystemVolumeInformationFolder");
                }
        }
 
@@ -651,6 +656,8 @@ win32_global_init(int init_flags)
        DEBUG("NtQueryDirectoryFile   @ %p", func_NtQueryDirectoryFile);
        DEBUG("NtQueryInformationFile @ %p", func_NtQueryInformationFile);
        DEBUG("NtSetSecurityObject    @ %p", func_NtSetSecurityObject);
+       DEBUG("RtlCreateSystemVolumeInformationFolder    @ %p",
+             func_RtlCreateSystemVolumeInformationFolder);
 #endif
 
        return 0;