4 * Support for creating WIMBoot pointer files.
6 * See http://technet.microsoft.com/en-us/library/dn594399.aspx for general
7 * information about WIMBoot.
9 * Note that WIMBoot pointer files are actually implemented on top of the
10 * Windows Overlay File System Filter (WOF). See wof.h for more info.
14 * Copyright (C) 2014 Eric Biggers
16 * This file is free software; you can redistribute it and/or modify it under
17 * the terms of the GNU Lesser General Public License as published by the Free
18 * Software Foundation; either version 3 of the License, or (at your option) any
21 * This file is distributed in the hope that it will be useful, but WITHOUT
22 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
26 * You should have received a copy of the GNU Lesser General Public License
27 * along with this file; if not, see http://www.gnu.org/licenses/.
36 #include "wimlib/win32_common.h"
38 #include "wimlib/assert.h"
39 #include "wimlib/error.h"
40 #include "wimlib/lookup_table.h"
41 #include "wimlib/util.h"
42 #include "wimlib/wimboot.h"
43 #include "wimlib/win32.h"
44 #include "wimlib/wof.h"
47 open_file(const wchar_t *device_name, DWORD desiredAccess)
49 return CreateFile(device_name, desiredAccess,
50 FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
51 FILE_FLAG_BACKUP_SEMANTICS, NULL);
55 query_device(HANDLE h, DWORD code, void *out, DWORD out_size)
58 return DeviceIoControl(h, code, NULL, 0, out, out_size,
59 &bytes_returned, NULL);
63 * Gets partition and drive information for the specified path.
66 * Absolute path which must begin with a drive letter. For example, if the
67 * path is D:\install.wim, this function will query information about the
70 * Partition info is returned here.
72 * Drive info is returned here. The contained partition info will not be
75 * Returns 0 on success, or a positive error code on failure.
78 query_partition_and_disk_info(const wchar_t *path,
79 PARTITION_INFORMATION_EX *part_info,
80 DRIVE_LAYOUT_INFORMATION_EX *drive_info_ret)
82 wchar_t vol_name[] = L"\\\\.\\X:";
83 wchar_t disk_name[] = L"\\\\?\\PhysicalDriveXXXXXXXXXX";
84 HANDLE h = INVALID_HANDLE_VALUE;
85 VOLUME_DISK_EXTENTS *extents = NULL;
87 DRIVE_LAYOUT_INFORMATION_EX *drive_info = NULL;
88 size_t drive_info_size;
91 wimlib_assert(path[0] != L'\0' && path[1] == L':');
93 *(wcschr(vol_name, L'X')) = path[0];
95 h = open_file(vol_name, GENERIC_READ);
96 if (h == INVALID_HANDLE_VALUE) {
97 win32_error(GetLastError(), L"\"%ls\": Can't open volume device",
99 ret = WIMLIB_ERR_OPEN;
103 if (!query_device(h, IOCTL_DISK_GET_PARTITION_INFO_EX,
104 part_info, sizeof(PARTITION_INFORMATION_EX)))
106 win32_error(GetLastError(),
107 L"\"%ls\": Can't get partition info", vol_name);
108 ret = WIMLIB_ERR_READ;
112 extents_size = sizeof(VOLUME_DISK_EXTENTS);
114 extents_size += 4 * sizeof(DISK_EXTENT);
115 extents = MALLOC(extents_size);
117 ret = WIMLIB_ERR_NOMEM;
121 if (query_device(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
122 extents, extents_size))
124 if (GetLastError() != ERROR_MORE_DATA) {
125 win32_error(GetLastError(),
126 L"\"%ls\": Can't get volume extent info",
128 ret = WIMLIB_ERR_READ;
135 h = INVALID_HANDLE_VALUE;
137 if (extents->NumberOfDiskExtents != 1) {
138 ERROR("\"%ls\": This volume has %"PRIu32" disk extents, "
139 "but this code is untested for more than 1",
140 vol_name, (u32)extents->NumberOfDiskExtents);
141 ret = WIMLIB_ERR_UNSUPPORTED;
145 wsprintf(wcschr(disk_name, L'X'), L"%"PRIu32,
146 extents->Extents[0].DiskNumber);
148 h = open_file(disk_name, GENERIC_READ);
149 if (h == INVALID_HANDLE_VALUE) {
150 win32_error(GetLastError(),
151 L"\"%ls\": Can't open disk device", disk_name);
152 ret = WIMLIB_ERR_OPEN;
156 drive_info_size = sizeof(DRIVE_LAYOUT_INFORMATION_EX);
158 drive_info_size += 4 * sizeof(PARTITION_INFORMATION_EX);
159 drive_info = MALLOC(drive_info_size);
161 ret = WIMLIB_ERR_NOMEM;
165 if (query_device(h, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
166 drive_info, drive_info_size))
168 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
169 win32_error(GetLastError(),
170 L"\"%ls\": Can't get disk info", disk_name);
171 ret = WIMLIB_ERR_READ;
177 *drive_info_ret = *drive_info; /* doesn't include partitions */
179 h = INVALID_HANDLE_VALUE;
181 if (drive_info->PartitionStyle != part_info->PartitionStyle) {
182 ERROR("\"%ls\", \"%ls\": Inconsistent partition table type!",
183 vol_name, disk_name);
184 ret = WIMLIB_ERR_UNSUPPORTED;
188 if (part_info->PartitionStyle == PARTITION_STYLE_GPT) {
189 BUILD_BUG_ON(sizeof(part_info->Gpt.PartitionId) !=
190 sizeof(drive_info->Gpt.DiskId));
191 if (!memcmp(&part_info->Gpt.PartitionId,
192 &drive_info->Gpt.DiskId,
193 sizeof(drive_info->Gpt.DiskId)))
195 ERROR("\"%ls\", \"%ls\": Partition GUID is the "
196 "same as the disk GUID???", vol_name, disk_name);
197 ret = WIMLIB_ERR_UNSUPPORTED;
202 if (part_info->PartitionStyle != PARTITION_STYLE_MBR &&
203 part_info->PartitionStyle != PARTITION_STYLE_GPT)
205 ERROR("\"%ls\": Unknown partition style 0x%08"PRIx32,
206 vol_name, (u32)part_info->PartitionStyle);
207 ret = WIMLIB_ERR_UNSUPPORTED;
215 if (h != INVALID_HANDLE_VALUE)
221 * Allocate a new WIM data source ID.
224 * Previous WimOverlay.dat contents, or NULL if file did not exist.
226 * Returns the new data source ID.
229 alloc_new_data_source_id(const struct WimOverlay_dat_header *old_hdr)
234 for (u64 id = 0; ; id++) {
235 for (u32 i = 0; i < old_hdr->num_entries_1; i++)
236 if (id == old_hdr->entry_1s[i].data_source_id)
245 * Calculate the size of WimOverlay.dat with one entry added.
248 * Previous WimOverlay.dat contents, or NULL if file did not exist.
250 * Size of entry_2 being added.
252 * Size will be returned here.
254 * Returns 0 on success, or WIMLIB_ERR_UNSUPPORTED if size overflows 32 bits.
257 calculate_wimoverlay_dat_size(const struct WimOverlay_dat_header *old_hdr,
258 u32 new_entry_2_size, u32 *size_ret)
263 size_64 = sizeof(struct WimOverlay_dat_header);
265 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
266 size_64 += sizeof(struct WimOverlay_dat_entry_1);
267 size_64 += old_hdr->entry_1s[i].entry_2_length;
270 size_64 += sizeof(struct WimOverlay_dat_entry_1);
271 size_64 += new_entry_2_size;
275 return WIMLIB_ERR_UNSUPPORTED;
282 * Writes @size bytes of @contents to the named file @path.
284 * Returns 0 on success; WIMLIB_ERR_OPEN or WIMLIB_ERR_WRITE on failure.
287 write_wimoverlay_dat(const wchar_t *path, const void *contents, u32 size)
292 h = CreateFile(path, GENERIC_WRITE, 0, NULL,
293 CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
294 if (h == INVALID_HANDLE_VALUE) {
295 win32_error(GetLastError(),
296 L"\"%ls\": Can't open file for writing", path);
297 return WIMLIB_ERR_OPEN;
301 if (!WriteFile(h, contents, size, &bytes_written, NULL) ||
302 bytes_written != size)
304 win32_error(GetLastError(),
305 L"\"%ls\": Can't write file", path);
307 return WIMLIB_ERR_WRITE;
310 if (!CloseHandle(h)) {
311 win32_error(GetLastError(),
312 L"\"%ls\": Can't close handle", path);
313 return WIMLIB_ERR_WRITE;
320 * Generates the contents of WimOverlay.dat in memory, with one entry added.
323 * Buffer large enough to hold the new contents.
325 * Old contents of WimOverlay.dat, or NULL if it did not exist.
327 * Absolute path to the WIM file. It must begin with a drive letter; for
328 * example, D:\install.wim.
330 * GUID of the WIM, from the WIM header.
332 * Number of the image in the WIM to specify in the new entry.
333 * @new_data_source_id
334 * Data source ID to use for the new entry.
336 * Partition information for the WIM file.
338 * Disk information for the WIM file.
340 * Size, in bytes, of the new location information structure ('struct
341 * WimOverlay_dat_entry_2').
343 * Returns a pointer one past the last byte of @buf filled in.
346 fill_in_wimoverlay_dat(u8 *buf,
347 const struct WimOverlay_dat_header *old_hdr,
348 const wchar_t *wim_path,
349 const u8 wim_guid[WIM_GUID_LEN],
351 u64 new_data_source_id,
352 const PARTITION_INFORMATION_EX *part_info,
353 const DRIVE_LAYOUT_INFORMATION_EX *disk_info,
354 u32 new_entry_2_size)
356 struct WimOverlay_dat_header *new_hdr;
357 struct WimOverlay_dat_entry_1 *new_entry_1;
358 struct WimOverlay_dat_entry_2 *new_entry_2;
362 new_hdr = (struct WimOverlay_dat_header *)p;
364 /* Fill in new header */
365 new_hdr->magic = WIMOVERLAY_DAT_MAGIC;
366 new_hdr->wim_provider_version = WIM_PROVIDER_CURRENT_VERSION;
367 new_hdr->unknown_0x08 = 0x00000028;
368 new_hdr->num_entries_1 = (old_hdr ? old_hdr->num_entries_1 : 0) + 1;
369 new_hdr->num_entries_2 = (old_hdr ? old_hdr->num_entries_2 : 0) + 1;
370 new_hdr->unknown_0x14 = 0x00000000;
372 p += sizeof(struct WimOverlay_dat_header);
374 /* Copy WIM-specific information for old entries */
375 entry_2_offset = sizeof(struct WimOverlay_dat_header) +
376 (new_hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1));
378 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
379 new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
381 p = mempcpy(p, &old_hdr->entry_1s[i],
382 sizeof(struct WimOverlay_dat_entry_1));
384 new_entry_1->entry_2_offset = entry_2_offset;
385 entry_2_offset += new_entry_1->entry_2_length;
389 /* Generate WIM-specific information for new entry */
390 new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
392 new_entry_1->data_source_id = new_data_source_id;
393 new_entry_1->entry_2_offset = entry_2_offset;
394 new_entry_1->entry_2_length = new_entry_2_size;
395 new_entry_1->wim_type = WIM_BOOT_NOT_OS_WIM;
396 new_entry_1->wim_index = image;
397 BUILD_BUG_ON(sizeof(new_entry_1->guid) != WIM_GUID_LEN);
398 memcpy(new_entry_1->guid, wim_guid, WIM_GUID_LEN);
400 p += sizeof(struct WimOverlay_dat_entry_1);
402 /* Copy WIM location information for old entries */
404 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
405 wimlib_assert(new_hdr->entry_1s[i].entry_2_offset == p - buf);
406 wimlib_assert(old_hdr->entry_1s[i].entry_2_length ==
407 new_hdr->entry_1s[i].entry_2_length);
409 ((const u8 *)old_hdr + old_hdr->entry_1s[i].entry_2_offset),
410 old_hdr->entry_1s[i].entry_2_length);
414 /* Generate WIM location information for new entry */
415 new_entry_2 = (struct WimOverlay_dat_entry_2 *)p;
417 new_entry_2->unknown_0x00 = 0x00000000;
418 new_entry_2->unknown_0x04 = 0x00000000;
419 new_entry_2->entry_2_length = new_entry_2_size;
420 new_entry_2->unknown_0x0C = 0x00000000;
421 new_entry_2->unknown_0x10 = 0x00000005;
422 new_entry_2->unknown_0x14 = 0x00000001;
423 new_entry_2->inner_struct_size = new_entry_2_size - 0x14;
424 new_entry_2->unknown_0x1C = 0x00000005;
425 new_entry_2->unknown_0x20 = 0x00000006;
426 new_entry_2->unknown_0x24 = 0x00000000;
427 new_entry_2->unknown_0x28 = 0x00000048;
428 new_entry_2->unknown_0x2C = 0x00000000;
429 new_entry_2->unknown_0x40 = 0x00000000;
431 if (part_info->PartitionStyle == PARTITION_STYLE_MBR) {
432 new_entry_2->partition.mbr.part_start_offset = part_info->StartingOffset.QuadPart;
433 new_entry_2->partition.mbr.padding = 0;
434 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_MBR;
435 new_entry_2->disk.mbr.disk_id = disk_info->Mbr.Signature;
436 new_entry_2->disk.mbr.padding[0] = 0x00000000;
437 new_entry_2->disk.mbr.padding[1] = 0x00000000;
438 new_entry_2->disk.mbr.padding[2] = 0x00000000;
440 BUILD_BUG_ON(sizeof(new_entry_2->partition.gpt.part_unique_guid) !=
441 sizeof(part_info->Gpt.PartitionId));
442 memcpy(new_entry_2->partition.gpt.part_unique_guid,
443 &part_info->Gpt.PartitionId,
444 sizeof(part_info->Gpt.PartitionId));
445 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_GPT;
447 BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
448 sizeof(disk_info->Gpt.DiskId));
449 memcpy(new_entry_2->disk.gpt.disk_guid,
450 &disk_info->Gpt.DiskId,
451 sizeof(disk_info->Gpt.DiskId));
453 BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
454 sizeof(new_entry_2->partition.gpt.part_unique_guid));
456 new_entry_2->unknown_0x58[0] = 0x00000000;
457 new_entry_2->unknown_0x58[1] = 0x00000000;
458 new_entry_2->unknown_0x58[2] = 0x00000000;
459 new_entry_2->unknown_0x58[3] = 0x00000000;
461 wimlib_assert(wim_path[2] == L'\\');
462 return mempcpy(new_entry_2->wim_file_name,
464 new_entry_2_size - sizeof(struct WimOverlay_dat_entry_2));
468 * Prepares the new contents of WimOverlay.dat in memory, with one entry added.
471 * Old contents of WimOverlay.dat, or NULL if it did not exist.
473 * Absolute path to the WIM file. It must begin with a drive letter; for
474 * example, D:\install.wim.
476 * GUID of the WIM, from the WIM header.
478 * Number of the image in the WIM to specify in the new entry.
480 * Location into which to return the new contents as a malloc()ed buffer on
482 * @new_contents_size_ret
483 * Location into which to return the size, in bytes, of the new contents on
485 * @new_data_source_id_ret
486 * Location into which to return the data source ID of the new entry on
489 * Returns 0 on success, or a positive error code on failure.
492 prepare_wimoverlay_dat(const struct WimOverlay_dat_header *old_hdr,
493 const wchar_t *wim_path,
494 const u8 wim_guid[WIM_GUID_LEN],
496 void **new_contents_ret,
497 u32 *new_contents_size_ret,
498 u64 *new_data_source_id_ret)
501 PARTITION_INFORMATION_EX part_info;
502 DRIVE_LAYOUT_INFORMATION_EX disk_info;
503 u64 new_data_source_id;
504 u32 new_entry_2_size;
505 u32 new_contents_size;
509 ret = query_partition_and_disk_info(wim_path, &part_info, &disk_info);
513 new_data_source_id = alloc_new_data_source_id(old_hdr);
515 new_entry_2_size = sizeof(struct WimOverlay_dat_entry_2) +
516 ((wcslen(wim_path) - 2 + 1) * sizeof(wchar_t));
517 ret = calculate_wimoverlay_dat_size(old_hdr, new_entry_2_size,
522 buf = MALLOC(new_contents_size);
524 return WIMLIB_ERR_NOMEM;
526 end = fill_in_wimoverlay_dat(buf, old_hdr, wim_path, wim_guid, image,
528 &part_info, &disk_info, new_entry_2_size);
530 wimlib_assert(end - buf == new_contents_size);
532 *new_contents_ret = buf;
533 *new_contents_size_ret = new_contents_size;
534 *new_data_source_id_ret = new_data_source_id;
539 * Reads and validates a WimOverlay.dat file.
542 * Path to the WimOverlay.dat file, such as
543 * C:\System Volume Information\WimOverlay.dat
545 * Location into which to return the contents as a malloc()ed buffer on
546 * success. This can be cast to 'struct WimOverlay_dat_header', and its
547 * contents are guaranteed to be valid. Alternatively, if the file does
548 * not exist, NULL will be returned here.
550 * Returns 0 on success, or a positive error code on failure.
553 read_wimoverlay_dat(const wchar_t *path, void **contents_ret)
556 BY_HANDLE_FILE_INFORMATION info;
559 const struct WimOverlay_dat_header *hdr;
561 bool already_retried = false;
564 h = open_file(path, GENERIC_READ);
565 if (h == INVALID_HANDLE_VALUE) {
566 DWORD err = GetLastError();
567 if (err == ERROR_FILE_NOT_FOUND) {
568 *contents_ret = NULL;
571 if (err == ERROR_PATH_NOT_FOUND &&
572 func_RtlCreateSystemVolumeInformationFolder)
574 wchar_t volume_root_path[] = L"\\??\\X:\\";
576 *(wcschr(volume_root_path, L'X')) = path[0];
578 UNICODE_STRING str = {
579 .Length = sizeof(volume_root_path) - sizeof(wchar_t),
580 .MaximumLength = sizeof(volume_root_path),
581 .Buffer = volume_root_path,
586 status = (*func_RtlCreateSystemVolumeInformationFolder)(&str);
588 err2 = (*func_RtlNtStatusToDosError)(status);
589 if (err2 == ERROR_SUCCESS) {
590 if (!already_retried) {
591 already_retried = true;
598 win32_error(err, L"\"%ls\": Can't open for reading", path);
599 return WIMLIB_ERR_OPEN;
601 if (!GetFileInformationByHandle(h, &info)) {
602 win32_error(GetLastError(), L"\"%ls\": Can't query metadata", path);
604 return WIMLIB_ERR_STAT;
608 if (!info.nFileSizeHigh)
609 contents = MALLOC(info.nFileSizeLow);
611 ERROR("\"%ls\": File is too large to fit into memory", path);
613 return WIMLIB_ERR_NOMEM;
617 if (!ReadFile(h, contents, info.nFileSizeLow, &bytes_read, NULL) ||
618 bytes_read != info.nFileSizeLow)
620 win32_error(GetLastError(), L"\"%ls\": Can't read data", path);
622 ret = WIMLIB_ERR_READ;
623 goto out_free_contents;
628 if (info.nFileSizeLow < sizeof(struct WimOverlay_dat_header)) {
629 ERROR("\"%ls\": File is unexpectedly small (only %"PRIu32" bytes)",
630 path, (u32)info.nFileSizeLow);
631 ret = WIMLIB_ERR_UNSUPPORTED;
632 goto out_free_contents;
635 hdr = (const struct WimOverlay_dat_header *)contents;
637 if (hdr->magic != WIMOVERLAY_DAT_MAGIC ||
638 hdr->wim_provider_version != WIM_PROVIDER_CURRENT_VERSION ||
639 hdr->unknown_0x08 != 0x00000028 ||
640 (hdr->num_entries_1 != hdr->num_entries_2) ||
641 hdr->unknown_0x14 != 0x00000000)
643 ERROR("\"%ls\": Header contains unexpected data:", path);
644 if (wimlib_print_errors) {
645 print_byte_field((const u8 *)hdr,
646 sizeof(struct WimOverlay_dat_header),
648 fputc('\n', wimlib_error_file);
650 ret = WIMLIB_ERR_UNSUPPORTED;
651 goto out_free_contents;
654 if ((u64)hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1) >
655 info.nFileSizeLow - sizeof(struct WimOverlay_dat_header))
657 ERROR("\"%ls\": File is unexpectedly small "
658 "(only %"PRIu32" bytes, but has %"PRIu32" entries)",
659 path, (u32)info.nFileSizeLow, hdr->num_entries_1);
660 ret = WIMLIB_ERR_UNSUPPORTED;
661 goto out_free_contents;
664 for (u32 i = 0; i < hdr->num_entries_1; i++) {
665 const struct WimOverlay_dat_entry_1 *entry_1;
666 const struct WimOverlay_dat_entry_2 *entry_2;
667 u32 wim_file_name_length;
669 entry_1 = &hdr->entry_1s[i];
671 if (((u64)entry_1->entry_2_offset +
672 (u64)entry_1->entry_2_length) >
675 ERROR("\"%ls\": entry %"PRIu32" (2) "
676 "(data source ID 0x%016"PRIx64") "
678 path, i, entry_1->data_source_id);
679 ret = WIMLIB_ERR_UNSUPPORTED;
680 goto out_free_contents;
682 if (entry_1->entry_2_length < sizeof(struct WimOverlay_dat_entry_2)) {
683 ERROR("\"%ls\": entry %"PRIu32" (2) "
684 "(data source ID 0x%016"PRIx64") "
686 path, i, entry_1->data_source_id);
687 ret = WIMLIB_ERR_UNSUPPORTED;
688 goto out_free_contents;
691 if (entry_1->entry_2_offset % 2 != 0) {
692 ERROR("\"%ls\": entry %"PRIu32" (2) "
693 "(data source ID 0x%016"PRIx64") "
695 path, i, entry_1->data_source_id);
696 ret = WIMLIB_ERR_UNSUPPORTED;
697 goto out_free_contents;
700 entry_2 = (const struct WimOverlay_dat_entry_2 *)
701 ((const u8 *)hdr + entry_1->entry_2_offset);
703 wim_file_name_length = entry_1->entry_2_length -
704 sizeof(struct WimOverlay_dat_entry_2);
705 if ((wim_file_name_length < 2 * sizeof(wchar_t)) ||
706 (wim_file_name_length % sizeof(wchar_t) != 0) ||
707 (wmemchr(entry_2->wim_file_name, L'\0',
708 wim_file_name_length / sizeof(wchar_t))
709 != &entry_2->wim_file_name[wim_file_name_length /
710 sizeof(wchar_t) - 1]))
712 ERROR("\"%ls\": entry %"PRIu32" (2) "
713 "(data source ID 0x%016"PRIx64") "
714 "has invalid WIM file name",
715 path, i, entry_1->data_source_id);
716 if (wimlib_print_errors) {
717 print_byte_field((const u8 *)entry_2->wim_file_name,
718 wim_file_name_length,
720 fputc('\n', wimlib_error_file);
722 ret = WIMLIB_ERR_UNSUPPORTED;
723 goto out_free_contents;
726 if (entry_2->unknown_0x00 != 0x00000000 ||
727 entry_2->unknown_0x04 != 0x00000000 ||
728 entry_2->unknown_0x0C != 0x00000000 ||
729 entry_2->entry_2_length != entry_1->entry_2_length ||
730 entry_2->unknown_0x10 != 0x00000005 ||
731 entry_2->unknown_0x14 != 0x00000001 ||
732 entry_2->inner_struct_size != entry_1->entry_2_length - 0x14 ||
733 entry_2->unknown_0x1C != 0x00000005 ||
734 entry_2->unknown_0x20 != 0x00000006 ||
735 entry_2->unknown_0x24 != 0x00000000 ||
736 entry_2->unknown_0x28 != 0x00000048 ||
737 entry_2->unknown_0x2C != 0x00000000 ||
738 entry_2->unknown_0x40 != 0x00000000 ||
739 (entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_GPT &&
740 entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_MBR) ||
741 (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_MBR &&
742 entry_2->partition.mbr.padding != 0) ||
743 (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_GPT &&
744 entry_2->partition.mbr.padding == 0) ||
745 entry_2->unknown_0x58[0] != 0x00000000 ||
746 entry_2->unknown_0x58[1] != 0x00000000 ||
747 entry_2->unknown_0x58[2] != 0x00000000 ||
748 entry_2->unknown_0x58[3] != 0x00000000)
750 ERROR("\"%ls\": entry %"PRIu32" (2) "
751 "(data source ID 0x%016"PRIx64") "
752 "contains unexpected data!",
753 path, i, entry_1->data_source_id);
754 if (wimlib_print_errors) {
755 print_byte_field((const u8 *)entry_2,
756 entry_1->entry_2_length,
758 fputc('\n', wimlib_error_file);
760 ret = WIMLIB_ERR_UNSUPPORTED;
761 goto out_free_contents;
765 *contents_ret = contents;
774 * Update WimOverlay.dat manually in order to add a WIM data source to the
777 * THIS CODE IS EXPERIMENTAL AS I HAD TO REVERSE ENGINEER THE FILE FORMAT!
780 * Target drive. Must be a letter followed by a colon (e.g. D:).
782 * Absolute path to the WIM file. It must begin with a drive letter; for
783 * example, D:\install.wim.
785 * GUID of the WIM, from the WIM header.
787 * Number of the image in the WIM to specify in the new entry.
788 * @data_source_id_ret
789 * On success, the allocated data source ID is returned here.
792 update_wimoverlay_manually(const wchar_t *drive, const wchar_t *wim_path,
793 const u8 wim_guid[WIM_GUID_LEN],
794 int image, u64 *data_source_id_ret)
796 wchar_t path_main[] = L"A:\\System Volume Information\\WimOverlay.dat";
797 wchar_t path_backup[] = L"A:\\System Volume Information\\WimOverlay.backup";
798 wchar_t path_wimlib_backup[] = L"A:\\System Volume Information\\WimOverlay.wimlib_backup";
799 wchar_t path_new[] = L"A:\\System Volume Information\\WimOverlay.wimlib_new";
802 u32 new_contents_size;
803 u64 new_data_source_id;
806 wimlib_assert(drive[0] != L'\0' &&
810 path_main[0] = drive[0];
811 path_backup[0] = drive[0];
812 path_wimlib_backup[0] = drive[0];
813 path_new[0] = drive[0];
815 ret = read_wimoverlay_dat(path_main, &old_contents);
819 ret = prepare_wimoverlay_dat(old_contents, wim_path, wim_guid, image,
820 &new_contents, &new_contents_size,
821 &new_data_source_id);
826 /* Write WimOverlay.wimlib_new */
827 ret = write_wimoverlay_dat(path_new,
828 new_contents, new_contents_size);
830 goto out_free_new_contents;
832 /* Write WimOverlay.backup */
833 ret = write_wimoverlay_dat(path_backup,
834 new_contents, new_contents_size);
836 goto out_free_new_contents;
839 /* Rename WimOverlay.dat => WimOverlay.wimlib_backup */
840 ret = win32_rename_replacement(path_main, path_wimlib_backup);
842 ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
843 path_main, path_wimlib_backup);
844 ret = WIMLIB_ERR_RENAME;
845 goto out_free_new_contents;
849 /* Rename WimOverlay.wimlib_new => WimOverlay.dat */
850 ret = win32_rename_replacement(path_new, path_main);
852 ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
853 path_new, path_main);
854 ret = WIMLIB_ERR_RENAME;
856 out_free_new_contents:
859 if (ret == WIMLIB_ERR_UNSUPPORTED) {
860 ERROR("Please report to developer ("PACKAGE_BUGREPORT").\n"
861 " If possible send the file \"%ls\".\n\n", path_main);
864 *data_source_id_ret = new_data_source_id;
868 /* Try to attach an instance of the Windows Overlay File System Filter Driver to
869 * the specified drive (such as C:) */
871 try_to_attach_wof(const wchar_t *drive)
876 /* Use FilterAttach() from Fltlib.dll. */
878 fltlib = LoadLibrary(L"Fltlib.dll");
881 WARNING("Failed to load Fltlib.dll");
885 HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName,
886 LPCWSTR lpVolumeName,
887 LPCWSTR lpInstanceName,
888 DWORD dwCreatedInstanceNameLength,
889 LPWSTR lpCreatedInstanceName);
891 func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach");
893 if (func_FilterAttach) {
896 res = (*func_FilterAttach)(L"WoF", drive, NULL, 0, NULL);
901 WARNING("FilterAttach() does not exist in Fltlib.dll");
910 * Allocate a WOF data source ID for a WIM file.
913 * Absolute path to the WIM file. This must include a drive letter and use
914 * backslash path separators.
916 * GUID of the WIM, from the WIM header.
918 * Number of the image in the WIM being applied.
920 * Path to the target directory.
921 * @data_source_id_ret
922 * On success, an identifier for the backing WIM file will be returned
925 * Returns 0 on success, or a positive error code on failure.
928 wimboot_alloc_data_source_id(const wchar_t *wim_path,
929 const u8 wim_guid[WIM_GUID_LEN],
930 int image, const wchar_t *target,
931 u64 *data_source_id_ret, bool *wof_running_ret)
934 size_t wim_path_nchars;
935 size_t wim_file_name_length;
938 struct wof_external_info *wof_info;
939 struct wim_provider_add_overlay_input *wim_info;
942 DWORD bytes_returned;
944 const wchar_t *prefix = L"\\??\\";
945 const size_t prefix_nchars = 4;
946 bool tried_to_attach_wof = false;
948 ret = win32_get_drive_path(target, drive_path);
952 wimlib_assert(!wcschr(wim_path, L'/'));
953 wimlib_assert(wim_path[0] != L'\0' && wim_path[1] == L':');
955 wim_path_nchars = wcslen(wim_path);
956 wim_file_name_length = sizeof(wchar_t) *
957 (wim_path_nchars + prefix_nchars);
959 insize = sizeof(struct wof_external_info) +
960 sizeof(struct wim_provider_add_overlay_input) +
961 wim_file_name_length;
965 ret = WIMLIB_ERR_NOMEM;
969 wof_info = (struct wof_external_info *)in;
970 wof_info->version = WOF_CURRENT_VERSION;
971 wof_info->provider = WOF_PROVIDER_WIM;
973 wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1);
974 wim_info->wim_type = WIM_BOOT_NOT_OS_WIM;
975 wim_info->wim_index = image;
976 wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input,
978 wim_info->wim_file_name_length = wim_file_name_length;
979 wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars);
980 wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
983 h = open_file(drive_path, GENERIC_WRITE);
985 if (h == INVALID_HANDLE_VALUE) {
986 win32_error(GetLastError(),
987 L"Failed to open \"%ls\"", drive_path + 4);
988 ret = WIMLIB_ERR_OPEN;
992 if (!DeviceIoControl(h, FSCTL_ADD_OVERLAY,
994 &data_source_id, sizeof(data_source_id),
995 &bytes_returned, NULL))
997 DWORD err = GetLastError();
998 if (err == ERROR_INVALID_FUNCTION) {
999 if (!tried_to_attach_wof) {
1001 h = INVALID_HANDLE_VALUE;
1002 tried_to_attach_wof = true;
1003 if (try_to_attach_wof(drive_path + 4))
1006 ret = WIMLIB_ERR_UNSUPPORTED;
1007 goto out_close_handle;
1009 win32_error(err, L"Failed to add overlay source \"%ls\" "
1010 "to volume \"%ls\"", wim_path, drive_path + 4);
1011 ret = WIMLIB_ERR_WIMBOOT;
1012 goto out_close_handle;
1016 if (bytes_returned != sizeof(data_source_id)) {
1017 ret = WIMLIB_ERR_WIMBOOT;
1018 ERROR("Unexpected result size when adding "
1019 "overlay source \"%ls\" to volume \"%ls\"",
1020 wim_path, drive_path + 4);
1021 goto out_close_handle;
1024 *wof_running_ret = true;
1025 *data_source_id_ret = data_source_id;
1033 if (ret == WIMLIB_ERR_UNSUPPORTED) {
1036 "The version of Windows you are running does not appear to support\n"
1037 " the Windows Overlay File System Filter Driver. This is normally\n"
1038 " available on Windows 8.1 Update 1 or later. Therefore, wimlib\n"
1039 " will attempt to update the WimOverlay.dat file directly.\n");
1041 WARNING("WOF driver is not available; updating WimOverlay.dat directly.");
1043 ret = update_wimoverlay_manually(drive_path + 4, wim_path,
1045 data_source_id_ret);
1046 *wof_running_ret = false;
1053 * Set WIMBoot information on the specified file.
1055 * This turns it into a reparse point that redirects accesses to it, to the
1056 * corresponding resource in the WIM archive.
1059 * Open handle to the file, with GENERIC_WRITE access.
1061 * Unnamed data stream of the file.
1063 * Allocated identifier for the WIM data source on the destination volume.
1064 * @lookup_table_hash
1065 * SHA-1 message digest of the WIM's lookup table.
1067 * %true if the WOF driver appears to be available and working; %false if
1070 * Returns %true on success, or %false on failure with GetLastError() set.
1073 wimboot_set_pointer(HANDLE h,
1074 const struct wim_lookup_table_entry *lte,
1076 const u8 lookup_table_hash[SHA1_HASH_SIZE],
1079 DWORD bytes_returned;
1082 /* The WOF driver is running. We can create the reparse point
1083 * using FSCTL_SET_EXTERNAL_BACKING. */
1084 unsigned int max_retries = 4;
1086 struct wof_external_info wof_info;
1087 struct wim_provider_external_info wim_info;
1091 memset(&in, 0, sizeof(in));
1093 in.wof_info.version = WOF_CURRENT_VERSION;
1094 in.wof_info.provider = WOF_PROVIDER_WIM;
1096 in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
1097 in.wim_info.flags = 0;
1098 in.wim_info.data_source_id = data_source_id;
1099 copy_hash(in.wim_info.resource_hash, lte->hash);
1101 /* lookup_table_hash is not necessary */
1103 if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
1104 &in, sizeof(in), NULL, 0,
1105 &bytes_returned, NULL))
1107 /* Try to track down sporadic errors */
1108 if (wimlib_print_errors) {
1109 WARNING("FSCTL_SET_EXTERNAL_BACKING failed (err=%u); data was %zu bytes:",
1110 (u32)GetLastError(), sizeof(in));
1111 print_byte_field((const u8 *)&in, sizeof(in), wimlib_error_file);
1112 putc('\n', wimlib_error_file);
1114 if (--max_retries) {
1115 WARNING("Retrying after 100ms...");
1119 WARNING("Too many retries; returning failure");
1124 /* The WOF driver is running. We need to create the reparse
1125 * point manually. */
1133 struct wof_external_info wof_info;
1134 struct wim_provider_rpdata wim_info;
1137 BUILD_BUG_ON(sizeof(in) != 8 +
1138 sizeof(struct wof_external_info) +
1139 sizeof(struct wim_provider_rpdata));
1141 in.hdr.rptag = WIMLIB_REPARSE_TAG_WOF;
1142 in.hdr.rpdatalen = sizeof(in) - sizeof(in.hdr);
1143 in.hdr.rpreserved = 0;
1145 in.wof_info.version = WOF_CURRENT_VERSION;
1146 in.wof_info.provider = WOF_PROVIDER_WIM;
1148 in.wim_info.version = 2;
1149 in.wim_info.flags = 0;
1150 in.wim_info.data_source_id = data_source_id;
1151 copy_hash(in.wim_info.resource_hash, lte->hash);
1152 copy_hash(in.wim_info.wim_lookup_table_hash, lookup_table_hash);
1153 in.wim_info.stream_uncompressed_size = lte->size;
1154 in.wim_info.stream_compressed_size = lte->rspec->size_in_wim;
1155 in.wim_info.stream_offset_in_wim = lte->rspec->offset_in_wim;
1157 if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
1158 &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
1161 /* We also need to create an unnamed data stream of the correct
1162 * size. Otherwise the file shows up as zero length. It can be
1163 * a sparse stream containing all zeroes; its contents
1164 * are unimportant. */
1165 if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
1166 &bytes_returned, NULL))
1169 if (!SetFilePointerEx(h,
1170 (LARGE_INTEGER){ .QuadPart = lte->size},
1174 if (!SetEndOfFile(h))
1181 #endif /* __WIN32__ */