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 part of wimlib, a library for working with WIM files.
18 * wimlib is free software; you can redistribute it and/or modify it under the
19 * terms of the GNU General Public License as published by the Free
20 * Software Foundation; either version 3 of the License, or (at your option)
23 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
24 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
25 * A PARTICULAR PURPOSE. See the GNU General Public License for more
28 * You should have received a copy of the GNU General Public License
29 * along with wimlib; if not, see http://www.gnu.org/licenses/.
38 #include "wimlib/win32_common.h"
39 #include "wimlib/win32.h"
40 #include "wimlib/assert.h"
41 #include "wimlib/error.h"
42 #include "wimlib/lookup_table.h"
43 #include "wimlib/util.h"
44 #include "wimlib/wimboot.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_ret,
81 DRIVE_LAYOUT_INFORMATION_EX *drive_info_ret)
84 wchar_t vol_name[] = L"\\\\.\\X:";
85 wchar_t disk_name[] = L"\\\\?\\PhysicalDriveXXXXXXXXXX";
87 PARTITION_INFORMATION_EX part_info;
88 size_t extents_size = sizeof(VOLUME_DISK_EXTENTS) + 4 * sizeof(DISK_EXTENT);
89 VOLUME_DISK_EXTENTS *extents = alloca(extents_size);
90 size_t drive_info_size = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
91 8 * sizeof(PARTITION_INFORMATION_EX);
92 DRIVE_LAYOUT_INFORMATION_EX *drive_info = alloca(drive_info_size);
94 wimlib_assert(path[0] != L'\0' && path[1] == L':');
96 *(wcschr(vol_name, L'X')) = path[0];
98 h = open_file(vol_name, GENERIC_READ);
99 if (h == INVALID_HANDLE_VALUE) {
100 set_errno_from_GetLastError();
101 ERROR_WITH_ERRNO("\"%ls\": Can't open volume device", vol_name);
102 return WIMLIB_ERR_OPEN;
105 if (!query_device(h, IOCTL_DISK_GET_PARTITION_INFO_EX,
106 &part_info, sizeof(part_info)))
108 ERROR("\"%ls\": Can't get partition info (err=0x%08"PRIx32")",
109 vol_name, (u32)GetLastError());
111 return WIMLIB_ERR_READ;
114 if (!query_device(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
115 extents, extents_size))
117 ERROR("\"%ls\": Can't get volume extent info (err=0x%08"PRIx32")",
118 vol_name, (u32)GetLastError());
120 return WIMLIB_ERR_READ;
125 if (extents->NumberOfDiskExtents != 1) {
126 ERROR("\"%ls\": This volume has %"PRIu32" disk extents, "
127 "but this code is untested for more than 1",
128 vol_name, (u32)extents->NumberOfDiskExtents);
129 return WIMLIB_ERR_UNSUPPORTED;
132 wsprintf(wcschr(disk_name, L'X'), L"%"PRIu32,
133 extents->Extents[0].DiskNumber);
135 h = open_file(disk_name, GENERIC_READ);
136 if (h == INVALID_HANDLE_VALUE) {
137 set_errno_from_GetLastError();
138 ERROR_WITH_ERRNO("\"%ls\": Can't open disk device", disk_name);
139 return WIMLIB_ERR_OPEN;
142 if (!query_device(h, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
143 drive_info, drive_info_size))
145 ERROR("\"%ls\": Can't get disk info (err=0x%08"PRIx32")",
146 disk_name, (u32)GetLastError());
148 return WIMLIB_ERR_READ;
153 if (drive_info->PartitionStyle != part_info.PartitionStyle) {
154 ERROR("\"%ls\", \"%ls\": Inconsistent partition table type!",
155 vol_name, disk_name);
156 return WIMLIB_ERR_UNSUPPORTED;
159 if (part_info.PartitionStyle == PARTITION_STYLE_GPT) {
160 BUILD_BUG_ON(sizeof(part_info.Gpt.PartitionId) !=
161 sizeof(drive_info->Gpt.DiskId));
162 if (!memcmp(&part_info.Gpt.PartitionId,
163 &drive_info->Gpt.DiskId,
164 sizeof(drive_info->Gpt.DiskId)))
166 ERROR("\"%ls\", \"%ls\": Partition GUID is the "
167 "same as the disk GUID???", vol_name, disk_name);
168 return WIMLIB_ERR_UNSUPPORTED;
172 if (part_info.PartitionStyle != PARTITION_STYLE_MBR &&
173 part_info.PartitionStyle != PARTITION_STYLE_GPT)
175 ERROR("\"%ls\": Unknown partition style 0x%08"PRIx32,
176 vol_name, (u32)part_info.PartitionStyle);
177 return WIMLIB_ERR_UNSUPPORTED;
180 *part_info_ret = part_info;
181 *drive_info_ret = *drive_info;
186 * Allocate a new WIM data source ID.
189 * Previous WimOverlay.dat contents, or NULL if file did not exist.
191 * Returns the new data source ID.
194 alloc_new_data_source_id(const struct WimOverlay_dat_header *old_hdr)
199 for (u64 id = 0; ; id++) {
200 for (u32 i = 0; i < old_hdr->num_entries_1; i++)
201 if (id == old_hdr->entry_1s[i].data_source_id)
210 * Calculate the size of WimOverlay.dat with one entry added.
213 * Previous WimOverlay.dat contents, or NULL if file did not exist.
215 * Size of entry_2 being added.
217 * Size will be returned here.
219 * Returns 0 on success, or WIMLIB_ERR_UNSUPPORTED if size overflows 32 bits.
222 calculate_wimoverlay_dat_size(const struct WimOverlay_dat_header *old_hdr,
223 u32 new_entry_2_size, u32 *size_ret)
228 size_64 = sizeof(struct WimOverlay_dat_header);
230 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
231 size_64 += sizeof(struct WimOverlay_dat_entry_1);
232 size_64 += old_hdr->entry_1s[i].entry_2_length;
235 size_64 += sizeof(struct WimOverlay_dat_entry_1);
236 size_64 += new_entry_2_size;
240 return WIMLIB_ERR_UNSUPPORTED;
247 * Writes @size bytes of @contents to the named file @path.
249 * Returns 0 on success; WIMLIB_ERR_OPEN or WIMLIB_ERR_WRITE on failure.
252 write_wimoverlay_dat(const wchar_t *path, const void *contents, u32 size)
257 h = CreateFile(path, GENERIC_WRITE, 0, NULL,
258 CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
259 if (h == INVALID_HANDLE_VALUE) {
260 set_errno_from_GetLastError();
261 ERROR_WITH_ERRNO("\"%ls\": Can't open file for writing", path);
262 return WIMLIB_ERR_OPEN;
265 if (!WriteFile(h, contents, size, &bytes_written, NULL) ||
266 bytes_written != size)
268 set_errno_from_GetLastError();
269 ERROR_WITH_ERRNO("\"%ls\": Can't write file", path);
271 return WIMLIB_ERR_WRITE;
274 if (!CloseHandle(h)) {
275 set_errno_from_GetLastError();
276 ERROR_WITH_ERRNO("\"%ls\": Can't close handle", path);
277 return WIMLIB_ERR_WRITE;
284 * Generates the contents of WimOverlay.dat in memory, with one entry added.
287 * Buffer large enough to hold the new contents.
289 * Old contents of WimOverlay.dat, or NULL if it did not exist.
291 * Absolute path to the WIM file. It must begin with a drive letter; for
292 * example, D:\install.wim.
294 * GUID of the WIM, from the WIM header.
296 * Number of the image in the WIM to specify in the new entry.
297 * @new_data_source_id
298 * Data source ID to use for the new entry.
300 * Partition information for the WIM file.
302 * Disk information for the WIM file.
304 * Size, in bytes, of the new location information structure ('struct
305 * WimOverlay_dat_entry_2').
307 * Returns a pointer one past the last byte of @buf filled in.
310 fill_in_wimoverlay_dat(u8 *buf,
311 const struct WimOverlay_dat_header *old_hdr,
312 const wchar_t *wim_path,
313 const u8 wim_guid[WIM_GUID_LEN],
315 u64 new_data_source_id,
316 const PARTITION_INFORMATION_EX *part_info,
317 const DRIVE_LAYOUT_INFORMATION_EX *disk_info,
318 u32 new_entry_2_size)
320 struct WimOverlay_dat_header *new_hdr;
321 struct WimOverlay_dat_entry_1 *new_entry_1;
322 struct WimOverlay_dat_entry_2 *new_entry_2;
326 new_hdr = (struct WimOverlay_dat_header *)p;
328 /* Fill in new header */
329 new_hdr->magic = WIMOVERLAY_DAT_MAGIC;
330 new_hdr->wim_provider_version = WIM_PROVIDER_CURRENT_VERSION;
331 new_hdr->unknown_0x08 = 0x00000028;
332 new_hdr->num_entries_1 = (old_hdr ? old_hdr->num_entries_1 : 0) + 1;
333 new_hdr->num_entries_2 = (old_hdr ? old_hdr->num_entries_2 : 0) + 1;
334 new_hdr->unknown_0x14 = 0x00000000;
336 p += sizeof(struct WimOverlay_dat_header);
338 /* Copy WIM-specific information for old entries */
339 entry_2_offset = sizeof(struct WimOverlay_dat_header) +
340 (new_hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1));
342 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
343 new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
345 p = mempcpy(p, &old_hdr->entry_1s[i],
346 sizeof(struct WimOverlay_dat_entry_1));
348 new_entry_1->entry_2_offset = entry_2_offset;
349 entry_2_offset += new_entry_1->entry_2_length;
353 /* Generate WIM-specific information for new entry */
354 new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
356 new_entry_1->data_source_id = new_data_source_id;
357 new_entry_1->entry_2_offset = entry_2_offset;
358 new_entry_1->entry_2_length = new_entry_2_size;
359 new_entry_1->wim_type = WIM_BOOT_NOT_OS_WIM;
360 new_entry_1->wim_index = image;
361 BUILD_BUG_ON(sizeof(new_entry_1->guid) != WIM_GUID_LEN);
362 memcpy(new_entry_1->guid, wim_guid, WIM_GUID_LEN);
364 p += sizeof(struct WimOverlay_dat_entry_1);
366 /* Copy WIM location information for old entries */
368 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
369 wimlib_assert(new_hdr->entry_1s[i].entry_2_offset == p - buf);
370 wimlib_assert(old_hdr->entry_1s[i].entry_2_length ==
371 new_hdr->entry_1s[i].entry_2_length);
373 ((const u8 *)old_hdr + old_hdr->entry_1s[i].entry_2_offset),
374 old_hdr->entry_1s[i].entry_2_length);
378 /* Generate WIM location information for new entry */
379 new_entry_2 = (struct WimOverlay_dat_entry_2 *)p;
381 new_entry_2->unknown_0x00 = 0x00000000;
382 new_entry_2->unknown_0x04 = 0x00000000;
383 new_entry_2->entry_2_length = new_entry_2_size;
384 new_entry_2->unknown_0x0C = 0x00000000;
385 new_entry_2->unknown_0x10 = 0x00000005;
386 new_entry_2->unknown_0x14 = 0x00000001;
387 new_entry_2->inner_struct_size = new_entry_2_size - 0x14;
388 new_entry_2->unknown_0x1C = 0x00000005;
389 new_entry_2->unknown_0x20 = 0x00000006;
390 new_entry_2->unknown_0x24 = 0x00000000;
391 new_entry_2->unknown_0x28 = 0x00000048;
392 new_entry_2->unknown_0x2C = 0x00000000;
393 new_entry_2->unknown_0x40 = 0x00000000;
395 if (part_info->PartitionStyle == PARTITION_STYLE_MBR) {
396 new_entry_2->partition.mbr.part_start_offset = part_info->StartingOffset.QuadPart;
397 new_entry_2->partition.mbr.padding = 0;
398 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_MBR;
399 new_entry_2->disk.mbr.disk_id = disk_info->Mbr.Signature;
400 new_entry_2->disk.mbr.padding[0] = 0x00000000;
401 new_entry_2->disk.mbr.padding[1] = 0x00000000;
402 new_entry_2->disk.mbr.padding[2] = 0x00000000;
404 BUILD_BUG_ON(sizeof(new_entry_2->partition.gpt.part_unique_guid) !=
405 sizeof(part_info->Gpt.PartitionId));
406 memcpy(new_entry_2->partition.gpt.part_unique_guid,
407 &part_info->Gpt.PartitionId,
408 sizeof(part_info->Gpt.PartitionId));
409 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_GPT;
411 BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
412 sizeof(disk_info->Gpt.DiskId));
413 memcpy(new_entry_2->disk.gpt.disk_guid,
414 &disk_info->Gpt.DiskId,
415 sizeof(disk_info->Gpt.DiskId));
417 BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
418 sizeof(new_entry_2->partition.gpt.part_unique_guid));
420 new_entry_2->unknown_0x58[0] = 0x00000000;
421 new_entry_2->unknown_0x58[1] = 0x00000000;
422 new_entry_2->unknown_0x58[2] = 0x00000000;
423 new_entry_2->unknown_0x58[3] = 0x00000000;
425 wimlib_assert(wim_path[2] == L'\\');
426 return mempcpy(new_entry_2->wim_file_name,
428 new_entry_2_size - sizeof(struct WimOverlay_dat_entry_2));
432 * Prepares the new contents of WimOverlay.dat in memory, with one entry added.
435 * Old contents of WimOverlay.dat, or NULL if it did not exist.
437 * Absolute path to the WIM file. It must begin with a drive letter; for
438 * example, D:\install.wim.
440 * GUID of the WIM, from the WIM header.
442 * Number of the image in the WIM to specify in the new entry.
444 * Location into which to return the new contents as a malloc()ed buffer on
446 * @new_contents_size_ret
447 * Location into which to return the size, in bytes, of the new contents on
449 * @new_data_source_id_ret
450 * Location into which to return the data source ID of the new entry on
453 * Returns 0 on success, or a positive error code on failure.
456 prepare_wimoverlay_dat(const struct WimOverlay_dat_header *old_hdr,
457 const wchar_t *wim_path,
458 const u8 wim_guid[WIM_GUID_LEN],
460 void **new_contents_ret,
461 u32 *new_contents_size_ret,
462 u64 *new_data_source_id_ret)
465 PARTITION_INFORMATION_EX part_info;
466 DRIVE_LAYOUT_INFORMATION_EX disk_info;
467 u64 new_data_source_id;
468 u32 new_entry_2_size;
469 u32 new_contents_size;
473 ret = query_partition_and_disk_info(wim_path, &part_info, &disk_info);
477 new_data_source_id = alloc_new_data_source_id(old_hdr);
479 new_entry_2_size = sizeof(struct WimOverlay_dat_entry_2) +
480 ((wcslen(wim_path) - 2 + 1) * sizeof(wchar_t));
481 ret = calculate_wimoverlay_dat_size(old_hdr, new_entry_2_size,
486 buf = MALLOC(new_contents_size);
488 return WIMLIB_ERR_NOMEM;
490 end = fill_in_wimoverlay_dat(buf, old_hdr, wim_path, wim_guid, image,
492 &part_info, &disk_info, new_entry_2_size);
494 wimlib_assert(end - buf == new_contents_size);
496 *new_contents_ret = buf;
497 *new_contents_size_ret = new_contents_size;
498 *new_data_source_id_ret = new_data_source_id;
503 * Reads and validates a WimOverlay.dat file.
506 * Path to the WimOverlay.dat file, such as
507 * C:\System Volume Information\WimOverlay.dat
509 * Location into which to return the contents as a malloc()ed buffer on
510 * success. This can be cast to 'struct WimOverlay_dat_header', and its
511 * contents are guaranteed to be valid. Alternatively, if the file does
512 * not exist, NULL will be returned here.
514 * Returns 0 on success, or a positive error code on failure.
517 read_wimoverlay_dat(const wchar_t *path, void **contents_ret)
520 BY_HANDLE_FILE_INFORMATION info;
523 const struct WimOverlay_dat_header *hdr;
525 bool already_retried = false;
528 h = open_file(path, GENERIC_READ);
529 if (h == INVALID_HANDLE_VALUE) {
530 DWORD err = GetLastError();
531 if (err == ERROR_FILE_NOT_FOUND) {
532 *contents_ret = NULL;
535 if (err == ERROR_PATH_NOT_FOUND &&
536 func_RtlCreateSystemVolumeInformationFolder)
538 wchar_t volume_root_path[] = L"\\??\\X:\\";
540 *(wcschr(volume_root_path, L'X')) = path[0];
542 UNICODE_STRING str = {
543 .Length = sizeof(volume_root_path) - sizeof(wchar_t),
544 .MaximumLength = sizeof(volume_root_path),
545 .Buffer = volume_root_path,
550 status = (*func_RtlCreateSystemVolumeInformationFolder)(&str);
552 err2 = (*func_RtlNtStatusToDosError)(status);
553 if (err2 == ERROR_SUCCESS) {
554 if (!already_retried) {
555 already_retried = true;
562 set_errno_from_win32_error(err);
563 ERROR_WITH_ERRNO("\"%ls\": Can't open for reading", path);
564 return WIMLIB_ERR_OPEN;
566 if (!GetFileInformationByHandle(h, &info)) {
567 set_errno_from_GetLastError();
568 ERROR_WITH_ERRNO("\"%ls\": Can't query metadata", path);
570 return WIMLIB_ERR_STAT;
574 if (!info.nFileSizeHigh)
575 contents = MALLOC(info.nFileSizeLow);
577 ERROR("\"%ls\": File is too large to fit into memory", path);
579 return WIMLIB_ERR_NOMEM;
582 if (!ReadFile(h, contents, info.nFileSizeLow, &bytes_read, NULL) ||
583 bytes_read != info.nFileSizeLow)
585 set_errno_from_GetLastError();
586 ERROR_WITH_ERRNO("\"%ls\": Can't read data", path);
588 ret = WIMLIB_ERR_READ;
589 goto out_free_contents;
594 if (info.nFileSizeLow < sizeof(struct WimOverlay_dat_header)) {
595 ERROR("\"%ls\": File is unexpectedly small (only %"PRIu32" bytes)",
596 path, (u32)info.nFileSizeLow);
597 ret = WIMLIB_ERR_UNSUPPORTED;
598 goto out_free_contents;
601 hdr = (const struct WimOverlay_dat_header *)contents;
603 if (hdr->magic != WIMOVERLAY_DAT_MAGIC ||
604 hdr->wim_provider_version != WIM_PROVIDER_CURRENT_VERSION ||
605 hdr->unknown_0x08 != 0x00000028 ||
606 (hdr->num_entries_1 != hdr->num_entries_2) ||
607 hdr->unknown_0x14 != 0x00000000)
609 ERROR("\"%ls\": Header contains unexpected data:", path);
610 if (wimlib_print_errors) {
611 print_byte_field((const u8 *)hdr,
612 sizeof(struct WimOverlay_dat_header),
614 fputc('\n', wimlib_error_file);
616 ret = WIMLIB_ERR_UNSUPPORTED;
617 goto out_free_contents;
620 if ((u64)hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1) >
621 info.nFileSizeLow - sizeof(struct WimOverlay_dat_header))
623 ERROR("\"%ls\": File is unexpectedly small "
624 "(only %"PRIu32" bytes, but has %"PRIu32" entries)",
625 path, (u32)info.nFileSizeLow, hdr->num_entries_1);
626 ret = WIMLIB_ERR_UNSUPPORTED;
627 goto out_free_contents;
630 for (u32 i = 0; i < hdr->num_entries_1; i++) {
631 const struct WimOverlay_dat_entry_1 *entry_1;
632 const struct WimOverlay_dat_entry_2 *entry_2;
633 u32 wim_file_name_length;
635 entry_1 = &hdr->entry_1s[i];
637 if (((u64)entry_1->entry_2_offset +
638 (u64)entry_1->entry_2_length) >
641 ERROR("\"%ls\": entry %"PRIu32" (2) "
642 "(data source ID 0x%016"PRIx64") "
644 path, i, entry_1->data_source_id);
645 ret = WIMLIB_ERR_UNSUPPORTED;
646 goto out_free_contents;
648 if (entry_1->entry_2_length < sizeof(struct WimOverlay_dat_entry_2)) {
649 ERROR("\"%ls\": entry %"PRIu32" (2) "
650 "(data source ID 0x%016"PRIx64") "
652 path, i, entry_1->data_source_id);
653 ret = WIMLIB_ERR_UNSUPPORTED;
654 goto out_free_contents;
657 if (entry_1->entry_2_offset % 2 != 0) {
658 ERROR("\"%ls\": entry %"PRIu32" (2) "
659 "(data source ID 0x%016"PRIx64") "
661 path, i, entry_1->data_source_id);
662 ret = WIMLIB_ERR_UNSUPPORTED;
663 goto out_free_contents;
666 entry_2 = (const struct WimOverlay_dat_entry_2 *)
667 ((const u8 *)hdr + entry_1->entry_2_offset);
669 wim_file_name_length = entry_1->entry_2_length -
670 sizeof(struct WimOverlay_dat_entry_2);
671 if ((wim_file_name_length < 2 * sizeof(wchar_t)) ||
672 (wim_file_name_length % sizeof(wchar_t) != 0) ||
673 (wmemchr(entry_2->wim_file_name, L'\0',
674 wim_file_name_length / sizeof(wchar_t))
675 != &entry_2->wim_file_name[wim_file_name_length /
676 sizeof(wchar_t) - 1]))
678 ERROR("\"%ls\": entry %"PRIu32" (2) "
679 "(data source ID 0x%016"PRIx64") "
680 "has invalid WIM file name",
681 path, i, entry_1->data_source_id);
682 if (wimlib_print_errors) {
683 print_byte_field((const u8 *)entry_2->wim_file_name,
684 wim_file_name_length,
686 fputc('\n', wimlib_error_file);
688 ret = WIMLIB_ERR_UNSUPPORTED;
689 goto out_free_contents;
692 if (entry_2->unknown_0x00 != 0x00000000 ||
693 entry_2->unknown_0x04 != 0x00000000 ||
694 entry_2->unknown_0x0C != 0x00000000 ||
695 entry_2->entry_2_length != entry_1->entry_2_length ||
696 entry_2->unknown_0x10 != 0x00000005 ||
697 entry_2->unknown_0x14 != 0x00000001 ||
698 entry_2->inner_struct_size != entry_1->entry_2_length - 0x14 ||
699 entry_2->unknown_0x1C != 0x00000005 ||
700 entry_2->unknown_0x20 != 0x00000006 ||
701 entry_2->unknown_0x24 != 0x00000000 ||
702 entry_2->unknown_0x28 != 0x00000048 ||
703 entry_2->unknown_0x2C != 0x00000000 ||
704 entry_2->unknown_0x40 != 0x00000000 ||
705 (entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_GPT &&
706 entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_MBR) ||
707 (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_MBR &&
708 entry_2->partition.mbr.padding != 0) ||
709 (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_GPT &&
710 entry_2->partition.mbr.padding == 0) ||
711 entry_2->unknown_0x58[0] != 0x00000000 ||
712 entry_2->unknown_0x58[1] != 0x00000000 ||
713 entry_2->unknown_0x58[2] != 0x00000000 ||
714 entry_2->unknown_0x58[3] != 0x00000000)
716 ERROR("\"%ls\": entry %"PRIu32" (2) "
717 "(data source ID 0x%016"PRIx64") "
718 "contains unexpected data!",
719 path, i, entry_1->data_source_id);
720 if (wimlib_print_errors) {
721 print_byte_field((const u8 *)entry_2,
722 entry_1->entry_2_length,
724 fputc('\n', wimlib_error_file);
726 ret = WIMLIB_ERR_UNSUPPORTED;
727 goto out_free_contents;
731 *contents_ret = contents;
740 * Update WimOverlay.dat manually in order to add a WIM data source to the
743 * THIS CODE IS EXPERIMENTAL AS I HAD TO REVERSE ENGINEER THE FILE FORMAT!
746 * Target drive. Must be a letter followed by a colon (e.g. D:).
748 * Absolute path to the WIM file. It must begin with a drive letter; for
749 * example, D:\install.wim.
751 * GUID of the WIM, from the WIM header.
753 * Number of the image in the WIM to specify in the new entry.
754 * @data_source_id_ret
755 * On success, the allocated data source ID is returned here.
758 update_wimoverlay_manually(const wchar_t *drive, const wchar_t *wim_path,
759 const u8 wim_guid[WIM_GUID_LEN],
760 int image, u64 *data_source_id_ret)
762 wchar_t path_main[] = L"A:\\System Volume Information\\WimOverlay.dat";
763 wchar_t path_backup[] = L"A:\\System Volume Information\\WimOverlay.backup";
764 wchar_t path_wimlib_backup[] = L"A:\\System Volume Information\\WimOverlay.wimlib_backup";
765 wchar_t path_new[] = L"A:\\System Volume Information\\WimOverlay.wimlib_new";
768 u32 new_contents_size;
769 u64 new_data_source_id;
772 wimlib_assert(drive[0] != L'\0' &&
776 path_main[0] = drive[0];
777 path_backup[0] = drive[0];
778 path_wimlib_backup[0] = drive[0];
779 path_new[0] = drive[0];
781 ret = read_wimoverlay_dat(path_main, &old_contents);
785 ret = prepare_wimoverlay_dat(old_contents, wim_path, wim_guid, image,
786 &new_contents, &new_contents_size,
787 &new_data_source_id);
792 /* Write WimOverlay.wimlib_new */
793 ret = write_wimoverlay_dat(path_new,
794 new_contents, new_contents_size);
796 goto out_free_new_contents;
798 /* Write WimOverlay.backup */
799 ret = write_wimoverlay_dat(path_backup,
800 new_contents, new_contents_size);
802 goto out_free_new_contents;
805 /* Rename WimOverlay.dat => WimOverlay.wimlib_backup */
806 ret = win32_rename_replacement(path_main, path_wimlib_backup);
808 ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
809 path_main, path_wimlib_backup);
810 ret = WIMLIB_ERR_RENAME;
811 goto out_free_new_contents;
815 /* Rename WimOverlay.wimlib_new => WimOverlay.dat */
816 ret = win32_rename_replacement(path_new, path_main);
818 ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
819 path_new, path_main);
820 ret = WIMLIB_ERR_RENAME;
822 out_free_new_contents:
825 if (ret == WIMLIB_ERR_UNSUPPORTED) {
826 ERROR("Please report to developer ("PACKAGE_BUGREPORT").\n"
827 " If possible send the file \"%ls\".\n\n", path_main);
830 *data_source_id_ret = new_data_source_id;
835 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
839 file_abspath = realpath(file_path, NULL);
841 return WIMLIB_ERR_NOMEM;
843 if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
844 ERROR("\"%ls\": Path format not recognized", file_abspath);
846 return WIMLIB_ERR_UNSUPPORTED;
849 wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
854 /* Try to attach an instance of the Windows Overlay File System Filter Driver to
855 * the specified drive (such as C:) */
857 try_to_attach_wof(const wchar_t *drive)
862 /* Use FilterAttach() from Fltlib.dll. */
864 fltlib = LoadLibrary(L"Fltlib.dll");
867 WARNING("Failed to load Fltlib.dll");
871 HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName,
872 LPCWSTR lpVolumeName,
873 LPCWSTR lpInstanceName,
874 DWORD dwCreatedInstanceNameLength,
875 LPWSTR lpCreatedInstanceName);
877 func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach");
879 if (func_FilterAttach) {
882 res = (*func_FilterAttach)(L"WoF", drive, NULL, 0, NULL);
887 WARNING("FilterAttach() does not exist in Fltlib.dll");
896 * Allocate a WOF data source ID for a WIM file.
899 * Absolute path to the WIM file. This must include a drive letter and use
900 * backslash path separators.
902 * GUID of the WIM, from the WIM header.
904 * Number of the image in the WIM being applied.
906 * Path to the target directory.
907 * @data_source_id_ret
908 * On success, an identifier for the backing WIM file will be returned
911 * Returns 0 on success, or a positive error code on failure.
914 wimboot_alloc_data_source_id(const wchar_t *wim_path,
915 const u8 wim_guid[WIM_GUID_LEN],
916 int image, const wchar_t *target,
917 u64 *data_source_id_ret, bool *wof_running_ret)
920 size_t wim_path_nchars;
921 size_t wim_file_name_length;
924 struct wof_external_info *wof_info;
925 struct wim_provider_add_overlay_input *wim_info;
928 DWORD bytes_returned;
930 const wchar_t *prefix = L"\\??\\";
931 const size_t prefix_nchars = 4;
932 bool tried_to_attach_wof = false;
934 ret = win32_get_drive_path(target, drive_path);
938 wimlib_assert(!wcschr(wim_path, L'/'));
939 wimlib_assert(wim_path[0] != L'\0' && wim_path[1] == L':');
941 wim_path_nchars = wcslen(wim_path);
942 wim_file_name_length = sizeof(wchar_t) *
943 (wim_path_nchars + prefix_nchars);
945 insize = sizeof(struct wof_external_info) +
946 sizeof(struct wim_provider_add_overlay_input) +
947 wim_file_name_length;
951 ret = WIMLIB_ERR_NOMEM;
955 wof_info = (struct wof_external_info *)in;
956 wof_info->version = WOF_CURRENT_VERSION;
957 wof_info->provider = WOF_PROVIDER_WIM;
959 wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1);
960 wim_info->wim_type = WIM_BOOT_NOT_OS_WIM;
961 wim_info->wim_index = image;
962 wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input,
964 wim_info->wim_file_name_length = wim_file_name_length;
965 wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars);
966 wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
969 h = open_file(drive_path, GENERIC_WRITE);
971 if (h == INVALID_HANDLE_VALUE) {
972 set_errno_from_GetLastError();
973 ERROR_WITH_ERRNO("Failed to open \"%ls\"", drive_path + 4);
974 ret = WIMLIB_ERR_OPEN;
978 if (!DeviceIoControl(h, FSCTL_ADD_OVERLAY,
980 &data_source_id, sizeof(data_source_id),
981 &bytes_returned, NULL))
983 DWORD err = GetLastError();
984 if (err == ERROR_INVALID_FUNCTION) {
985 if (!tried_to_attach_wof) {
987 h = INVALID_HANDLE_VALUE;
988 tried_to_attach_wof = true;
989 if (try_to_attach_wof(drive_path + 4))
992 ret = WIMLIB_ERR_UNSUPPORTED;
993 goto out_close_handle;
995 set_errno_from_win32_error(err);
996 ERROR_WITH_ERRNO("Failed to add overlay source \"%ls\" "
997 "to volume \"%ls\" (err=0x%08"PRIx32")",
998 wim_path, drive_path + 4, (u32)err);
999 ret = WIMLIB_ERR_WIMBOOT;
1000 goto out_close_handle;
1004 if (bytes_returned != sizeof(data_source_id)) {
1005 set_errno_from_win32_error(ERROR_INVALID_DATA);
1006 ret = WIMLIB_ERR_WIMBOOT;
1007 ERROR("Unexpected result size when adding "
1008 "overlay source \"%ls\" to volume \"%ls\"",
1009 wim_path, drive_path + 4);
1010 goto out_close_handle;
1013 *wof_running_ret = true;
1014 *data_source_id_ret = data_source_id;
1022 if (ret == WIMLIB_ERR_UNSUPPORTED) {
1025 "The version of Windows you are running does not appear to support\n"
1026 " the Windows Overlay File System Filter Driver. This is normally\n"
1027 " available on Windows 8.1 Update 1 or later. Therefore, wimlib\n"
1028 " will attempt to update the WimOverlay.dat file directly.\n");
1030 WARNING("WOF driver is not available; updating WimOverlay.dat directly.");
1032 ret = update_wimoverlay_manually(drive_path + 4, wim_path,
1034 data_source_id_ret);
1035 *wof_running_ret = false;
1042 * Set WIMBoot information on the specified file.
1044 * This turns it into a reparse point that redirects accesses to it, to the
1045 * corresponding resource in the WIM archive.
1048 * Object attributes that specify the path to the file.
1050 * Printable representation of the path encoded in @attr.
1052 * Unnamed data stream of the file.
1054 * Allocated identifier for the WIM data source on the destination volume.
1055 * @lookup_table_hash
1056 * SHA-1 message digest of the WIM's lookup table.
1058 * %true if the WOF driver appears to be available and working; %false if
1061 * Returns 0 on success, or a positive error code on failure.
1064 wimboot_set_pointer(OBJECT_ATTRIBUTES *attr,
1065 const wchar_t *printable_name,
1066 const struct wim_lookup_table_entry *lte,
1068 const u8 lookup_table_hash[SHA1_HASH_SIZE],
1074 IO_STATUS_BLOCK iosb;
1075 DWORD bytes_returned;
1078 status = (*func_NtOpenFile)(&h, GENERIC_WRITE | SYNCHRONIZE, attr,
1079 &iosb, FILE_SHARE_VALID_FLAGS,
1080 FILE_OPEN_FOR_BACKUP_INTENT |
1081 FILE_OPEN_REPARSE_POINT |
1082 FILE_SYNCHRONOUS_IO_NONALERT);
1083 if (!NT_SUCCESS(status)) {
1084 SetLastError((*func_RtlNtStatusToDosError)(status));
1089 /* The WOF driver is running. We can create the reparse point
1090 * using FSCTL_SET_EXTERNAL_BACKING. */
1093 struct wof_external_info wof_info;
1094 struct wim_provider_external_info wim_info;
1097 in.wof_info.version = WOF_CURRENT_VERSION;
1098 in.wof_info.provider = WOF_PROVIDER_WIM;
1100 in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
1101 in.wim_info.flags = 0;
1102 in.wim_info.data_source_id = data_source_id;
1103 copy_hash(in.wim_info.resource_hash, lte->hash);
1105 /* lookup_table_hash is not necessary */
1107 if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
1108 &in, sizeof(in), NULL, 0,
1109 &bytes_returned, NULL))
1113 /* The WOF driver is running. We need to create the reparse
1114 * point manually. */
1122 struct wof_external_info wof_info;
1123 struct wim_provider_rpdata wim_info;
1126 BUILD_BUG_ON(sizeof(in) != 8 +
1127 sizeof(struct wof_external_info) +
1128 sizeof(struct wim_provider_rpdata));
1130 in.hdr.rptag = WIMLIB_REPARSE_TAG_WOF;
1131 in.hdr.rpdatalen = sizeof(in) - sizeof(in.hdr);
1132 in.hdr.rpreserved = 0;
1134 in.wof_info.version = WOF_CURRENT_VERSION;
1135 in.wof_info.provider = WOF_PROVIDER_WIM;
1137 in.wim_info.version = 2;
1138 in.wim_info.flags = 0;
1139 in.wim_info.data_source_id = data_source_id;
1140 copy_hash(in.wim_info.resource_hash, lte->hash);
1141 copy_hash(in.wim_info.wim_lookup_table_hash, lookup_table_hash);
1142 in.wim_info.stream_uncompressed_size = lte->size;
1143 in.wim_info.stream_compressed_size = lte->rspec->size_in_wim;
1144 in.wim_info.stream_offset_in_wim = lte->rspec->offset_in_wim;
1146 if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
1147 &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
1150 /* We also need to create an unnamed data stream of the correct
1151 * size. Otherwise the file shows up as zero length. It can be
1152 * a sparse stream containing all zeroes; its contents
1153 * are unimportant. */
1154 if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
1155 &bytes_returned, NULL))
1158 if (!SetFilePointerEx(h,
1159 (LARGE_INTEGER){ .QuadPart = lte->size},
1163 if (!SetEndOfFile(h))
1171 err = GetLastError();
1172 set_errno_from_win32_error(err);
1173 ERROR_WITH_ERRNO("\"%ls\": Couldn't set WIMBoot pointer data "
1174 "(err=%"PRIu32")", printable_name, (u32)err);
1175 ret = WIMLIB_ERR_WIMBOOT;
1183 #endif /* __WIN32__ */