]> wimlib.net Git - wimlib/blob - src/wimboot.c
wincfg: Add swapfile.sys
[wimlib] / src / wimboot.c
1 /*
2  * wimboot.c
3  *
4  * Support for creating WIMBoot pointer files.
5  *
6  * See http://technet.microsoft.com/en-us/library/dn594399.aspx for general
7  * information about WIMBoot.
8  *
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.
11  */
12
13 /*
14  * Copyright (C) 2014 Eric Biggers
15  *
16  * This file is part of wimlib, a library for working with WIM files.
17  *
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)
21  * any later version.
22  *
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
26  * details.
27  *
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/.
30  */
31
32
33 #ifdef HAVE_CONFIG_H
34 #  include "config.h"
35 #endif
36
37 #ifdef __WIN32__
38
39 #include "wimlib/win32_common.h"
40 #include "wimlib/win32.h"
41 #include "wimlib/assert.h"
42 #include "wimlib/error.h"
43 #include "wimlib/lookup_table.h"
44 #include "wimlib/util.h"
45 #include "wimlib/wimboot.h"
46 #include "wimlib/wof.h"
47
48 static HANDLE
49 open_file(const wchar_t *device_name, DWORD desiredAccess)
50 {
51         return CreateFile(device_name, desiredAccess,
52                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
53                           NULL, OPEN_EXISTING,
54                           FILE_FLAG_BACKUP_SEMANTICS, NULL);
55 }
56
57 static BOOL
58 query_device(HANDLE h, DWORD code, void *out, DWORD out_size)
59 {
60         DWORD bytes_returned;
61         return DeviceIoControl(h, code, NULL, 0, out, out_size,
62                                &bytes_returned, NULL);
63 }
64
65 /*
66  * Gets partition and drive information for the specified path.
67  *
68  * @path
69  *      Absolute path which must begin with a drive letter.  For example, if the
70  *      path is D:\install.wim, this function will query information about the
71  *      D: volume.
72  * @part_info_ret
73  *      Partition info is returned here.
74  * @drive_info_ret
75  *      Drive info is returned here.  The contained partition info will not be
76  *      valid.
77  *
78  * Returns 0 on success, or a positive error code on failure.
79  */
80 static int
81 query_partition_and_disk_info(const wchar_t *path,
82                               PARTITION_INFORMATION_EX *part_info_ret,
83                               DRIVE_LAYOUT_INFORMATION_EX *drive_info_ret)
84 {
85         HANDLE h;
86         wchar_t vol_name[] = L"\\\\.\\X:";
87         wchar_t disk_name[] = L"\\\\?\\PhysicalDriveXXXXXXXXXX";
88
89         PARTITION_INFORMATION_EX part_info;
90         size_t extents_size = sizeof(VOLUME_DISK_EXTENTS) + 4 * sizeof(DISK_EXTENT);
91         VOLUME_DISK_EXTENTS *extents = alloca(extents_size);
92         size_t drive_info_size = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
93                                         8 * sizeof(PARTITION_INFORMATION_EX);
94         DRIVE_LAYOUT_INFORMATION_EX *drive_info = alloca(drive_info_size);
95
96         wimlib_assert(path[0] != L'\0' && path[1] == L':');
97
98         *(wcschr(vol_name, L'X')) = path[0];
99
100         h = open_file(vol_name, GENERIC_READ);
101         if (h == INVALID_HANDLE_VALUE) {
102                 set_errno_from_GetLastError();
103                 ERROR_WITH_ERRNO("\"%ls\": Can't open volume device", vol_name);
104                 return WIMLIB_ERR_OPEN;
105         }
106
107         if (!query_device(h, IOCTL_DISK_GET_PARTITION_INFO_EX,
108                           &part_info, sizeof(part_info)))
109         {
110                 ERROR("\"%ls\": Can't get partition info (err=0x%08"PRIx32")",
111                       vol_name, (u32)GetLastError());
112                 CloseHandle(h);
113                 return WIMLIB_ERR_READ;
114         }
115
116         if (!query_device(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
117                           extents, extents_size))
118         {
119                 ERROR("\"%ls\": Can't get volume extent info (err=0x%08"PRIx32")",
120                       vol_name, (u32)GetLastError());
121                 CloseHandle(h);
122                 return WIMLIB_ERR_READ;
123         }
124
125         CloseHandle(h);
126
127         if (extents->NumberOfDiskExtents != 1) {
128                 ERROR("\"%ls\": This volume has %"PRIu32" disk extents, "
129                       "but this code is untested for more than 1",
130                       vol_name, (u32)extents->NumberOfDiskExtents);
131                 return WIMLIB_ERR_UNSUPPORTED;
132         }
133
134         wsprintf(wcschr(disk_name, L'X'), L"%"PRIu32,
135                  extents->Extents[0].DiskNumber);
136
137         h = open_file(disk_name, GENERIC_READ);
138         if (h == INVALID_HANDLE_VALUE) {
139                 set_errno_from_GetLastError();
140                 ERROR_WITH_ERRNO("\"%ls\": Can't open disk device", disk_name);
141                 return WIMLIB_ERR_OPEN;
142         }
143
144         if (!query_device(h, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
145                           drive_info, drive_info_size))
146         {
147                 ERROR("\"%ls\": Can't get disk info (err=0x%08"PRIx32")",
148                       disk_name, (u32)GetLastError());
149                 CloseHandle(h);
150                 return WIMLIB_ERR_READ;
151         }
152
153         CloseHandle(h);
154
155         if (drive_info->PartitionStyle != part_info.PartitionStyle) {
156                 ERROR("\"%ls\", \"%ls\": Inconsistent partition table type!",
157                       vol_name, disk_name);
158                 return WIMLIB_ERR_UNSUPPORTED;
159         }
160
161         if (part_info.PartitionStyle == PARTITION_STYLE_GPT) {
162                 BUILD_BUG_ON(sizeof(part_info.Gpt.PartitionId) !=
163                              sizeof(drive_info->Gpt.DiskId));
164                 if (!memcmp(&part_info.Gpt.PartitionId,
165                             &drive_info->Gpt.DiskId,
166                             sizeof(drive_info->Gpt.DiskId)))
167                 {
168                         ERROR("\"%ls\", \"%ls\": Partition GUID is the "
169                               "same as the disk GUID???", vol_name, disk_name);
170                         return WIMLIB_ERR_UNSUPPORTED;
171                 }
172         }
173
174         if (part_info.PartitionStyle != PARTITION_STYLE_MBR &&
175             part_info.PartitionStyle != PARTITION_STYLE_GPT)
176         {
177                 ERROR("\"%ls\": Unknown partition style 0x%08"PRIx32,
178                       vol_name, (u32)part_info.PartitionStyle);
179                 return WIMLIB_ERR_UNSUPPORTED;
180         }
181
182         *part_info_ret = part_info;
183         *drive_info_ret = *drive_info;
184         return 0;
185 }
186
187 /*
188  * Allocate a new WIM data source ID.
189  *
190  * @old_hdr
191  *      Previous WimOverlay.dat contents, or NULL if file did not exist.
192  *
193  * Returns the new data source ID.
194  */
195 static u64
196 alloc_new_data_source_id(const struct WimOverlay_dat_header *old_hdr)
197 {
198         if (!old_hdr)
199                 return 0;
200
201         for (u64 id = 0; ; id++) {
202                 for (u32 i = 0; i < old_hdr->num_entries_1; i++)
203                         if (id == old_hdr->entry_1s[i].data_source_id)
204                                 goto next;
205                 return id;
206         next:
207                 ;
208         }
209 }
210
211 /*
212  * Calculate the size of WimOverlay.dat with one entry added.
213  *
214  * @old_hdr
215  *      Previous WimOverlay.dat contents, or NULL if file did not exist.
216  * @new_entry_2_size
217  *      Size of entry_2 being added.
218  * @size_ret
219  *      Size will be returned here.
220  *
221  * Returns 0 on success, or WIMLIB_ERR_UNSUPPORTED if size overflows 32 bits.
222  */
223 static int
224 calculate_wimoverlay_dat_size(const struct WimOverlay_dat_header *old_hdr,
225                               u32 new_entry_2_size, u32 *size_ret)
226 {
227         u64 size_64;
228         u32 size;
229
230         size_64 = sizeof(struct WimOverlay_dat_header);
231         if (old_hdr) {
232                 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
233                         size_64 += sizeof(struct WimOverlay_dat_entry_1);
234                         size_64 += old_hdr->entry_1s[i].entry_2_length;
235                 }
236         }
237         size_64 += sizeof(struct WimOverlay_dat_entry_1);
238         size_64 += new_entry_2_size;
239
240         size = size_64;
241         if (size_64 != size)
242                 return WIMLIB_ERR_UNSUPPORTED;
243
244         *size_ret = size;
245         return 0;
246 }
247
248 /*
249  * Writes @size bytes of @contents to the named file @path.
250  *
251  * Returns 0 on success; WIMLIB_ERR_OPEN or WIMLIB_ERR_WRITE on failure.
252  */
253 static int
254 write_wimoverlay_dat(const wchar_t *path, const void *contents, u32 size)
255 {
256         HANDLE h;
257         DWORD bytes_written;
258
259         h = CreateFile(path, GENERIC_WRITE, 0, NULL,
260                        CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
261         if (h == INVALID_HANDLE_VALUE) {
262                 set_errno_from_GetLastError();
263                 ERROR_WITH_ERRNO("\"%ls\": Can't open file for writing", path);
264                 return WIMLIB_ERR_OPEN;
265         }
266
267         if (!WriteFile(h, contents, size, &bytes_written, NULL) ||
268             bytes_written != size)
269         {
270                 set_errno_from_GetLastError();
271                 ERROR_WITH_ERRNO("\"%ls\": Can't write file", path);
272                 CloseHandle(h);
273                 return WIMLIB_ERR_WRITE;
274         }
275
276         if (!CloseHandle(h)) {
277                 set_errno_from_GetLastError();
278                 ERROR_WITH_ERRNO("\"%ls\": Can't close handle", path);
279                 return WIMLIB_ERR_WRITE;
280         }
281
282         return 0;
283 }
284
285 /*
286  * Generates the contents of WimOverlay.dat in memory, with one entry added.
287  *
288  * @buf
289  *      Buffer large enough to hold the new contents.
290  * @old_hdr
291  *      Old contents of WimOverlay.dat, or NULL if it did not exist.
292  * @wim_path
293  *      Absolute path to the WIM file.  It must begin with a drive letter; for
294  *      example, D:\install.wim.
295  * @wim_guid
296  *      GUID of the WIM, from the WIM header.
297  * @image
298  *      Number of the image in the WIM to specify in the new entry.
299  * @new_data_source_id
300  *      Data source ID to use for the new entry.
301  * @part_info
302  *      Partition information for the WIM file.
303  * @disk_info
304  *      Disk information for the WIM file.
305  * @new_entry_2_size
306  *      Size, in bytes, of the new location information structure ('struct
307  *      WimOverlay_dat_entry_2').
308  *
309  * Returns a pointer one past the last byte of @buf filled in.
310  */
311 static u8 *
312 fill_in_wimoverlay_dat(u8 *buf,
313                        const struct WimOverlay_dat_header *old_hdr,
314                        const wchar_t *wim_path,
315                        const u8 wim_guid[WIM_GID_LEN],
316                        int image,
317                        u64 new_data_source_id,
318                        const PARTITION_INFORMATION_EX *part_info,
319                        const DRIVE_LAYOUT_INFORMATION_EX *disk_info,
320                        u32 new_entry_2_size)
321 {
322         struct WimOverlay_dat_header *new_hdr;
323         struct WimOverlay_dat_entry_1 *new_entry_1;
324         struct WimOverlay_dat_entry_2 *new_entry_2;
325         u32 entry_2_offset;
326         u8 *p = buf;
327
328         new_hdr = (struct WimOverlay_dat_header *)p;
329
330         /* Fill in new header  */
331         new_hdr->magic = WIMOVERLAY_DAT_MAGIC;
332         new_hdr->wim_provider_version = WIM_PROVIDER_CURRENT_VERSION;
333         new_hdr->unknown_0x08 = 0x00000028;
334         new_hdr->num_entries_1 = (old_hdr ? old_hdr->num_entries_1 : 0) + 1;
335         new_hdr->num_entries_2 = (old_hdr ? old_hdr->num_entries_2 : 0) + 1;
336         new_hdr->unknown_0x14 = 0x00000000;
337
338         p += sizeof(struct WimOverlay_dat_header);
339
340         /* Copy WIM-specific information for old entries  */
341         entry_2_offset = sizeof(struct WimOverlay_dat_header) +
342                         (new_hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1));
343         if (old_hdr) {
344                 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
345                         new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
346
347                         p = mempcpy(p, &old_hdr->entry_1s[i],
348                                     sizeof(struct WimOverlay_dat_entry_1));
349
350                         new_entry_1->entry_2_offset = entry_2_offset;
351                         entry_2_offset += new_entry_1->entry_2_length;
352                 }
353         }
354
355         /* Generate WIM-specific information for new entry  */
356         new_entry_1 = (struct WimOverlay_dat_entry_1 *)p;
357
358         new_entry_1->data_source_id = new_data_source_id;
359         new_entry_1->entry_2_offset = entry_2_offset;
360         new_entry_1->entry_2_length = new_entry_2_size;
361         new_entry_1->wim_type = WIM_BOOT_NOT_OS_WIM;
362         new_entry_1->wim_index = image;
363         BUILD_BUG_ON(sizeof(new_entry_1->guid) != WIM_GID_LEN);
364         memcpy(new_entry_1->guid, wim_guid, WIM_GID_LEN);
365
366         p += sizeof(struct WimOverlay_dat_entry_1);
367
368         /* Copy WIM location information for old entries  */
369         if (old_hdr) {
370                 for (u32 i = 0; i < old_hdr->num_entries_1; i++) {
371                         wimlib_assert(new_hdr->entry_1s[i].entry_2_offset == p - buf);
372                         wimlib_assert(old_hdr->entry_1s[i].entry_2_length ==
373                                       new_hdr->entry_1s[i].entry_2_length);
374                         p = mempcpy(p,
375                                     ((const u8 *)old_hdr + old_hdr->entry_1s[i].entry_2_offset),
376                                     old_hdr->entry_1s[i].entry_2_length);
377                 }
378         }
379
380         /* Generate WIM location information for new entry  */
381         new_entry_2 = (struct WimOverlay_dat_entry_2 *)p;
382
383         new_entry_2->unknown_0x00 = 0x00000000;
384         new_entry_2->unknown_0x04 = 0x00000000;
385         new_entry_2->entry_2_length = new_entry_2_size;
386         new_entry_2->unknown_0x0C = 0x00000000;
387         new_entry_2->unknown_0x10 = 0x00000005;
388         new_entry_2->unknown_0x14 = 0x00000001;
389         new_entry_2->inner_struct_size = new_entry_2_size - 0x14;
390         new_entry_2->unknown_0x1C = 0x00000005;
391         new_entry_2->unknown_0x20 = 0x00000006;
392         new_entry_2->unknown_0x24 = 0x00000000;
393         new_entry_2->unknown_0x28 = 0x00000048;
394         new_entry_2->unknown_0x2C = 0x00000000;
395         new_entry_2->unknown_0x40 = 0x00000000;
396
397         if (part_info->PartitionStyle == PARTITION_STYLE_MBR) {
398                 new_entry_2->partition.mbr.part_start_offset = part_info->StartingOffset.QuadPart;
399                 new_entry_2->partition.mbr.padding = 0;
400                 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_MBR;
401                 new_entry_2->disk.mbr.disk_id = disk_info->Mbr.Signature;
402                 new_entry_2->disk.mbr.padding[0] = 0x00000000;
403                 new_entry_2->disk.mbr.padding[1] = 0x00000000;
404                 new_entry_2->disk.mbr.padding[2] = 0x00000000;
405         } else {
406                 BUILD_BUG_ON(sizeof(new_entry_2->partition.gpt.part_unique_guid) !=
407                              sizeof(part_info->Gpt.PartitionId));
408                 memcpy(new_entry_2->partition.gpt.part_unique_guid,
409                        &part_info->Gpt.PartitionId,
410                        sizeof(part_info->Gpt.PartitionId));
411                 new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_GPT;
412
413                 BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
414                              sizeof(disk_info->Gpt.DiskId));
415                 memcpy(new_entry_2->disk.gpt.disk_guid,
416                        &disk_info->Gpt.DiskId,
417                        sizeof(disk_info->Gpt.DiskId));
418
419                 BUILD_BUG_ON(sizeof(new_entry_2->disk.gpt.disk_guid) !=
420                              sizeof(new_entry_2->partition.gpt.part_unique_guid));
421         }
422         new_entry_2->unknown_0x58[0] = 0x00000000;
423         new_entry_2->unknown_0x58[1] = 0x00000000;
424         new_entry_2->unknown_0x58[2] = 0x00000000;
425         new_entry_2->unknown_0x58[3] = 0x00000000;
426
427         wimlib_assert(wim_path[2] == L'\\');
428         return mempcpy(new_entry_2->wim_file_name,
429                        wim_path + 2,
430                        new_entry_2_size - sizeof(struct WimOverlay_dat_entry_2));
431 }
432
433 /*
434  * Prepares the new contents of WimOverlay.dat in memory, with one entry added.
435  *
436  * @old_hdr
437  *      Old contents of WimOverlay.dat, or NULL if it did not exist.
438  * @wim_path
439  *      Absolute path to the WIM file.  It must begin with a drive letter; for
440  *      example, D:\install.wim.
441  * @wim_guid
442  *      GUID of the WIM, from the WIM header.
443  * @image
444  *      Number of the image in the WIM to specify in the new entry.
445  * @new_contents_ret
446  *      Location into which to return the new contents as a malloc()ed buffer on
447  *      success.
448  * @new_contents_size_ret
449  *      Location into which to return the size, in bytes, of the new contents on
450  *      success.
451  * @new_data_source_id_ret
452  *      Location into which to return the data source ID of the new entry on
453  *      success.
454  *
455  * Returns 0 on success, or a positive error code on failure.
456  */
457 static int
458 prepare_wimoverlay_dat(const struct WimOverlay_dat_header *old_hdr,
459                        const wchar_t *wim_path,
460                        const u8 wim_guid[WIM_GID_LEN],
461                        int image,
462                        void **new_contents_ret,
463                        u32 *new_contents_size_ret,
464                        u64 *new_data_source_id_ret)
465 {
466         int ret;
467         PARTITION_INFORMATION_EX part_info;
468         DRIVE_LAYOUT_INFORMATION_EX disk_info;
469         u64 new_data_source_id;
470         u32 new_entry_2_size;
471         u32 new_contents_size;
472         u8 *buf;
473         u8 *end;
474
475         ret = query_partition_and_disk_info(wim_path, &part_info, &disk_info);
476         if (ret)
477                 return ret;
478
479         new_data_source_id = alloc_new_data_source_id(old_hdr);
480
481         new_entry_2_size = sizeof(struct WimOverlay_dat_entry_2) +
482                                 ((wcslen(wim_path) - 2 + 1) * sizeof(wchar_t));
483         ret = calculate_wimoverlay_dat_size(old_hdr, new_entry_2_size,
484                                             &new_contents_size);
485         if (ret)
486                 return ret;
487
488         buf = MALLOC(new_contents_size);
489         if (!buf)
490                 return WIMLIB_ERR_NOMEM;
491
492         end = fill_in_wimoverlay_dat(buf, old_hdr, wim_path, wim_guid, image,
493                                      new_data_source_id,
494                                      &part_info, &disk_info, new_entry_2_size);
495
496         wimlib_assert(end - buf == new_contents_size);
497
498         *new_contents_ret = buf;
499         *new_contents_size_ret = new_contents_size;
500         *new_data_source_id_ret = new_data_source_id;
501         return 0;
502 }
503
504 /*
505  * Reads and validates a WimOverlay.dat file.
506  *
507  * @path
508  *      Path to the WimOverlay.dat file, such as
509  *      C:\System Volume Information\WimOverlay.dat
510  * @contents_ret
511  *      Location into which to return the contents as a malloc()ed buffer on
512  *      success.  This can be cast to 'struct WimOverlay_dat_header', and its
513  *      contents are guaranteed to be valid.  Alternatively, if the file does
514  *      not exist, NULL will be returned here.
515  *
516  * Returns 0 on success, or a positive error code on failure.
517  */
518 static int
519 read_wimoverlay_dat(const wchar_t *path, void **contents_ret)
520 {
521         HANDLE h;
522         BY_HANDLE_FILE_INFORMATION info;
523         int ret;
524         void *contents;
525         const struct WimOverlay_dat_header *hdr;
526         DWORD bytes_read;
527         bool already_retried = false;
528
529 retry:
530         h = open_file(path, GENERIC_READ);
531         if (h == INVALID_HANDLE_VALUE) {
532                 DWORD err = GetLastError();
533                 if (err == ERROR_FILE_NOT_FOUND) {
534                         *contents_ret = NULL;
535                         return 0;
536                 }
537                 if (err == ERROR_PATH_NOT_FOUND &&
538                     func_RtlCreateSystemVolumeInformationFolder)
539                 {
540                         wchar_t volume_root_path[] = L"\\??\\X:\\";
541
542                         *(wcschr(volume_root_path, L'X')) = path[0];
543
544                         UNICODE_STRING str = {
545                                 .Length = sizeof(volume_root_path) - sizeof(wchar_t),
546                                 .MaximumLength = sizeof(volume_root_path),
547                                 .Buffer = volume_root_path,
548                         };
549                         NTSTATUS status;
550                         DWORD err2;
551
552                         status = (*func_RtlCreateSystemVolumeInformationFolder)(&str);
553
554                         err2 = (*func_RtlNtStatusToDosError)(status);
555                         if (err2 == ERROR_SUCCESS) {
556                                 if (!already_retried) {
557                                         already_retried = true;
558                                         goto retry;
559                                 }
560                         } else {
561                                 err = err2;
562                         }
563                 }
564                 set_errno_from_win32_error(err);
565                 ERROR_WITH_ERRNO("\"%ls\": Can't open for reading", path);
566                 return WIMLIB_ERR_OPEN;
567         }
568         if (!GetFileInformationByHandle(h, &info)) {
569                 set_errno_from_GetLastError();
570                 ERROR_WITH_ERRNO("\"%ls\": Can't query metadata", path);
571                 CloseHandle(h);
572                 return WIMLIB_ERR_STAT;
573         }
574
575         contents = NULL;
576         if (!info.nFileSizeHigh)
577                 contents = MALLOC(info.nFileSizeLow);
578         if (!contents) {
579                 ERROR("\"%ls\": File is too large to fit into memory", path);
580                 CloseHandle(h);
581                 return WIMLIB_ERR_NOMEM;
582         }
583
584         if (!ReadFile(h, contents, info.nFileSizeLow, &bytes_read, NULL) ||
585             bytes_read != info.nFileSizeLow)
586         {
587                 set_errno_from_GetLastError();
588                 ERROR_WITH_ERRNO("\"%ls\": Can't read data", path);
589                 CloseHandle(h);
590                 ret = WIMLIB_ERR_READ;
591                 goto out_free_contents;
592         }
593
594         CloseHandle(h);
595
596         if (info.nFileSizeLow < sizeof(struct WimOverlay_dat_header)) {
597                 ERROR("\"%ls\": File is unexpectedly small (only %"PRIu32" bytes)",
598                       path, (u32)info.nFileSizeLow);
599                 ret = WIMLIB_ERR_UNSUPPORTED;
600                 goto out_free_contents;
601         }
602
603         hdr = (const struct WimOverlay_dat_header *)contents;
604
605         if (hdr->magic != WIMOVERLAY_DAT_MAGIC ||
606             hdr->wim_provider_version != WIM_PROVIDER_CURRENT_VERSION ||
607             hdr->unknown_0x08 != 0x00000028 ||
608             (hdr->num_entries_1 != hdr->num_entries_2) ||
609             hdr->unknown_0x14 != 0x00000000)
610         {
611                 ERROR("\"%ls\": Header contains unexpected data:", path);
612                 if (wimlib_print_errors) {
613                         print_byte_field((const u8 *)hdr,
614                                          sizeof(struct WimOverlay_dat_header),
615                                          stderr);
616                         fputc('\n', stderr);
617                 }
618                 ret = WIMLIB_ERR_UNSUPPORTED;
619                 goto out_free_contents;
620         }
621
622         if ((u64)hdr->num_entries_1 * sizeof(struct WimOverlay_dat_entry_1) >
623             info.nFileSizeLow - sizeof(struct WimOverlay_dat_header))
624         {
625                 ERROR("\"%ls\": File is unexpectedly small "
626                       "(only %"PRIu32" bytes, but has %"PRIu32" entries)",
627                       path, (u32)info.nFileSizeLow, hdr->num_entries_1);
628                 ret = WIMLIB_ERR_UNSUPPORTED;
629                 goto out_free_contents;
630         }
631
632         for (u32 i = 0; i < hdr->num_entries_1; i++) {
633                 const struct WimOverlay_dat_entry_1 *entry_1;
634                 const struct WimOverlay_dat_entry_2 *entry_2;
635                 u32 wim_file_name_length;
636
637                 entry_1 = &hdr->entry_1s[i];
638
639                 if (((u64)entry_1->entry_2_offset +
640                      (u64)entry_1->entry_2_length) >
641                     info.nFileSizeLow)
642                 {
643                         ERROR("\"%ls\": entry %"PRIu32" (2) "
644                               "(data source ID 0x%016"PRIx64") "
645                               "overflows file",
646                               path, i, entry_1->data_source_id);
647                         ret = WIMLIB_ERR_UNSUPPORTED;
648                         goto out_free_contents;
649                 }
650                 if (entry_1->entry_2_length < sizeof(struct WimOverlay_dat_entry_2)) {
651                         ERROR("\"%ls\": entry %"PRIu32" (2) "
652                               "(data source ID 0x%016"PRIx64") "
653                               "is too short",
654                               path, i, entry_1->data_source_id);
655                         ret = WIMLIB_ERR_UNSUPPORTED;
656                         goto out_free_contents;
657                 }
658
659                 if (entry_1->entry_2_offset % 2 != 0) {
660                         ERROR("\"%ls\": entry %"PRIu32" (2) "
661                               "(data source ID 0x%016"PRIx64") "
662                               "is misaligned",
663                               path, i, entry_1->data_source_id);
664                         ret = WIMLIB_ERR_UNSUPPORTED;
665                         goto out_free_contents;
666                 }
667
668                 entry_2 = (const struct WimOverlay_dat_entry_2 *)
669                                 ((const u8 *)hdr + entry_1->entry_2_offset);
670
671                 wim_file_name_length = entry_1->entry_2_length -
672                                         sizeof(struct WimOverlay_dat_entry_2);
673                 if ((wim_file_name_length < 2 * sizeof(wchar_t)) ||
674                     (wim_file_name_length % sizeof(wchar_t) != 0) ||
675                     (wmemchr(entry_2->wim_file_name, L'\0',
676                              wim_file_name_length / sizeof(wchar_t))
677                      != &entry_2->wim_file_name[wim_file_name_length /
678                                                 sizeof(wchar_t) - 1]))
679                 {
680                         ERROR("\"%ls\": entry %"PRIu32" (2) "
681                               "(data source ID 0x%016"PRIx64") "
682                               "has invalid WIM file name",
683                               path, i, entry_1->data_source_id);
684                         if (wimlib_print_errors) {
685                                 print_byte_field((const u8 *)entry_2->wim_file_name,
686                                                  wim_file_name_length, stderr);
687                                 fputc('\n', stderr);
688                         }
689                         ret = WIMLIB_ERR_UNSUPPORTED;
690                         goto out_free_contents;
691                 }
692
693                 if (entry_2->unknown_0x00 != 0x00000000 ||
694                     entry_2->unknown_0x04 != 0x00000000 ||
695                     entry_2->unknown_0x0C != 0x00000000 ||
696                     entry_2->entry_2_length != entry_1->entry_2_length ||
697                     entry_2->unknown_0x10 != 0x00000005 ||
698                     entry_2->unknown_0x14 != 0x00000001 ||
699                     entry_2->inner_struct_size != entry_1->entry_2_length - 0x14 ||
700                     entry_2->unknown_0x1C != 0x00000005 ||
701                     entry_2->unknown_0x20 != 0x00000006 ||
702                     entry_2->unknown_0x24 != 0x00000000 ||
703                     entry_2->unknown_0x28 != 0x00000048 ||
704                     entry_2->unknown_0x2C != 0x00000000 ||
705                     entry_2->unknown_0x40 != 0x00000000 ||
706                     (entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_GPT &&
707                      entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_MBR) ||
708                     (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_MBR &&
709                      entry_2->partition.mbr.padding != 0) ||
710                     (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_GPT &&
711                      entry_2->partition.mbr.padding == 0) ||
712                     entry_2->unknown_0x58[0] != 0x00000000 ||
713                     entry_2->unknown_0x58[1] != 0x00000000 ||
714                     entry_2->unknown_0x58[2] != 0x00000000 ||
715                     entry_2->unknown_0x58[3] != 0x00000000)
716                 {
717                         ERROR("\"%ls\": entry %"PRIu32" (2) "
718                               "(data source ID 0x%016"PRIx64") "
719                               "contains unexpected data!",
720                               path, i, entry_1->data_source_id);
721                         if (wimlib_print_errors) {
722                                 print_byte_field((const u8 *)entry_2,
723                                                  entry_1->entry_2_length,
724                                                  stderr);
725                                 fputc('\n', stderr);
726                         }
727                         ret = WIMLIB_ERR_UNSUPPORTED;
728                         goto out_free_contents;
729                 }
730         }
731
732         *contents_ret = contents;
733         return 0;
734
735 out_free_contents:
736         FREE(contents);
737         return ret;
738 }
739
740 /*
741  * Update WimOverlay.dat manually in order to add a WIM data source to the
742  * target volume.
743  *
744  * THIS CODE IS EXPERIMENTAL AS I HAD TO REVERSE ENGINEER THE FILE FORMAT!
745  *
746  * @path
747  *      Target drive.  Must be a letter followed by a colon (e.g. D:).
748  * @wim_path
749  *      Absolute path to the WIM file.  It must begin with a drive letter; for
750  *      example, D:\install.wim.
751  * @wim_guid
752  *      GUID of the WIM, from the WIM header.
753  * @image
754  *      Number of the image in the WIM to specify in the new entry.
755  * @data_source_id_ret
756  *      On success, the allocated data source ID is returned here.
757  */
758 static int
759 update_wimoverlay_manually(const wchar_t *drive, const wchar_t *wim_path,
760                            const u8 wim_guid[WIM_GID_LEN],
761                            int image, u64 *data_source_id_ret)
762 {
763         wchar_t path_main[] = L"A:\\System Volume Information\\WimOverlay.dat";
764         wchar_t path_backup[] = L"A:\\System Volume Information\\WimOverlay.backup";
765         wchar_t path_wimlib_backup[] = L"A:\\System Volume Information\\WimOverlay.wimlib_backup";
766         wchar_t path_new[] = L"A:\\System Volume Information\\WimOverlay.wimlib_new";
767         void *old_contents;
768         void *new_contents;
769         u32 new_contents_size;
770         u64 new_data_source_id;
771         int ret;
772
773         wimlib_assert(drive[0] != L'\0' &&
774                       drive[1] == L':' &&
775                       drive[2] == L'\0');
776
777         path_main[0]          = drive[0];
778         path_backup[0]        = drive[0];
779         path_wimlib_backup[0] = drive[0];
780         path_new[0]           = drive[0];
781
782         ret = read_wimoverlay_dat(path_main, &old_contents);
783         if (ret)
784                 goto out;
785
786         ret = prepare_wimoverlay_dat(old_contents, wim_path, wim_guid, image,
787                                      &new_contents, &new_contents_size,
788                                      &new_data_source_id);
789         FREE(old_contents);
790         if (ret)
791                 goto out;
792
793         /* Write WimOverlay.wimlib_new  */
794         ret = write_wimoverlay_dat(path_new,
795                                    new_contents, new_contents_size);
796         if (ret)
797                 goto out_free_new_contents;
798
799         /* Write WimOverlay.backup  */
800         ret = write_wimoverlay_dat(path_backup,
801                                    new_contents, new_contents_size);
802         if (ret)
803                 goto out_free_new_contents;
804
805         if (old_contents) {
806                 /* Rename WimOverlay.dat => WimOverlay.wimlib_backup  */
807                 ret = win32_rename_replacement(path_main, path_wimlib_backup);
808                 if (ret) {
809                         ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
810                                          path_main, path_wimlib_backup);
811                         goto out_free_new_contents;
812                 }
813         }
814
815         /* Rename WimOverlay.wimlib_new => WimOverlay.dat  */
816         ret = win32_rename_replacement(path_new, path_main);
817         if (ret) {
818                 ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"",
819                                  path_new, path_main);
820         }
821 out_free_new_contents:
822         FREE(new_contents);
823 out:
824         if (ret == WIMLIB_ERR_UNSUPPORTED) {
825                 ERROR("Please report to developer ("PACKAGE_BUGREPORT").\n"
826                       "        If possible send the file \"%ls\".\n\n", path_main);
827         }
828         if (ret == 0)
829                 *data_source_id_ret = new_data_source_id;
830         return ret;
831 }
832
833 static int
834 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
835 {
836         tchar *file_abspath;
837
838         file_abspath = realpath(file_path, NULL);
839         if (!file_abspath)
840                 return WIMLIB_ERR_NOMEM;
841
842         if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
843                 ERROR("\"%ls\": Path format not recognized", file_abspath);
844                 FREE(file_abspath);
845                 return WIMLIB_ERR_UNSUPPORTED;
846         }
847
848         wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
849         FREE(file_abspath);
850         return 0;
851 }
852
853 /* Try to attach an instance of the Windows Overlay File System Filter Driver to
854  * the specified drive (such as C:)  */
855 static bool
856 try_to_attach_wof(const wchar_t *drive)
857 {
858         HMODULE fltlib;
859         bool retval = false;
860
861         /* Use FilterAttach() from Fltlib.dll.  */
862
863         fltlib = LoadLibrary(L"Fltlib.dll");
864
865         if (!fltlib) {
866                 WARNING("Failed to load Fltlib.dll");
867                 return retval;
868         }
869
870         HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName,
871                                             LPCWSTR lpVolumeName,
872                                             LPCWSTR lpInstanceName,
873                                             DWORD dwCreatedInstanceNameLength,
874                                             LPWSTR lpCreatedInstanceName);
875
876         func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach");
877
878         if (func_FilterAttach) {
879                 HRESULT res;
880
881                 res = (*func_FilterAttach)(L"WoF", drive, NULL, 0, NULL);
882
883                 if (res == S_OK)
884                         retval = true;
885         } else {
886                 WARNING("FilterAttach() does not exist in Fltlib.dll");
887         }
888
889         FreeLibrary(fltlib);
890
891         return retval;
892 }
893
894 /*
895  * Allocate a WOF data source ID for a WIM file.
896  *
897  * @wim_path
898  *      Absolute path to the WIM file.  This must include a drive letter and use
899  *      backslash path separators.
900  * @wim_guid
901  *      GUID of the WIM, from the WIM header.
902  * @image
903  *      Number of the image in the WIM being applied.
904  * @target
905  *      Path to the target drive.
906  * @data_source_id_ret
907  *      On success, an identifier for the backing WIM file will be returned
908  *      here.
909  *
910  * Returns 0 on success, or a positive error code on failure.
911  */
912 int
913 wimboot_alloc_data_source_id(const wchar_t *wim_path,
914                              const u8 wim_guid[WIM_GID_LEN],
915                              int image, const wchar_t *target,
916                              u64 *data_source_id_ret, bool *wof_running_ret)
917 {
918         tchar drive_path[7];
919         size_t wim_path_nchars;
920         size_t wim_file_name_length;
921         void *in;
922         size_t insize;
923         struct wof_external_info *wof_info;
924         struct wim_provider_add_overlay_input *wim_info;
925         HANDLE h;
926         u64 data_source_id;
927         DWORD bytes_returned;
928         int ret;
929         const wchar_t *prefix = L"\\??\\";
930         const size_t prefix_nchars = 4;
931         bool tried_to_attach_wof = false;
932
933         ret = win32_get_drive_path(target, drive_path);
934         if (ret)
935                 return ret;
936
937         wimlib_assert(!wcschr(wim_path, L'/'));
938         wimlib_assert(wim_path[0] != L'\0' && wim_path[1] == L':');
939
940         wim_path_nchars = wcslen(wim_path);
941         wim_file_name_length = sizeof(wchar_t) *
942                                (wim_path_nchars + prefix_nchars);
943
944         insize = sizeof(struct wof_external_info) +
945                  sizeof(struct wim_provider_add_overlay_input) +
946                  wim_file_name_length;
947
948         in = MALLOC(insize);
949         if (!in) {
950                 ret = WIMLIB_ERR_NOMEM;
951                 goto out;
952         }
953
954         wof_info = (struct wof_external_info *)in;
955         wof_info->version = WOF_CURRENT_VERSION;
956         wof_info->provider = WOF_PROVIDER_WIM;
957
958         wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1);
959         wim_info->wim_type = WIM_BOOT_NOT_OS_WIM;
960         wim_info->wim_index = image;
961         wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input,
962                                                   wim_file_name);
963         wim_info->wim_file_name_length = wim_file_name_length;
964         wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars);
965         wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
966
967 retry_ioctl:
968         h = open_file(drive_path, GENERIC_WRITE);
969
970         if (h == INVALID_HANDLE_VALUE) {
971                 set_errno_from_GetLastError();
972                 ERROR_WITH_ERRNO("Failed to open \"%ls\"", drive_path + 4);
973                 ret = WIMLIB_ERR_OPEN;
974                 goto out_free_in;
975         }
976
977         if (!DeviceIoControl(h, FSCTL_ADD_OVERLAY,
978                              in, insize,
979                              &data_source_id, sizeof(data_source_id),
980                              &bytes_returned, NULL))
981         {
982                 DWORD err = GetLastError();
983                 if (err == ERROR_INVALID_FUNCTION) {
984                         if (!tried_to_attach_wof) {
985                                 CloseHandle(h);
986                                 h = INVALID_HANDLE_VALUE;
987                                 tried_to_attach_wof = true;
988                                 if (try_to_attach_wof(drive_path + 4))
989                                         goto retry_ioctl;
990                         }
991                         ret = WIMLIB_ERR_UNSUPPORTED;
992                         goto out_close_handle;
993                 } else {
994                         set_errno_from_win32_error(err);
995                         ERROR_WITH_ERRNO("Failed to add overlay source \"%ls\" "
996                                          "to volume \"%ls\" (err=0x%08"PRIx32")",
997                                          wim_path, drive_path + 4, (u32)err);
998                         ret = WIMLIB_ERR_WIMBOOT;
999                         goto out_close_handle;
1000                 }
1001         }
1002
1003         if (bytes_returned != sizeof(data_source_id)) {
1004                 set_errno_from_win32_error(ERROR_INVALID_DATA);
1005                 ret = WIMLIB_ERR_WIMBOOT;
1006                 ERROR("Unexpected result size when adding "
1007                       "overlay source \"%ls\" to volume \"%ls\"",
1008                       wim_path, drive_path + 4);
1009                 goto out_close_handle;
1010         }
1011
1012         *wof_running_ret = true;
1013         *data_source_id_ret = data_source_id;
1014         ret = 0;
1015
1016 out_close_handle:
1017         CloseHandle(h);
1018 out_free_in:
1019         FREE(in);
1020 out:
1021         if (ret == WIMLIB_ERR_UNSUPPORTED) {
1022         #if 0
1023                 WARNING(
1024                 "The version of Windows you are running does not appear to support\n"
1025                 "        the Windows Overlay File System Filter Driver.  This is normally\n"
1026                 "        available on Windows 8.1 Update 1 or later.  Therefore, wimlib\n"
1027                 "        will attempt to update the WimOverlay.dat file directly.\n");
1028         #else
1029                 WARNING("WOF driver is not available; updating WimOverlay.dat directly.");
1030         #endif
1031                 ret = update_wimoverlay_manually(drive_path + 4, wim_path,
1032                                                  wim_guid, image,
1033                                                  data_source_id_ret);
1034                 *wof_running_ret = false;
1035         }
1036         return ret;
1037 }
1038
1039
1040 /*
1041  * Set WIMBoot information on the specified file.
1042  *
1043  * This turns it into a reparse point that redirects accesses to it, to the
1044  * corresponding resource in the WIM archive.
1045  *
1046  * @path
1047  *      Path to extracted file (already created).
1048  * @lte
1049  *      Unnamed data stream of the file.
1050  * @data_source_id
1051  *      Allocated identifier for the WIM data source on the destination volume.
1052  * @lookup_table_hash
1053  *      SHA-1 message digest of the WIM's lookup table.
1054  * @wof_running
1055  *      %true if the WOF driver appears to be available and working; %false if
1056  *      not.
1057  *
1058  * Returns 0 on success, or a positive error code on failure.
1059  */
1060 int
1061 wimboot_set_pointer(const wchar_t *path,
1062                     const struct wim_lookup_table_entry *lte,
1063                     u64 data_source_id,
1064                     const u8 lookup_table_hash[SHA1_HASH_SIZE],
1065                     bool wof_running)
1066 {
1067         HANDLE h;
1068         DWORD bytes_returned;
1069         int ret;
1070
1071
1072         /* Open the file  */
1073         h = win32_open_existing_file(path, GENERIC_WRITE);
1074         if (h == INVALID_HANDLE_VALUE) {
1075                 set_errno_from_GetLastError();
1076                 ret = WIMLIB_ERR_OPEN;
1077                 goto out;
1078         }
1079
1080         if (wof_running) {
1081                 /* The WOF driver is running.  We can create the reparse point
1082                  * using FSCTL_SET_EXTERNAL_BACKING.  */
1083
1084                 struct {
1085                         struct wof_external_info wof_info;
1086                         struct wim_provider_external_info wim_info;
1087                 } in;
1088
1089                 in.wof_info.version = WOF_CURRENT_VERSION;
1090                 in.wof_info.provider = WOF_PROVIDER_WIM;
1091
1092                 in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
1093                 in.wim_info.flags = 0;
1094                 in.wim_info.data_source_id = data_source_id;
1095                 copy_hash(in.wim_info.resource_hash, lte->hash);
1096
1097                 /* lookup_table_hash is not necessary  */
1098
1099                 if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
1100                                      &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
1101                         goto fail;
1102         } else {
1103
1104                 /* The WOF driver is running.  We need to create the reparse
1105                  * point manually.  */
1106
1107                 struct {
1108                         struct {
1109                                 le32 rptag;
1110                                 le16 rpdatalen;
1111                                 le16 rpreserved;
1112                         } hdr;
1113                         struct wof_external_info wof_info;
1114                         struct wim_provider_rpdata wim_info;
1115                 } in;
1116
1117                 BUILD_BUG_ON(sizeof(in) != 8 +
1118                              sizeof(struct wof_external_info) +
1119                              sizeof(struct wim_provider_rpdata));
1120
1121                 in.hdr.rptag = WIMLIB_REPARSE_TAG_WOF;
1122                 in.hdr.rpdatalen = sizeof(in) - sizeof(in.hdr);
1123                 in.hdr.rpreserved = 0;
1124
1125                 in.wof_info.version = WOF_CURRENT_VERSION;
1126                 in.wof_info.provider = WOF_PROVIDER_WIM;
1127
1128                 in.wim_info.version = 2;
1129                 in.wim_info.flags = 0;
1130                 in.wim_info.data_source_id = data_source_id;
1131                 copy_hash(in.wim_info.resource_hash, lte->hash);
1132                 copy_hash(in.wim_info.wim_lookup_table_hash, lookup_table_hash);
1133                 in.wim_info.stream_uncompressed_size = lte->size;
1134                 in.wim_info.stream_compressed_size = lte->rspec->size_in_wim;
1135                 in.wim_info.stream_offset_in_wim = lte->rspec->offset_in_wim;
1136
1137                 if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
1138                                      &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
1139                         goto fail;
1140
1141                 /* We also need to create an unnamed data stream of the correct
1142                  * size.  Otherwise the file shows up as zero length.  It can be
1143                  * a sparse stream containing all zeroes; its contents
1144                  * are unimportant.  */
1145                 if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
1146                                      &bytes_returned, NULL))
1147                         goto fail;
1148
1149                 if (!SetFilePointerEx(h,
1150                                       (LARGE_INTEGER){ .QuadPart = lte->size},
1151                                       NULL, FILE_BEGIN))
1152                         goto fail;
1153
1154                 if (!SetEndOfFile(h))
1155                         goto fail;
1156         }
1157
1158         ret = 0;
1159 out_close_handle:
1160         CloseHandle(h);
1161 out:
1162         return ret;
1163
1164 fail:
1165         {
1166                 DWORD err = GetLastError();
1167                 set_errno_from_win32_error(err);
1168                 ERROR_WITH_ERRNO("\"%ls\": Couldn't set WIMBoot pointer data "
1169                                  "(err=0x%08"PRIx32")", path, (u32)err);
1170                 ret = WIMLIB_ERR_WIMBOOT;
1171                 goto out_close_handle;
1172         }
1173 }
1174
1175 #endif /* __WIN32__ */