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