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