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 Filesystem filter (WOF). See wof.h for more info.
14 * Copyright (C) 2014-2016 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/blob_table.h"
40 #include "wimlib/inode.h"
41 #include "wimlib/error.h"
42 #include "wimlib/util.h"
43 #include "wimlib/wimboot.h"
44 #include "wimlib/win32.h"
45 #include "wimlib/wof.h"
48 open_file(const wchar_t *device_name, DWORD desiredAccess)
50 return CreateFile(device_name, desiredAccess,
51 FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
52 FILE_FLAG_BACKUP_SEMANTICS, NULL);
56 query_device(HANDLE h, DWORD code, void *out, DWORD out_size)
59 return DeviceIoControl(h, code, NULL, 0, out, out_size,
60 &bytes_returned, NULL);
64 * Gets partition and drive information for the specified path.
67 * Absolute path which must begin with a drive letter. For example, if the
68 * path is D:\install.wim, this function will query information about the
71 * Partition info is returned here.
73 * Drive info is returned here. The contained partition info will not be
76 * Returns 0 on success, or a positive error code on failure.
79 query_partition_and_disk_info(const wchar_t *path,
80 PARTITION_INFORMATION_EX *part_info,
81 DRIVE_LAYOUT_INFORMATION_EX *drive_info_ret)
83 wchar_t vol_name[] = L"\\\\.\\X:";
84 wchar_t disk_name[] = L"\\\\?\\PhysicalDriveXXXXXXXXXX";
85 HANDLE h = INVALID_HANDLE_VALUE;
86 VOLUME_DISK_EXTENTS *extents = NULL;
88 DRIVE_LAYOUT_INFORMATION_EX *drive_info = NULL;
89 size_t drive_info_size;
92 wimlib_assert(path[0] != L'\0' && path[1] == L':');
94 *(wcschr(vol_name, L'X')) = path[0];
96 h = open_file(vol_name, GENERIC_READ);
97 if (h == INVALID_HANDLE_VALUE) {
98 win32_error(GetLastError(), L"\"%ls\": Can't open volume device",
100 ret = WIMLIB_ERR_OPEN;
104 if (!query_device(h, IOCTL_DISK_GET_PARTITION_INFO_EX,
105 part_info, sizeof(PARTITION_INFORMATION_EX)))
107 win32_error(GetLastError(),
108 L"\"%ls\": Can't get partition info", vol_name);
109 ret = WIMLIB_ERR_READ;
113 extents_size = sizeof(VOLUME_DISK_EXTENTS);
115 extents_size += 4 * sizeof(DISK_EXTENT);
116 extents = MALLOC(extents_size);
118 ret = WIMLIB_ERR_NOMEM;
122 if (query_device(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
123 extents, extents_size))
125 if (GetLastError() != ERROR_MORE_DATA) {
126 win32_error(GetLastError(),
127 L"\"%ls\": Can't get volume extent info",
129 ret = WIMLIB_ERR_READ;
136 h = INVALID_HANDLE_VALUE;
138 if (extents->NumberOfDiskExtents != 1) {
139 ERROR("\"%ls\": This volume has %"PRIu32" disk extents, "
140 "but this code is untested for more than 1",
141 vol_name, (u32)extents->NumberOfDiskExtents);
142 ret = WIMLIB_ERR_UNSUPPORTED;
146 wsprintf(wcschr(disk_name, L'X'), L"%"PRIu32,
147 extents->Extents[0].DiskNumber);
149 h = open_file(disk_name, GENERIC_READ);
150 if (h == INVALID_HANDLE_VALUE) {
151 win32_error(GetLastError(),
152 L"\"%ls\": Can't open disk device", disk_name);
153 ret = WIMLIB_ERR_OPEN;
157 drive_info_size = sizeof(DRIVE_LAYOUT_INFORMATION_EX);
159 drive_info_size += 4 * sizeof(PARTITION_INFORMATION_EX);
160 drive_info = MALLOC(drive_info_size);
162 ret = WIMLIB_ERR_NOMEM;
166 if (query_device(h, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
167 drive_info, drive_info_size))
169 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
170 win32_error(GetLastError(),
171 L"\"%ls\": Can't get disk info", disk_name);
172 ret = WIMLIB_ERR_READ;
178 *drive_info_ret = *drive_info; /* doesn't include partitions */
180 h = INVALID_HANDLE_VALUE;
182 if (drive_info->PartitionStyle != part_info->PartitionStyle) {
183 ERROR("\"%ls\", \"%ls\": Inconsistent partition table type!",
184 vol_name, disk_name);
185 ret = WIMLIB_ERR_UNSUPPORTED;
189 if (part_info->PartitionStyle == PARTITION_STYLE_GPT) {
190 STATIC_ASSERT(sizeof(part_info->Gpt.PartitionId) ==
191 sizeof(drive_info->Gpt.DiskId));
192 if (!memcmp(&part_info->Gpt.PartitionId,
193 &drive_info->Gpt.DiskId,
194 sizeof(drive_info->Gpt.DiskId)))
196 ERROR("\"%ls\", \"%ls\": Partition GUID is the "
197 "same as the disk GUID???", vol_name, disk_name);
198 ret = WIMLIB_ERR_UNSUPPORTED;
203 if (part_info->PartitionStyle != PARTITION_STYLE_MBR &&
204 part_info->PartitionStyle != PARTITION_STYLE_GPT)
206 ERROR("\"%ls\": Unknown partition style 0x%08"PRIx32,
207 vol_name, (u32)part_info->PartitionStyle);
208 ret = WIMLIB_ERR_UNSUPPORTED;
216 if (h != INVALID_HANDLE_VALUE)
222 * Calculate the size of WimOverlay.dat with one entry added.
225 * Previous WimOverlay.dat contents, or NULL if file did not exist.
227 * Size of entry_2 being added.
229 * Size will be returned here.
231 * Returns 0 on success, or WIMLIB_ERR_UNSUPPORTED if size overflows 32 bits.
234 calculate_wimoverlay_dat_size(const struct WimOverlay_dat_header *old_hdr,
235 u32 new_entry_2_size, u32 *size_ret)
240 size_64 = sizeof(struct WimOverlay_dat_header);
242 for (u32 i = 0; i < old_hdr->num_entries; i++) {
243 size_64 += sizeof(struct WimOverlay_dat_entry_1);
244 size_64 += old_hdr->entry_1s[i].entry_2_length;
247 size_64 += sizeof(struct WimOverlay_dat_entry_1);
248 size_64 += new_entry_2_size;
252 return WIMLIB_ERR_UNSUPPORTED;
259 * Writes @size bytes of @contents to the named file @path.
261 * Returns 0 on success; WIMLIB_ERR_OPEN or WIMLIB_ERR_WRITE on failure.
264 write_wimoverlay_dat(const wchar_t *path, const void *contents, u32 size)
269 h = CreateFile(path, GENERIC_WRITE, 0, NULL,
270 CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
271 if (h == INVALID_HANDLE_VALUE) {
272 win32_error(GetLastError(),
273 L"\"%ls\": Can't open file for writing", path);
274 return WIMLIB_ERR_OPEN;
278 if (!WriteFile(h, contents, size, &bytes_written, NULL) ||
279 bytes_written != size)
281 win32_error(GetLastError(),
282 L"\"%ls\": Can't write file", path);
284 return WIMLIB_ERR_WRITE;
287 if (!CloseHandle(h)) {
288 win32_error(GetLastError(),
289 L"\"%ls\": Can't close handle", path);
290 return WIMLIB_ERR_WRITE;
297 * Generates the contents of WimOverlay.dat in memory, with one entry added.
300 * Buffer large enough to hold the new contents.
302 * Old contents of WimOverlay.dat, or NULL if it did not exist.
304 * Absolute path to the WIM file. It must begin with a drive letter; for
305 * example, D:\install.wim.
307 * GUID of the WIM, from the WIM header.
309 * Number of the image in the WIM to specify in the new entry.
310 * @new_data_source_id
311 * Data source ID to use for the new entry.
313 * Partition information for the WIM file.
315 * Disk information for the WIM file.
317 * Size, in bytes, of the new location information structure ('struct
318 * WimOverlay_dat_entry_2').
320 * Returns a pointer one past the last byte of @buf filled in.
323 fill_in_wimoverlay_dat(u8 *buf,
324 const struct WimOverlay_dat_header *old_hdr,
325 const wchar_t *wim_path,
326 const u8 wim_guid[GUID_SIZE],
328 u64 new_data_source_id,
329 const PARTITION_INFORMATION_EX *part_info,
330 const DRIVE_LAYOUT_INFORMATION_EX *disk_info,
331 u32 new_entry_2_size)
333 struct WimOverlay_dat_header *new_hdr;
334 struct WimOverlay_dat_entry_1 *new_entry_1;
335 struct WimOverlay_dat_entry_2 *new_entry_2;
339 new_hdr = (struct WimOverlay_dat_header *)p;
341 /* Fill in new header */
342 new_hdr->magic = WIMOVERLAY_DAT_MAGIC;
343 new_hdr->wim_provider_version = WIM_PROVIDER_CURRENT_VERSION;
344 new_hdr->unknown_0x08 = 0x00000028;
345 new_hdr->num_entries = (old_hdr ? old_hdr->num_entries : 0) + 1;
346 new_hdr->next_data_source_id = (old_hdr ? old_hdr->next_data_source_id : 0) + 1;
348 p += sizeof(struct WimOverlay_dat_header);
350 /* Copy WIM-specific information for old entries */
351 entry_2_offset = sizeof(struct WimOverlay_dat_header) +
352 (new_hdr->num_entries * sizeof(struct WimOverlay_dat_entry_1));
354 for (u32 i = 0; i < old_hdr->num_entries; i++) {
355 new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
357 p = mempcpy(p, &old_hdr->entry_1s[i],
358 sizeof(struct WimOverlay_dat_entry_1));
360 new_entry_1->entry_2_offset = entry_2_offset;
361 entry_2_offset += new_entry_1->entry_2_length;
365 /* Generate WIM-specific information for new entry */
366 new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
368 new_entry_1->data_source_id = new_data_source_id;
369 new_entry_1->entry_2_offset = entry_2_offset;
370 new_entry_1->entry_2_length = new_entry_2_size;
371 new_entry_1->wim_type = WIM_BOOT_NOT_OS_WIM;
372 new_entry_1->wim_index = image;
373 STATIC_ASSERT(sizeof(new_entry_1->guid) == GUID_SIZE);
374 copy_guid(new_entry_1->guid, wim_guid);
376 p += sizeof(struct WimOverlay_dat_entry_1);
378 /* Copy WIM location information for old entries */
380 for (u32 i = 0; i < old_hdr->num_entries; i++) {
381 wimlib_assert(new_hdr->entry_1s[i].entry_2_offset == p - buf);
382 wimlib_assert(old_hdr->entry_1s[i].entry_2_length ==
383 new_hdr->entry_1s[i].entry_2_length);
385 ((const u8 *)old_hdr + old_hdr->entry_1s[i].entry_2_offset),
386 old_hdr->entry_1s[i].entry_2_length);
390 /* Generate WIM location information for new entry */
391 new_entry_2 = (struct WimOverlay_dat_entry_2 *)p;
393 new_entry_2->unknown_0x00 = 0x00000000;
394 new_entry_2->unknown_0x04 = 0x00000000;
395 new_entry_2->entry_2_length = new_entry_2_size;
396 new_entry_2->unknown_0x0C = 0x00000000;
397 new_entry_2->unknown_0x10 = 0x00000005;
398 new_entry_2->unknown_0x14 = 0x00000001;
399 new_entry_2->inner_struct_size = new_entry_2_size - 0x14;
400 new_entry_2->unknown_0x1C = 0x00000005;
401 new_entry_2->unknown_0x20 = 0x00000006;
402 new_entry_2->unknown_0x24 = 0x00000000;
403 new_entry_2->unknown_0x28 = 0x00000048;
404 new_entry_2->unknown_0x2C = 0x00000000;
405 new_entry_2->unknown_0x40 = 0x00000000;
407 if (part_info->PartitionStyle == PARTITION_STYLE_MBR) {
408 new_entry_2->partition.mbr.part_start_offset = part_info->StartingOffset.QuadPart;
409 new_entry_2->partition.mbr.padding = 0;
410 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_MBR;
411 new_entry_2->disk.mbr.disk_id = disk_info->Mbr.Signature;
412 new_entry_2->disk.mbr.padding[0] = 0x00000000;
413 new_entry_2->disk.mbr.padding[1] = 0x00000000;
414 new_entry_2->disk.mbr.padding[2] = 0x00000000;
416 STATIC_ASSERT(sizeof(new_entry_2->partition.gpt.part_unique_guid) ==
417 sizeof(part_info->Gpt.PartitionId));
418 memcpy(new_entry_2->partition.gpt.part_unique_guid,
419 &part_info->Gpt.PartitionId,
420 sizeof(part_info->Gpt.PartitionId));
421 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_GPT;
423 STATIC_ASSERT(sizeof(new_entry_2->disk.gpt.disk_guid) ==
424 sizeof(disk_info->Gpt.DiskId));
425 memcpy(new_entry_2->disk.gpt.disk_guid,
426 &disk_info->Gpt.DiskId,
427 sizeof(disk_info->Gpt.DiskId));
429 STATIC_ASSERT(sizeof(new_entry_2->disk.gpt.disk_guid) ==
430 sizeof(new_entry_2->partition.gpt.part_unique_guid));
432 new_entry_2->unknown_0x58[0] = 0x00000000;
433 new_entry_2->unknown_0x58[1] = 0x00000000;
434 new_entry_2->unknown_0x58[2] = 0x00000000;
435 new_entry_2->unknown_0x58[3] = 0x00000000;
437 wimlib_assert(wim_path[2] == L'\\');
438 return mempcpy(new_entry_2->wim_file_name,
440 new_entry_2_size - sizeof(struct WimOverlay_dat_entry_2));
444 * Prepares the new contents of WimOverlay.dat in memory, with one entry added.
447 * Old contents of WimOverlay.dat, or NULL if it did not exist.
449 * Absolute path to the WIM file. It must begin with a drive letter; for
450 * example, D:\install.wim.
452 * GUID of the WIM, from the WIM header.
454 * Number of the image in the WIM to specify in the new entry.
456 * Location into which to return the new contents as a malloc()ed buffer on
458 * @new_contents_size_ret
459 * Location into which to return the size, in bytes, of the new contents on
461 * @new_data_source_id_ret
462 * Location into which to return the data source ID of the new entry on
465 * Returns 0 on success, or a positive error code on failure.
468 prepare_wimoverlay_dat(const struct WimOverlay_dat_header *old_hdr,
469 const wchar_t *wim_path,
470 const u8 wim_guid[GUID_SIZE],
472 void **new_contents_ret,
473 u32 *new_contents_size_ret,
474 u64 *new_data_source_id_ret)
477 PARTITION_INFORMATION_EX part_info;
478 DRIVE_LAYOUT_INFORMATION_EX disk_info;
479 u64 new_data_source_id;
480 u32 new_entry_2_size;
481 u32 new_contents_size;
485 ret = query_partition_and_disk_info(wim_path, &part_info, &disk_info);
489 new_data_source_id = old_hdr ? old_hdr->next_data_source_id : 0;
491 new_entry_2_size = sizeof(struct WimOverlay_dat_entry_2) +
492 ((wcslen(wim_path) - 2 + 1) * sizeof(wchar_t));
493 ret = calculate_wimoverlay_dat_size(old_hdr, new_entry_2_size,
498 buf = MALLOC(new_contents_size);
500 return WIMLIB_ERR_NOMEM;
502 end = fill_in_wimoverlay_dat(buf, old_hdr, wim_path, wim_guid, image,
504 &part_info, &disk_info, new_entry_2_size);
506 wimlib_assert(end - buf == new_contents_size);
508 *new_contents_ret = buf;
509 *new_contents_size_ret = new_contents_size;
510 *new_data_source_id_ret = new_data_source_id;
515 * Reads and validates a WimOverlay.dat file.
518 * Path to the WimOverlay.dat file, such as
519 * C:\System Volume Information\WimOverlay.dat
521 * Location into which to return the contents as a malloc()ed buffer on
522 * success. This can be cast to 'struct WimOverlay_dat_header', and its
523 * contents are guaranteed to be valid. Alternatively, if the file does
524 * not exist, NULL will be returned here.
526 * Returns 0 on success, or a positive error code on failure.
529 read_wimoverlay_dat(const wchar_t *path, void **contents_ret)
532 BY_HANDLE_FILE_INFORMATION info;
535 const struct WimOverlay_dat_header *hdr;
537 bool already_retried = false;
540 h = open_file(path, GENERIC_READ);
541 if (h == INVALID_HANDLE_VALUE) {
542 DWORD err = GetLastError();
543 if (err == ERROR_FILE_NOT_FOUND) {
544 *contents_ret = NULL;
547 if (err == ERROR_PATH_NOT_FOUND &&
548 func_RtlCreateSystemVolumeInformationFolder)
550 wchar_t volume_root_path[] = L"\\??\\X:\\";
552 *(wcschr(volume_root_path, L'X')) = path[0];
554 UNICODE_STRING str = {
555 .Length = sizeof(volume_root_path) - sizeof(wchar_t),
556 .MaximumLength = sizeof(volume_root_path),
557 .Buffer = volume_root_path,
562 status = (*func_RtlCreateSystemVolumeInformationFolder)(&str);
564 err2 = RtlNtStatusToDosError(status);
565 if (err2 == ERROR_SUCCESS) {
566 if (!already_retried) {
567 already_retried = true;
574 win32_error(err, L"\"%ls\": Can't open for reading", path);
575 return WIMLIB_ERR_OPEN;
577 if (!GetFileInformationByHandle(h, &info)) {
578 win32_error(GetLastError(), L"\"%ls\": Can't query metadata", path);
580 return WIMLIB_ERR_STAT;
584 if (!info.nFileSizeHigh)
585 contents = MALLOC(info.nFileSizeLow);
587 ERROR("\"%ls\": File is too large to fit into memory", path);
589 return WIMLIB_ERR_NOMEM;
593 if (!ReadFile(h, contents, info.nFileSizeLow, &bytes_read, NULL) ||
594 bytes_read != info.nFileSizeLow)
596 win32_error(GetLastError(), L"\"%ls\": Can't read data", path);
598 ret = WIMLIB_ERR_READ;
599 goto out_free_contents;
604 if (info.nFileSizeLow < sizeof(struct WimOverlay_dat_header)) {
605 ERROR("\"%ls\": File is unexpectedly small (only %"PRIu32" bytes)",
606 path, (u32)info.nFileSizeLow);
607 ret = WIMLIB_ERR_UNSUPPORTED;
608 goto out_free_contents;
611 hdr = (const struct WimOverlay_dat_header *)contents;
613 if (hdr->magic != WIMOVERLAY_DAT_MAGIC ||
614 hdr->wim_provider_version != WIM_PROVIDER_CURRENT_VERSION ||
615 hdr->unknown_0x08 != 0x00000028)
617 ERROR("\"%ls\": Header contains unexpected data:", path);
618 if (wimlib_print_errors) {
619 print_byte_field((const u8 *)hdr,
620 sizeof(struct WimOverlay_dat_header),
622 fputc('\n', wimlib_error_file);
624 ret = WIMLIB_ERR_UNSUPPORTED;
625 goto out_free_contents;
628 if ((u64)hdr->num_entries * sizeof(struct WimOverlay_dat_entry_1) >
629 info.nFileSizeLow - sizeof(struct WimOverlay_dat_header))
631 ERROR("\"%ls\": File is unexpectedly small "
632 "(only %"PRIu32" bytes, but has %"PRIu32" entries)",
633 path, (u32)info.nFileSizeLow, hdr->num_entries);
634 ret = WIMLIB_ERR_UNSUPPORTED;
635 goto out_free_contents;
638 for (u32 i = 0; i < hdr->num_entries; i++) {
639 const struct WimOverlay_dat_entry_1 *entry_1;
640 const struct WimOverlay_dat_entry_2 *entry_2;
641 u32 wim_file_name_length;
643 entry_1 = &hdr->entry_1s[i];
645 if (entry_1->data_source_id >= hdr->next_data_source_id) {
646 ERROR("\"%ls\": value of next_data_source_id "
647 "(0x%016"PRIx64") is unexpected, since entry "
648 "%"PRIu32" has data source ID 0x%016"PRIx64,
649 path, hdr->next_data_source_id,
650 i, entry_1->data_source_id);
651 ret = WIMLIB_ERR_UNSUPPORTED;
652 goto out_free_contents;
655 if (((u64)entry_1->entry_2_offset +
656 (u64)entry_1->entry_2_length) >
659 ERROR("\"%ls\": entry %"PRIu32" (2) "
660 "(data source ID 0x%016"PRIx64") "
662 path, i, entry_1->data_source_id);
663 ret = WIMLIB_ERR_UNSUPPORTED;
664 goto out_free_contents;
666 if (entry_1->entry_2_length < sizeof(struct WimOverlay_dat_entry_2)) {
667 ERROR("\"%ls\": entry %"PRIu32" (2) "
668 "(data source ID 0x%016"PRIx64") "
670 path, i, entry_1->data_source_id);
671 ret = WIMLIB_ERR_UNSUPPORTED;
672 goto out_free_contents;
675 if (entry_1->entry_2_offset % 2 != 0) {
676 ERROR("\"%ls\": entry %"PRIu32" (2) "
677 "(data source ID 0x%016"PRIx64") "
679 path, i, entry_1->data_source_id);
680 ret = WIMLIB_ERR_UNSUPPORTED;
681 goto out_free_contents;
684 entry_2 = (const struct WimOverlay_dat_entry_2 *)
685 ((const u8 *)hdr + entry_1->entry_2_offset);
687 wim_file_name_length = entry_1->entry_2_length -
688 sizeof(struct WimOverlay_dat_entry_2);
689 if ((wim_file_name_length < 2 * sizeof(wchar_t)) ||
690 (wim_file_name_length % sizeof(wchar_t) != 0) ||
691 (wmemchr(entry_2->wim_file_name, L'\0',
692 wim_file_name_length / sizeof(wchar_t))
693 != &entry_2->wim_file_name[wim_file_name_length /
694 sizeof(wchar_t) - 1]))
696 ERROR("\"%ls\": entry %"PRIu32" (2) "
697 "(data source ID 0x%016"PRIx64") "
698 "has invalid WIM file name",
699 path, i, entry_1->data_source_id);
700 if (wimlib_print_errors) {
701 print_byte_field((const u8 *)entry_2->wim_file_name,
702 wim_file_name_length,
704 fputc('\n', wimlib_error_file);
706 ret = WIMLIB_ERR_UNSUPPORTED;
707 goto out_free_contents;
710 if (entry_2->unknown_0x00 != 0x00000000 ||
711 entry_2->unknown_0x04 != 0x00000000 ||
712 entry_2->unknown_0x0C != 0x00000000 ||
713 entry_2->entry_2_length != entry_1->entry_2_length ||
714 entry_2->unknown_0x10 != 0x00000005 ||
715 entry_2->unknown_0x14 != 0x00000001 ||
716 entry_2->inner_struct_size != entry_1->entry_2_length - 0x14 ||
717 entry_2->unknown_0x1C != 0x00000005 ||
718 entry_2->unknown_0x20 != 0x00000006 ||
719 entry_2->unknown_0x24 != 0x00000000 ||
720 entry_2->unknown_0x28 != 0x00000048 ||
721 entry_2->unknown_0x2C != 0x00000000 ||
722 entry_2->unknown_0x40 != 0x00000000 ||
723 (entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_GPT &&
724 entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_MBR) ||
725 (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_MBR &&
726 entry_2->partition.mbr.padding != 0) ||
727 (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_GPT &&
728 entry_2->partition.mbr.padding == 0) ||
729 entry_2->unknown_0x58[0] != 0x00000000 ||
730 entry_2->unknown_0x58[1] != 0x00000000 ||
731 entry_2->unknown_0x58[2] != 0x00000000 ||
732 entry_2->unknown_0x58[3] != 0x00000000)
734 ERROR("\"%ls\": entry %"PRIu32" (2) "
735 "(data source ID 0x%016"PRIx64") "
736 "contains unexpected data!",
737 path, i, entry_1->data_source_id);
738 if (wimlib_print_errors) {
739 print_byte_field((const u8 *)entry_2,
740 entry_1->entry_2_length,
742 fputc('\n', wimlib_error_file);
744 ret = WIMLIB_ERR_UNSUPPORTED;
745 goto out_free_contents;
749 *contents_ret = contents;
758 * Update WimOverlay.dat manually in order to add a WIM data source to the
761 * THIS CODE IS EXPERIMENTAL AS I HAD TO REVERSE ENGINEER THE FILE FORMAT!
764 * Target drive. Must be a letter followed by a colon (e.g. D:).
766 * Absolute path to the WIM file. It must begin with a drive letter; for
767 * example, D:\install.wim.
769 * GUID of the WIM, from the WIM header.
771 * Number of the image in the WIM to specify in the new entry.
772 * @data_source_id_ret
773 * On success, the allocated data source ID is returned here.
776 update_wimoverlay_manually(const wchar_t *drive, const wchar_t *wim_path,
777 const u8 wim_guid[GUID_SIZE],
778 int image, u64 *data_source_id_ret)
780 wchar_t path_main[] = L"A:\\System Volume Information\\WimOverlay.dat";
781 wchar_t path_backup[] = L"A:\\System Volume Information\\WimOverlay.backup";
782 wchar_t path_wimlib_backup[] = L"A:\\System Volume Information\\WimOverlay.wimlib_backup";
783 wchar_t path_new[] = L"A:\\System Volume Information\\WimOverlay.wimlib_new";
784 void *old_contents = NULL;
785 void *new_contents = NULL;
786 u32 new_contents_size = 0;
787 u64 new_data_source_id = -1;
790 wimlib_assert(drive[0] != L'\0' &&
794 path_main[0] = drive[0];
795 path_backup[0] = drive[0];
796 path_wimlib_backup[0] = drive[0];
797 path_new[0] = drive[0];
799 ret = read_wimoverlay_dat(path_main, &old_contents);
803 ret = prepare_wimoverlay_dat(old_contents, wim_path, wim_guid, image,
804 &new_contents, &new_contents_size,
805 &new_data_source_id);
810 /* Write WimOverlay.wimlib_new */
811 ret = write_wimoverlay_dat(path_new,
812 new_contents, new_contents_size);
814 goto out_free_new_contents;
816 /* Write WimOverlay.backup */
817 ret = write_wimoverlay_dat(path_backup,
818 new_contents, new_contents_size);
820 goto out_free_new_contents;
823 /* Rename WimOverlay.dat => WimOverlay.wimlib_backup */
824 ret = win32_rename_replacement(path_main, path_wimlib_backup);
826 ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
827 path_main, path_wimlib_backup);
828 ret = WIMLIB_ERR_RENAME;
829 goto out_free_new_contents;
833 /* Rename WimOverlay.wimlib_new => WimOverlay.dat */
834 ret = win32_rename_replacement(path_new, path_main);
836 ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
837 path_new, path_main);
838 ret = WIMLIB_ERR_RENAME;
840 out_free_new_contents:
843 if (ret == WIMLIB_ERR_UNSUPPORTED) {
844 ERROR("Please report to developer ("PACKAGE_BUGREPORT").\n"
845 " If possible send the file \"%ls\".\n\n", path_main);
848 *data_source_id_ret = new_data_source_id;
853 * Allocate a WOF data source ID for a WIM file.
856 * Absolute path to the WIM file. This must include a drive letter and use
857 * backslash path separators.
859 * GUID of the WIM, from the WIM header.
861 * Number of the image in the WIM being applied.
863 * Path to the target directory.
864 * @data_source_id_ret
865 * On success, an identifier for the backing WIM file will be returned
868 * Returns 0 on success, or a positive error code on failure.
871 wimboot_alloc_data_source_id(const wchar_t *wim_path,
872 const u8 wim_guid[GUID_SIZE],
873 int image, const wchar_t *target,
874 u64 *data_source_id_ret, bool *wof_running_ret)
877 size_t wim_path_nchars;
878 size_t wim_file_name_length;
881 struct wof_external_info *wof_info;
882 struct wim_provider_add_overlay_input *wim_info;
885 DWORD bytes_returned;
887 const wchar_t *prefix = L"\\??\\";
888 const size_t prefix_nchars = 4;
889 bool tried_to_attach_wof = false;
891 ret = win32_get_drive_path(target, drive_path);
895 wimlib_assert(!wcschr(wim_path, L'/'));
896 wimlib_assert(wim_path[0] != L'\0' && wim_path[1] == L':');
898 wim_path_nchars = wcslen(wim_path);
899 wim_file_name_length = sizeof(wchar_t) *
900 (wim_path_nchars + prefix_nchars);
902 insize = sizeof(struct wof_external_info) +
903 sizeof(struct wim_provider_add_overlay_input) +
904 wim_file_name_length;
908 ret = WIMLIB_ERR_NOMEM;
912 wof_info = (struct wof_external_info *)in;
913 wof_info->version = WOF_CURRENT_VERSION;
914 wof_info->provider = WOF_PROVIDER_WIM;
916 wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1);
917 wim_info->wim_type = WIM_BOOT_NOT_OS_WIM;
918 wim_info->wim_index = image;
919 wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input,
921 wim_info->wim_file_name_length = wim_file_name_length;
922 wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars);
923 wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
926 h = open_file(drive_path, GENERIC_WRITE);
928 if (h == INVALID_HANDLE_VALUE) {
929 win32_error(GetLastError(),
930 L"Failed to open \"%ls\"", drive_path + 4);
931 ret = WIMLIB_ERR_OPEN;
935 if (!DeviceIoControl(h, FSCTL_ADD_OVERLAY,
937 &data_source_id, sizeof(data_source_id),
938 &bytes_returned, NULL))
940 DWORD err = GetLastError();
941 if (err == ERROR_INVALID_FUNCTION) {
942 if (!tried_to_attach_wof) {
944 h = INVALID_HANDLE_VALUE;
945 tried_to_attach_wof = true;
946 if (win32_try_to_attach_wof(drive_path + 4))
949 ret = WIMLIB_ERR_UNSUPPORTED;
950 goto out_close_handle;
952 win32_error(err, L"Failed to add overlay source \"%ls\" "
953 "to volume \"%ls\"", wim_path, drive_path + 4);
954 ret = WIMLIB_ERR_WIMBOOT;
955 goto out_close_handle;
959 if (bytes_returned != sizeof(data_source_id)) {
960 ret = WIMLIB_ERR_WIMBOOT;
961 ERROR("Unexpected result size when adding "
962 "overlay source \"%ls\" to volume \"%ls\"",
963 wim_path, drive_path + 4);
964 goto out_close_handle;
967 *wof_running_ret = true;
968 *data_source_id_ret = data_source_id;
976 if (ret == WIMLIB_ERR_UNSUPPORTED) {
977 WARNING("WOF driver is not available; updating WimOverlay.dat directly.");
978 ret = update_wimoverlay_manually(drive_path + 4, wim_path,
981 *wof_running_ret = false;
988 * Set WIMBoot information on the specified file.
990 * This turns it into a reparse point that redirects accesses to it, to the
991 * corresponding resource in the WIM archive.
994 * Open handle to the file, with GENERIC_WRITE access.
996 * The blob for the unnamed data stream of the file.
998 * Allocated identifier for the WIM data source on the destination volume.
1000 * SHA-1 message digest of the WIM's blob table.
1002 * %true if the WOF driver appears to be available and working; %false if
1005 * Returns %true on success, or %false on failure with GetLastError() set.
1008 wimboot_set_pointer(HANDLE h,
1009 const struct blob_descriptor *blob,
1011 const u8 blob_table_hash[SHA1_HASH_SIZE],
1014 DWORD bytes_returned;
1017 /* The WOF driver is running. We can create the reparse point
1018 * using FSCTL_SET_EXTERNAL_BACKING. */
1019 unsigned int max_retries = 4;
1021 struct wof_external_info wof_info;
1022 struct wim_provider_external_info wim_info;
1026 memset(&in, 0, sizeof(in));
1028 in.wof_info.version = WOF_CURRENT_VERSION;
1029 in.wof_info.provider = WOF_PROVIDER_WIM;
1031 in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
1032 in.wim_info.flags = 0;
1033 in.wim_info.data_source_id = data_source_id;
1034 copy_hash(in.wim_info.unnamed_data_stream_hash, blob->hash);
1036 /* blob_table_hash is not necessary */
1038 if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
1039 &in, sizeof(in), NULL, 0,
1040 &bytes_returned, NULL))
1042 /* Try to track down sporadic errors */
1043 if (wimlib_print_errors) {
1044 WARNING("FSCTL_SET_EXTERNAL_BACKING failed (err=%u); data was %zu bytes:",
1045 (u32)GetLastError(), sizeof(in));
1046 print_byte_field((const u8 *)&in, sizeof(in), wimlib_error_file);
1047 putc('\n', wimlib_error_file);
1049 if (--max_retries) {
1050 WARNING("Retrying after 100ms...");
1054 WARNING("Too many retries; returning failure");
1059 /* The WOF driver is running. We need to create the reparse
1060 * point manually. */
1068 struct wof_external_info wof_info;
1069 struct wim_provider_rpdata wim_info;
1072 STATIC_ASSERT(sizeof(in) == 8 +
1073 sizeof(struct wof_external_info) +
1074 sizeof(struct wim_provider_rpdata));
1076 in.hdr.rptag = WIM_IO_REPARSE_TAG_WOF;
1077 in.hdr.rpdatalen = sizeof(in) - sizeof(in.hdr);
1078 in.hdr.rpreserved = 0;
1080 in.wof_info.version = WOF_CURRENT_VERSION;
1081 in.wof_info.provider = WOF_PROVIDER_WIM;
1083 in.wim_info.version = 2;
1084 in.wim_info.flags = 0;
1085 in.wim_info.data_source_id = data_source_id;
1086 copy_hash(in.wim_info.unnamed_data_stream_hash, blob->hash);
1087 copy_hash(in.wim_info.blob_table_hash, blob_table_hash);
1088 in.wim_info.unnamed_data_stream_size = blob->size;
1089 in.wim_info.unnamed_data_stream_size_in_wim = blob->rdesc->size_in_wim;
1090 in.wim_info.unnamed_data_stream_offset_in_wim = blob->rdesc->offset_in_wim;
1092 if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
1093 &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
1096 /* We also need to create an unnamed data stream of the correct
1097 * size. Otherwise the file shows up as zero length. It can be
1098 * a sparse stream containing all zeroes; its contents
1099 * are unimportant. */
1100 if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
1101 &bytes_returned, NULL))
1104 if (!SetFilePointerEx(h,
1105 (LARGE_INTEGER){ .QuadPart = blob->size},
1109 if (!SetEndOfFile(h))
1116 #endif /* __WIN32__ */