]> wimlib.net Git - wimlib/blob - src/win32_apply.c
v1.14.4
[wimlib] / src / win32_apply.c
1 /*
2  * win32_apply.c - Windows-specific code for applying files from a WIM image.
3  */
4
5 /*
6  * Copyright (C) 2013-2021 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21
22 #ifdef __WIN32__
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "wimlib/win32_common.h"
29
30 #include "wimlib/apply.h"
31 #include "wimlib/assert.h"
32 #include "wimlib/blob_table.h"
33 #include "wimlib/dentry.h"
34 #include "wimlib/encoding.h"
35 #include "wimlib/error.h"
36 #include "wimlib/metadata.h"
37 #include "wimlib/object_id.h"
38 #include "wimlib/paths.h"
39 #include "wimlib/pattern.h"
40 #include "wimlib/reparse.h"
41 #include "wimlib/scan.h" /* for mangle_pat() and match_pattern_list()  */
42 #include "wimlib/textfile.h"
43 #include "wimlib/wimboot.h"
44 #include "wimlib/wof.h"
45 #include "wimlib/xattr.h"
46 #include "wimlib/xml.h"
47
48 struct win32_apply_ctx {
49
50         /* Extract flags, the pointer to the WIMStruct, etc.  */
51         struct apply_ctx common;
52
53         /* WIMBoot information, only filled in if WIMLIB_EXTRACT_FLAG_WIMBOOT
54          * was provided  */
55         struct {
56                 /* This array contains the WIM files registered with WOF on the
57                  * target volume for this extraction operation.  All WIMStructs
58                  * in this array are distinct and have ->filename != NULL.  */
59                 struct wimboot_wim {
60                         WIMStruct *wim;
61                         u64 data_source_id;
62                         u8 blob_table_hash[SHA1_HASH_SIZE];
63                 } *wims;
64                 size_t num_wims;
65                 bool wof_running;
66                 bool have_wrong_version_wims;
67                 bool have_uncompressed_wims;
68                 bool have_unsupported_compressed_resources;
69                 bool have_huge_resources;
70         } wimboot;
71
72         /* External backing information  */
73         struct string_list *prepopulate_pats;
74         void *mem_prepopulate_pats;
75         bool tried_to_load_prepopulate_list;
76
77         /* Open handle to the target directory  */
78         HANDLE h_target;
79
80         /* NT namespace path to the target directory (buffer allocated)  */
81         UNICODE_STRING target_ntpath;
82
83         /* Temporary buffer for building paths (buffer allocated)  */
84         UNICODE_STRING pathbuf;
85
86         /* Object attributes to reuse for opening files in the target directory.
87          * (attr.ObjectName == &pathbuf) and (attr.RootDirectory == h_target).
88          */
89         OBJECT_ATTRIBUTES attr;
90
91         /* Temporary I/O status block for system calls  */
92         IO_STATUS_BLOCK iosb;
93
94         /* Allocated buffer for creating "printable" paths from our
95          * target-relative NT paths  */
96         wchar_t *print_buffer;
97
98         /* Allocated buffer for reading blob data when it cannot be extracted
99          * directly  */
100         u8 *data_buffer;
101
102         /* Pointer to the next byte in @data_buffer to fill  */
103         u8 *data_buffer_ptr;
104
105         /* Size allocated in @data_buffer  */
106         size_t data_buffer_size;
107
108         /* Current offset in the raw encrypted file being written  */
109         size_t encrypted_offset;
110
111         /* Current size of the raw encrypted file being written  */
112         size_t encrypted_size;
113
114         /* Temporary buffer for reparse data  */
115         struct reparse_buffer_disk rpbuf;
116
117         /* Temporary buffer for reparse data of "fixed" absolute symbolic links
118          * and junctions  */
119         struct reparse_buffer_disk rpfixbuf;
120
121         /* Array of open handles to filesystem streams currently being written
122          */
123         HANDLE open_handles[MAX_OPEN_FILES];
124
125         /* Number of handles in @open_handles currently open (filled in from the
126          * beginning of the array)  */
127         unsigned num_open_handles;
128
129         /* For each currently open stream, whether we're writing to it in
130          * "sparse" mode or not.  */
131         bool is_sparse_stream[MAX_OPEN_FILES];
132
133         /* Whether is_sparse_stream[] is true for any currently open stream  */
134         bool any_sparse_streams;
135
136         /* List of dentries, joined by @d_tmp_list, that need to have reparse
137          * data extracted as soon as the whole blob has been read into
138          * @data_buffer.  */
139         struct list_head reparse_dentries;
140
141         /* List of dentries, joined by @d_tmp_list, that need to have raw
142          * encrypted data extracted as soon as the whole blob has been read into
143          * @data_buffer.  */
144         struct list_head encrypted_dentries;
145
146         /* Number of files for which we didn't have permission to set the full
147          * security descriptor.  */
148         unsigned long partial_security_descriptors;
149
150         /* Number of files for which we didn't have permission to set any part
151          * of the security descriptor.  */
152         unsigned long no_security_descriptors;
153
154         /* Number of files for which we couldn't set the short name.  */
155         unsigned long num_set_short_name_failures;
156
157         /* Number of files for which we couldn't remove the short name.  */
158         unsigned long num_remove_short_name_failures;
159
160         /* Number of files on which we couldn't set System Compression.  */
161         unsigned long num_system_compression_failures;
162
163         /* The number of files which, for compatibility with the Windows
164          * bootloader, were not compressed using the requested system
165          * compression format.  This includes matches with the hardcoded pattern
166          * list only; it does not include matches with patterns in
167          * [PrepopulateList].  */
168         unsigned long num_system_compression_exclusions;
169
170         /* Number of files for which we couldn't set the object ID.  */
171         unsigned long num_object_id_failures;
172
173         /* Number of files for which we couldn't set extended attributes.  */
174         unsigned long num_xattr_failures;
175
176         /* The Windows build number of the image being applied, or 0 if unknown.
177          */
178         u64 windows_build_number;
179
180         /* Have we tried to enable short name support on the target volume yet?
181          */
182         bool tried_to_enable_short_names;
183 };
184
185 /* Get the drive letter from a Windows path, or return the null character if the
186  * path is relative.  */
187 static wchar_t
188 get_drive_letter(const wchar_t *path)
189 {
190         /* Skip \\?\ prefix  */
191         if (!wcsncmp(path, L"\\\\?\\", 4))
192                 path += 4;
193
194         /* Return drive letter if valid  */
195         if (((path[0] >= L'a' && path[0] <= L'z') ||
196              (path[0] >= L'A' && path[0] <= L'Z')) && path[1] == L':')
197                 return path[0];
198
199         return L'\0';
200 }
201
202 static void
203 get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
204               bool *short_names_supported_ret)
205 {
206         wchar_t filesystem_name[MAX_PATH + 1];
207         wchar_t drive[4];
208         wchar_t *volume = NULL;
209
210         *vol_flags_ret = 0;
211         *short_names_supported_ret = false;
212
213         drive[0] = get_drive_letter(target);
214         if (drive[0]) {
215                 drive[1] = L':';
216                 drive[2] = L'\\';
217                 drive[3] = L'\0';
218                 volume = drive;
219         }
220
221         if (!GetVolumeInformation(volume, NULL, 0, NULL, NULL,
222                                   vol_flags_ret, filesystem_name,
223                                   ARRAY_LEN(filesystem_name)))
224         {
225                 win32_warning(GetLastError(),
226                               L"Failed to get volume information for \"%ls\"",
227                               target);
228                 return;
229         }
230
231         if (wcsstr(filesystem_name, L"NTFS")) {
232                 /*
233                  * FILE_SUPPORTS_HARD_LINKS and
234                  * FILE_SUPPORTS_EXTENDED_ATTRIBUTES are only supported on
235                  * Windows 7 and later.  Force them on anyway if the filesystem
236                  * is NTFS.
237                  */
238                 *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS;
239                 *vol_flags_ret |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES;
240
241                 /* There's no volume flag for short names, but according to the
242                  * MS documentation they are only user-settable on NTFS.  */
243                 *short_names_supported_ret = true;
244         }
245 }
246
247 static const wchar_t *
248 current_path(struct win32_apply_ctx *ctx);
249
250 static void
251 build_extraction_path(const struct wim_dentry *dentry,
252                       struct win32_apply_ctx *ctx);
253
254 static int
255 report_dentry_apply_error(const struct wim_dentry *dentry,
256                           struct win32_apply_ctx *ctx, int ret)
257 {
258         build_extraction_path(dentry, ctx);
259         return report_apply_error(&ctx->common, ret, current_path(ctx));
260 }
261
262 static inline int
263 check_apply_error(const struct wim_dentry *dentry,
264                   struct win32_apply_ctx *ctx, int ret)
265 {
266         if (unlikely(ret))
267                 ret = report_dentry_apply_error(dentry, ctx, ret);
268         return ret;
269 }
270
271 static int
272 win32_get_supported_features(const wchar_t *target,
273                              struct wim_features *supported_features)
274 {
275         DWORD vol_flags;
276         bool short_names_supported;
277
278         /* Query the features of the target volume.  */
279
280         get_vol_flags(target, &vol_flags, &short_names_supported);
281
282         supported_features->readonly_files = 1;
283         supported_features->hidden_files = 1;
284         supported_features->system_files = 1;
285         supported_features->archive_files = 1;
286
287         if (vol_flags & FILE_FILE_COMPRESSION)
288                 supported_features->compressed_files = 1;
289
290         if (vol_flags & FILE_SUPPORTS_ENCRYPTION) {
291                 supported_features->encrypted_files = 1;
292                 supported_features->encrypted_directories = 1;
293         }
294
295         supported_features->not_context_indexed_files = 1;
296
297         if (vol_flags & FILE_SUPPORTS_SPARSE_FILES)
298                 supported_features->sparse_files = 1;
299
300         if (vol_flags & FILE_NAMED_STREAMS)
301                 supported_features->named_data_streams = 1;
302
303         if (vol_flags & FILE_SUPPORTS_HARD_LINKS)
304                 supported_features->hard_links = 1;
305
306         if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS)
307                 supported_features->reparse_points = 1;
308
309         if (vol_flags & FILE_PERSISTENT_ACLS)
310                 supported_features->security_descriptors = 1;
311
312         if (short_names_supported)
313                 supported_features->short_names = 1;
314
315         if (vol_flags & FILE_SUPPORTS_OBJECT_IDS)
316                 supported_features->object_ids = 1;
317
318         supported_features->timestamps = 1;
319
320         if (vol_flags & FILE_CASE_SENSITIVE_SEARCH) {
321                 /*
322                  * The filesystem supports case-sensitive filenames.  But does
323                  * the operating system as well?  This normally requires the
324                  * registry setting ObCaseInsensitive=0.  We can test it
325                  * indirectly by attempting to open the "\SystemRoot" symbolic
326                  * link using a name with the wrong case.  If we get
327                  * STATUS_OBJECT_NAME_NOT_FOUND instead of STATUS_ACCESS_DENIED,
328                  * then case-sensitive names must be enabled.
329                  */
330                 UNICODE_STRING path;
331                 OBJECT_ATTRIBUTES attr;
332                 HANDLE h;
333                 NTSTATUS status;
334
335                 RtlInitUnicodeString(&path, L"\\systemroot");
336                 InitializeObjectAttributes(&attr, &path, 0, NULL, NULL);
337
338                 status = NtOpenSymbolicLinkObject(&h, 0, &attr);
339                 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
340                         supported_features->case_sensitive_filenames = 1;
341         }
342
343         if (vol_flags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)
344                 supported_features->xattrs = 1;
345
346         return 0;
347 }
348
349 #define COMPACT_FLAGS   (WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K |         \
350                          WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K |         \
351                          WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K |        \
352                          WIMLIB_EXTRACT_FLAG_COMPACT_LZX)
353
354
355
356 /*
357  * If not done already, load the patterns from the [PrepopulateList] section of
358  * WimBootCompress.ini in the WIM image being extracted.
359  *
360  * Note: WimBootCompress.ini applies to both types of "external backing":
361  *
362  *      - WIM backing ("WIMBoot" - Windows 8.1 and later)
363  *      - File backing ("System Compression" - Windows 10 and later)
364  */
365 static int
366 load_prepopulate_pats(struct win32_apply_ctx *ctx)
367 {
368         const wchar_t *path = L"\\Windows\\System32\\WimBootCompress.ini";
369         struct wim_dentry *dentry;
370         const struct blob_descriptor *blob;
371         int ret;
372         void *buf;
373         struct string_list *strings;
374         void *mem;
375         struct text_file_section sec;
376
377         if (ctx->tried_to_load_prepopulate_list)
378                 return 0;
379
380         ctx->tried_to_load_prepopulate_list = true;
381
382         dentry = get_dentry(ctx->common.wim, path, WIMLIB_CASE_INSENSITIVE);
383         if (!dentry ||
384             (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
385                                               FILE_ATTRIBUTE_REPARSE_POINT |
386                                               FILE_ATTRIBUTE_ENCRYPTED)) ||
387             !(blob = inode_get_blob_for_unnamed_data_stream(dentry->d_inode,
388                                                             ctx->common.wim->blob_table)))
389         {
390                 WARNING("%ls does not exist in the WIM image.\n"
391                         "          The default configuration will be used instead; it assumes that all\n"
392                         "          files are valid for external backing regardless of path, equivalent\n"
393                         "          to an empty [PrepopulateList] section.", path);
394                 return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
395         }
396
397         ret = read_blob_into_alloc_buf(blob, &buf);
398         if (ret)
399                 return ret;
400
401         strings = CALLOC(1, sizeof(struct string_list));
402         if (!strings) {
403                 FREE(buf);
404                 return WIMLIB_ERR_NOMEM;
405         }
406
407         sec.name = T("PrepopulateList");
408         sec.strings = strings;
409
410         ret = load_text_file(path, buf, blob->size, &mem, &sec, 1,
411                              LOAD_TEXT_FILE_REMOVE_QUOTES |
412                              LOAD_TEXT_FILE_NO_WARNINGS,
413                              mangle_pat);
414         STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR);
415         FREE(buf);
416         if (ret) {
417                 FREE(strings);
418                 return ret;
419         }
420         ctx->prepopulate_pats = strings;
421         ctx->mem_prepopulate_pats = mem;
422         return 0;
423 }
424
425 /* Returns %true if the specified absolute path to a file in the WIM image can
426  * be subject to external backing when extracted.  Otherwise returns %false.  */
427 static bool
428 can_externally_back_path(const wchar_t *path, const struct win32_apply_ctx *ctx)
429 {
430         /* Does the path match a pattern given in the [PrepopulateList] section
431          * of WimBootCompress.ini?  */
432         if (ctx->prepopulate_pats && match_pattern_list(path, ctx->prepopulate_pats,
433                                                         MATCH_RECURSIVELY))
434                 return false;
435
436         /* Since we attempt to modify the SYSTEM registry after it's extracted
437          * (see end_wimboot_extraction()), it can't be extracted as externally
438          * backed.  This extends to associated files such as SYSTEM.LOG that
439          * also must be writable in order to write to the registry.  Normally,
440          * SYSTEM is in [PrepopulateList], and the SYSTEM.* files match patterns
441          * in [ExclusionList] and therefore are not captured in the WIM at all.
442          * However, a WIM that wasn't specifically captured in "WIMBoot mode"
443          * may contain SYSTEM.* files.  So to make things "just work", hard-code
444          * the pattern.  */
445         if (match_path(path, L"\\Windows\\System32\\config\\SYSTEM*", 0))
446                 return false;
447
448         return true;
449 }
450
451 /* Can the specified WIM resource be used as the source of an external backing
452  * for the wof.sys WIM provider?  */
453 static bool
454 is_resource_valid_for_external_backing(const struct wim_resource_descriptor *rdesc,
455                                        struct win32_apply_ctx *ctx)
456 {
457         /* Must be the original WIM file format.  This check excludes pipable
458          * resources and solid resources.  It also excludes other resources
459          * contained in such files even if they would be otherwise compatible.
460          */
461         if (rdesc->wim->hdr.magic != WIM_MAGIC ||
462             rdesc->wim->hdr.wim_version != WIM_VERSION_DEFAULT)
463         {
464                 ctx->wimboot.have_wrong_version_wims = true;
465                 return false;
466         }
467
468         /*
469          * Whitelist of compression types and chunk sizes supported by
470          * Microsoft's WOF driver.
471          *
472          * Notes:
473          *    - Uncompressed WIMs result in BSOD.  However, this only applies to
474          *      the WIM file itself, not to uncompressed resources in a WIM file
475          *      that is otherwise compressed.
476          *    - XPRESS 64K sometimes appears to work, but sometimes it causes
477          *      reads to fail with STATUS_UNSUCCESSFUL.
478          */
479         switch (rdesc->compression_type) {
480         case WIMLIB_COMPRESSION_TYPE_NONE:
481                 if (rdesc->wim->compression_type == WIMLIB_COMPRESSION_TYPE_NONE) {
482                         ctx->wimboot.have_uncompressed_wims = true;
483                         return false;
484                 }
485                 break;
486         case WIMLIB_COMPRESSION_TYPE_XPRESS:
487                 switch (rdesc->chunk_size) {
488                 case 4096:
489                 case 8192:
490                 case 16384:
491                 case 32768:
492                         break;
493                 default:
494                         ctx->wimboot.have_unsupported_compressed_resources = true;
495                         return false;
496                 }
497                 break;
498         case WIMLIB_COMPRESSION_TYPE_LZX:
499                 switch (rdesc->chunk_size) {
500                 case 32768:
501                         break;
502                 default:
503                         ctx->wimboot.have_unsupported_compressed_resources = true;
504                         return false;
505                 }
506                 break;
507         default:
508                 ctx->wimboot.have_unsupported_compressed_resources = true;
509                 return false;
510         }
511
512         /* Microsoft's WoF driver errors out if it tries to satisfy a read with
513          * ending offset >= 4 GiB from an externally backed file.  */
514         if (rdesc->uncompressed_size > 4200000000) {
515                 ctx->wimboot.have_huge_resources = true;
516                 return false;
517         }
518
519         return true;
520 }
521
522 #define EXTERNAL_BACKING_NOT_ENABLED            -1
523 #define EXTERNAL_BACKING_NOT_POSSIBLE           -2
524 #define EXTERNAL_BACKING_EXCLUDED               -3
525
526 /*
527  * Determines whether the specified file will be externally backed.  Returns a
528  * negative status code if no, 0 if yes, or a positive wimlib error code on
529  * error.  If the file is excluded from external backing based on its path, then
530  * *excluded_dentry_ret is set to the dentry for the path that matched the
531  * exclusion rule.
532  *
533  * Note that this logic applies to both types of "external backing":
534  *
535  *      - WIM backing ("WIMBoot" - Windows 8.1 and later)
536  *      - File backing ("System Compression" - Windows 10 and later)
537  *
538  * However, in the case of WIM backing we also need to validate that the WIM
539  * resource that would be the source of the backing is supported by the wof.sys
540  * WIM provider.
541  */
542 static int
543 will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx,
544                            const struct wim_dentry **excluded_dentry_ret,
545                            bool wimboot_mode)
546 {
547         struct wim_dentry *dentry;
548         struct blob_descriptor *blob;
549         int ret;
550
551         if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM)
552                 return WIMLIB_ERR_NOMEM;
553
554         if (inode->i_can_externally_back)
555                 return 0;
556
557         /* This may do redundant checks because the cached value
558          * i_can_externally_back is 2-state (as opposed to 3-state:
559          * unknown/no/yes).  But most files can be externally backed, so this
560          * way is fine.  */
561
562         if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
563                                    FILE_ATTRIBUTE_REPARSE_POINT |
564                                    FILE_ATTRIBUTE_ENCRYPTED))
565                 return EXTERNAL_BACKING_NOT_POSSIBLE;
566
567         blob = inode_get_blob_for_unnamed_data_stream_resolved(inode);
568
569         if (!blob)
570                 return EXTERNAL_BACKING_NOT_POSSIBLE;
571
572         if (wimboot_mode &&
573             (blob->blob_location != BLOB_IN_WIM ||
574              !is_resource_valid_for_external_backing(blob->rdesc, ctx)))
575                 return EXTERNAL_BACKING_NOT_POSSIBLE;
576
577         /*
578          * We need to check the patterns in [PrepopulateList] against every name
579          * of the inode, in case any of them match.
580          */
581
582         inode_for_each_extraction_alias(dentry, inode) {
583
584                 ret = calculate_dentry_full_path(dentry);
585                 if (ret)
586                         return ret;
587
588                 if (!can_externally_back_path(dentry->d_full_path, ctx)) {
589                         if (excluded_dentry_ret)
590                                 *excluded_dentry_ret = dentry;
591                         return EXTERNAL_BACKING_EXCLUDED;
592                 }
593         }
594
595         inode->i_can_externally_back = 1;
596         return 0;
597 }
598
599 /*
600  * Determines if the unnamed data stream of a file will be created as a WIM
601  * external backing (a "WIMBoot pointer file"), as opposed to a standard
602  * extraction.
603  */
604 static int
605 win32_will_back_from_wim(struct wim_dentry *dentry, struct apply_ctx *_ctx)
606 {
607         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
608
609         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
610                 return EXTERNAL_BACKING_NOT_ENABLED;
611
612         return will_externally_back_inode(dentry->d_inode, ctx, NULL, true);
613 }
614
615 /* Find the WOF registration information for the specified WIM file.  */
616 static struct wimboot_wim *
617 find_wimboot_wim(WIMStruct *wim_to_find, struct win32_apply_ctx *ctx)
618 {
619         for (size_t i = 0; i < ctx->wimboot.num_wims; i++)
620                 if (wim_to_find == ctx->wimboot.wims[i].wim)
621                         return &ctx->wimboot.wims[i];
622
623         wimlib_assert(0);
624         return NULL;
625 }
626
627 static int
628 set_backed_from_wim(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *ctx)
629 {
630         int ret;
631         const struct wim_dentry *excluded_dentry;
632         const struct blob_descriptor *blob;
633         const struct wimboot_wim *wimboot_wim;
634
635         ret = will_externally_back_inode(inode, ctx, &excluded_dentry, true);
636         if (ret > 0) /* Error.  */
637                 return ret;
638
639         if (ret < 0 && ret != EXTERNAL_BACKING_EXCLUDED)
640                 return 0; /* Not externally backing, other than due to exclusion.  */
641
642         if (unlikely(ret == EXTERNAL_BACKING_EXCLUDED)) {
643                 /* Not externally backing due to exclusion.  */
644                 union wimlib_progress_info info;
645
646                 build_extraction_path(excluded_dentry, ctx);
647
648                 info.wimboot_exclude.path_in_wim = excluded_dentry->d_full_path;
649                 info.wimboot_exclude.extraction_path = current_path(ctx);
650
651                 return call_progress(ctx->common.progfunc,
652                                      WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
653                                      &info, ctx->common.progctx);
654         }
655
656         /* Externally backing.  */
657
658         blob = inode_get_blob_for_unnamed_data_stream_resolved(inode);
659         wimboot_wim = find_wimboot_wim(blob->rdesc->wim, ctx);
660
661         if (unlikely(!wimboot_set_pointer(h,
662                                           blob,
663                                           wimboot_wim->data_source_id,
664                                           wimboot_wim->blob_table_hash,
665                                           ctx->wimboot.wof_running)))
666         {
667                 const DWORD err = GetLastError();
668
669                 build_extraction_path(inode_first_extraction_dentry(inode), ctx);
670                 win32_error(err, L"\"%ls\": Couldn't set WIMBoot pointer data",
671                             current_path(ctx));
672                 return WIMLIB_ERR_WIMBOOT;
673         }
674         return 0;
675 }
676
677 /* Calculates the SHA-1 message digest of the WIM's blob table.  */
678 static int
679 hash_blob_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE])
680 {
681         return wim_reshdr_to_hash(&wim->hdr.blob_table_reshdr, wim, hash);
682 }
683
684 static int
685 register_wim_with_wof(WIMStruct *wim, struct win32_apply_ctx *ctx)
686 {
687         struct wimboot_wim *p;
688         int ret;
689
690         /* Check if already registered  */
691         for (size_t i = 0; i < ctx->wimboot.num_wims; i++)
692                 if (wim == ctx->wimboot.wims[i].wim)
693                         return 0;
694
695         /* Not yet registered  */
696
697         p = REALLOC(ctx->wimboot.wims,
698                     (ctx->wimboot.num_wims + 1) * sizeof(ctx->wimboot.wims[0]));
699         if (!p)
700                 return WIMLIB_ERR_NOMEM;
701         ctx->wimboot.wims = p;
702
703         ctx->wimboot.wims[ctx->wimboot.num_wims].wim = wim;
704
705         ret = hash_blob_table(wim, ctx->wimboot.wims[ctx->wimboot.num_wims].blob_table_hash);
706         if (ret)
707                 return ret;
708
709         ret = wimboot_alloc_data_source_id(wim->filename,
710                                            wim->hdr.guid,
711                                            ctx->common.wim->current_image,
712                                            ctx->common.target,
713                                            &ctx->wimboot.wims[ctx->wimboot.num_wims].data_source_id,
714                                            &ctx->wimboot.wof_running);
715         if (ret)
716                 return ret;
717
718         ctx->wimboot.num_wims++;
719         return 0;
720 }
721
722 /* Prepare for doing a "WIMBoot" extraction by registering each source WIM file
723  * with WOF on the target volume.  */
724 static int
725 start_wimboot_extraction(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
726 {
727         int ret;
728         struct wim_dentry *dentry;
729
730         if (!xml_get_wimboot(ctx->common.wim->xml_info,
731                              ctx->common.wim->current_image))
732                 WARNING("The WIM image is not marked as WIMBoot compatible.  This usually\n"
733                         "          means it is not intended to be used to back a Windows operating\n"
734                         "          system.  Proceeding anyway.");
735
736         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
737                 struct blob_descriptor *blob;
738
739                 ret = win32_will_back_from_wim(dentry, &ctx->common);
740                 if (ret > 0) /* Error */
741                         return ret;
742                 if (ret < 0) /* Won't externally back */
743                         continue;
744
745                 blob = inode_get_blob_for_unnamed_data_stream_resolved(dentry->d_inode);
746                 ret = register_wim_with_wof(blob->rdesc->wim, ctx);
747                 if (ret)
748                         return ret;
749         }
750
751         if (ctx->wimboot.have_wrong_version_wims) {
752   WARNING("At least one of the source WIM files uses a version of the WIM\n"
753 "          file format that not supported by Microsoft's wof.sys driver.\n"
754 "          Files whose data is contained in one of these WIM files will be\n"
755 "          extracted as full files rather than externally backed.");
756         }
757
758         if (ctx->wimboot.have_uncompressed_wims) {
759   WARNING("At least one of the source WIM files is uncompressed.  Files whose\n"
760 "          data is contained in an uncompressed WIM file will be extracted as\n"
761 "          full files rather than externally backed, since uncompressed WIM\n"
762 "          files are not supported by Microsoft's wof.sys driver.");
763         }
764
765         if (ctx->wimboot.have_unsupported_compressed_resources) {
766   WARNING("At least one of the source WIM files uses a compression format that\n"
767 "          is not supported by Microsoft's wof.sys driver.  Files whose data is\n"
768 "          contained in a compressed resource in one of these WIM files will be\n"
769 "          extracted as full files rather than externally backed.  (The\n"
770 "          compression formats supported by wof.sys are: XPRESS 4K, XPRESS 8K,\n"
771 "          XPRESS 16K, XPRESS 32K, and LZX 32K.)");
772         }
773
774         if (ctx->wimboot.have_huge_resources) {
775   WARNING("Some files exceeded 4.2 GB in size.  Such files will be extracted\n"
776 "          as full files rather than externally backed, since very large files\n"
777 "          are not supported by Microsoft's wof.sys driver.");
778         }
779
780         return 0;
781 }
782
783 static void
784 build_win32_extraction_path(const struct wim_dentry *dentry,
785                             struct win32_apply_ctx *ctx);
786
787 /* Sets WimBoot=1 in the extracted SYSTEM registry hive.
788  *
789  * WIMGAPI does this, and it's possible that it's important.
790  * But I don't know exactly what this value means to Windows.  */
791 static int
792 end_wimboot_extraction(struct win32_apply_ctx *ctx)
793 {
794         struct wim_dentry *dentry;
795         wchar_t subkeyname[32];
796         LONG res;
797         LONG res2;
798         HKEY key;
799         DWORD value;
800
801         dentry = get_dentry(ctx->common.wim, L"\\Windows\\System32\\config\\SYSTEM",
802                             WIMLIB_CASE_INSENSITIVE);
803
804         if (!dentry || !will_extract_dentry(dentry))
805                 goto out;
806
807         if (!will_extract_dentry(wim_get_current_root_dentry(ctx->common.wim)))
808                 goto out;
809
810         /* Not bothering to use the native routines (e.g. NtLoadKey()) for this.
811          * If this doesn't work, you probably also have many other problems.  */
812
813         build_win32_extraction_path(dentry, ctx);
814
815         get_random_alnum_chars(subkeyname, 20);
816         subkeyname[20] = L'\0';
817
818         res = RegLoadKey(HKEY_LOCAL_MACHINE, subkeyname, ctx->pathbuf.Buffer);
819         if (res)
820                 goto out_check_res;
821
822         wcscpy(&subkeyname[20], L"\\Setup");
823
824         res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, subkeyname, 0, NULL,
825                              REG_OPTION_BACKUP_RESTORE, 0, NULL, &key, NULL);
826         if (res)
827                 goto out_unload_key;
828
829         value = 1;
830
831         res = RegSetValueEx(key, L"WimBoot", 0, REG_DWORD,
832                             (const BYTE *)&value, sizeof(DWORD));
833         if (res)
834                 goto out_close_key;
835
836         res = RegFlushKey(key);
837
838 out_close_key:
839         res2 = RegCloseKey(key);
840         if (!res)
841                 res = res2;
842 out_unload_key:
843         subkeyname[20] = L'\0';
844         RegUnLoadKey(HKEY_LOCAL_MACHINE, subkeyname);
845 out_check_res:
846         if (res) {
847                 /* Warning only.  */
848                 win32_warning(res, L"Failed to set \\Setup: dword \"WimBoot\"=1 "
849                               "value in registry hive \"%ls\"",
850                               ctx->pathbuf.Buffer);
851         }
852 out:
853         return 0;
854 }
855
856 /* Returns the number of wide characters needed to represent the path to the
857  * specified @dentry, relative to the target directory, when extracted.
858  *
859  * Does not include null terminator (not needed for NtCreateFile).  */
860 static size_t
861 dentry_extraction_path_length(const struct wim_dentry *dentry)
862 {
863         size_t len = 0;
864         const struct wim_dentry *d;
865
866         d = dentry;
867         do {
868                 len += d->d_extraction_name_nchars + 1;
869                 d = d->d_parent;
870         } while (!dentry_is_root(d) && will_extract_dentry(d));
871
872         return --len;  /* No leading slash  */
873 }
874
875 /* Returns the length of the longest string that might need to be appended to
876  * the path to an alias of an inode to open or create a named data stream.
877  *
878  * If the inode has no named data streams, this will be 0.  Otherwise, this will
879  * be 1 plus the length of the longest-named data stream, since the data stream
880  * name must be separated from the path by the ':' character.  */
881 static size_t
882 inode_longest_named_data_stream_spec(const struct wim_inode *inode)
883 {
884         size_t max = 0;
885         for (unsigned i = 0; i < inode->i_num_streams; i++) {
886                 const struct wim_inode_stream *strm = &inode->i_streams[i];
887                 if (!stream_is_named_data_stream(strm))
888                         continue;
889                 size_t len = utf16le_len_chars(strm->stream_name);
890                 if (len > max)
891                         max = len;
892         }
893         if (max)
894                 max += 1;
895         return max;
896 }
897
898 /* Find the length, in wide characters, of the longest path needed for
899  * extraction of any file in @dentry_list relative to the target directory.
900  *
901  * Accounts for named data streams, but does not include null terminator (not
902  * needed for NtCreateFile).  */
903 static size_t
904 compute_path_max(struct list_head *dentry_list)
905 {
906         size_t max = 0;
907         const struct wim_dentry *dentry;
908
909         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
910                 size_t len;
911
912                 len = dentry_extraction_path_length(dentry);
913
914                 /* Account for named data streams  */
915                 len += inode_longest_named_data_stream_spec(dentry->d_inode);
916
917                 if (len > max)
918                         max = len;
919         }
920
921         return max;
922 }
923
924 /* Build the path at which to extract the @dentry, relative to the target
925  * directory.
926  *
927  * The path is saved in ctx->pathbuf.  */
928 static void
929 build_extraction_path(const struct wim_dentry *dentry,
930                       struct win32_apply_ctx *ctx)
931 {
932         size_t len;
933         wchar_t *p;
934         const struct wim_dentry *d;
935
936         len = dentry_extraction_path_length(dentry);
937
938         ctx->pathbuf.Length = len * sizeof(wchar_t);
939         p = ctx->pathbuf.Buffer + len;
940         for (d = dentry;
941              !dentry_is_root(d->d_parent) && will_extract_dentry(d->d_parent);
942              d = d->d_parent)
943         {
944                 p -= d->d_extraction_name_nchars;
945                 if (d->d_extraction_name_nchars)
946                         wmemcpy(p, d->d_extraction_name,
947                                 d->d_extraction_name_nchars);
948                 *--p = '\\';
949         }
950         /* No leading slash  */
951         p -= d->d_extraction_name_nchars;
952         wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
953 }
954
955 /* Build the path at which to extract the @dentry, relative to the target
956  * directory, adding the suffix for a named data stream.
957  *
958  * The path is saved in ctx->pathbuf.  */
959 static void
960 build_extraction_path_with_ads(const struct wim_dentry *dentry,
961                                struct win32_apply_ctx *ctx,
962                                const wchar_t *stream_name,
963                                size_t stream_name_nchars)
964 {
965         wchar_t *p;
966
967         build_extraction_path(dentry, ctx);
968
969         /* Add :NAME for named data stream  */
970         p = ctx->pathbuf.Buffer + (ctx->pathbuf.Length / sizeof(wchar_t));
971         *p++ = L':';
972         wmemcpy(p, stream_name, stream_name_nchars);
973         ctx->pathbuf.Length += (1 + stream_name_nchars) * sizeof(wchar_t);
974 }
975
976 /* Build the Win32 namespace path to the specified @dentry when extracted.
977  *
978  * The path is saved in ctx->pathbuf and will be null terminated.
979  *
980  * XXX: We could get rid of this if it wasn't needed for the file encryption
981  * APIs, and the registry manipulation in WIMBoot mode.  */
982 static void
983 build_win32_extraction_path(const struct wim_dentry *dentry,
984                             struct win32_apply_ctx *ctx)
985 {
986         build_extraction_path(dentry, ctx);
987
988         /* Prepend target_ntpath to our relative path, then change \??\ into \\?\  */
989
990         memmove(ctx->pathbuf.Buffer +
991                         (ctx->target_ntpath.Length / sizeof(wchar_t)) + 1,
992                 ctx->pathbuf.Buffer, ctx->pathbuf.Length);
993         memcpy(ctx->pathbuf.Buffer, ctx->target_ntpath.Buffer,
994                 ctx->target_ntpath.Length);
995         ctx->pathbuf.Buffer[ctx->target_ntpath.Length / sizeof(wchar_t)] = L'\\';
996         ctx->pathbuf.Length += ctx->target_ntpath.Length + sizeof(wchar_t);
997         ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)] = L'\0';
998
999         wimlib_assert(ctx->pathbuf.Length >= 4 * sizeof(wchar_t) &&
1000                       !wmemcmp(ctx->pathbuf.Buffer, L"\\??\\", 4));
1001
1002         ctx->pathbuf.Buffer[1] = L'\\';
1003
1004 }
1005
1006 /* Returns a "printable" representation of the last relative NT path that was
1007  * constructed with build_extraction_path() or build_extraction_path_with_ads().
1008  *
1009  * This will be overwritten by the next call to this function.  */
1010 static const wchar_t *
1011 current_path(struct win32_apply_ctx *ctx)
1012 {
1013         wchar_t *p = ctx->print_buffer;
1014
1015         p = wmempcpy(p, ctx->common.target, ctx->common.target_nchars);
1016         *p++ = L'\\';
1017         p = wmempcpy(p, ctx->pathbuf.Buffer, ctx->pathbuf.Length / sizeof(wchar_t));
1018         *p = L'\0';
1019         return ctx->print_buffer;
1020 }
1021
1022 /* Open handle to the target directory if it is not already open.  If the target
1023  * directory does not exist, this creates it.  */
1024 static int
1025 open_target_directory(struct win32_apply_ctx *ctx)
1026 {
1027         NTSTATUS status;
1028
1029         if (ctx->h_target)
1030                 return 0;
1031
1032         ctx->attr.Length = sizeof(ctx->attr);
1033         ctx->attr.RootDirectory = NULL;
1034         ctx->attr.ObjectName = &ctx->target_ntpath;
1035
1036         /* Don't use FILE_OPEN_REPARSE_POINT here; we want the extraction to
1037          * happen at the directory "pointed to" by the reparse point. */
1038         status = NtCreateFile(&ctx->h_target,
1039                               FILE_TRAVERSE,
1040                               &ctx->attr,
1041                               &ctx->iosb,
1042                               NULL,
1043                               0,
1044                               FILE_SHARE_VALID_FLAGS,
1045                               FILE_OPEN_IF,
1046                               FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT,
1047                               NULL,
1048                               0);
1049         if (!NT_SUCCESS(status)) {
1050                 winnt_error(status, L"Can't open or create directory \"%ls\"",
1051                             ctx->common.target);
1052                 return WIMLIB_ERR_OPENDIR;
1053         }
1054         ctx->attr.RootDirectory = ctx->h_target;
1055         ctx->attr.ObjectName = &ctx->pathbuf;
1056         return 0;
1057 }
1058
1059 static void
1060 close_target_directory(struct win32_apply_ctx *ctx)
1061 {
1062         if (ctx->h_target) {
1063                 NtClose(ctx->h_target);
1064                 ctx->h_target = NULL;
1065                 ctx->attr.RootDirectory = NULL;
1066         }
1067 }
1068
1069 /*
1070  * Ensures the target directory exists and opens a handle to it, in preparation
1071  * of using paths relative to it.
1072  */
1073 static int
1074 prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1075 {
1076         int ret;
1077         size_t path_max;
1078
1079         ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath);
1080         if (ret)
1081                 return ret;
1082
1083         ret = open_target_directory(ctx);
1084         if (ret)
1085                 return ret;
1086
1087         path_max = compute_path_max(dentry_list);
1088         /* Add some extra for building Win32 paths for the file encryption APIs,
1089          * and ensure we have at least enough to potentially use an 8.3 name for
1090          * the last component.  */
1091         path_max += max(2 + (ctx->target_ntpath.Length / sizeof(wchar_t)),
1092                         8 + 1 + 3);
1093
1094         ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t);
1095         if (ctx->pathbuf.MaximumLength != path_max * sizeof(wchar_t)) {
1096                 /* Paths are too long for a UNICODE_STRING! */
1097                 ERROR("Some paths are too long to extract (> 32768 characters)!");
1098                 return WIMLIB_ERR_UNSUPPORTED;
1099         }
1100
1101         ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength);
1102         if (!ctx->pathbuf.Buffer)
1103                 return WIMLIB_ERR_NOMEM;
1104
1105         ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) *
1106                                    sizeof(wchar_t));
1107         if (!ctx->print_buffer)
1108                 return WIMLIB_ERR_NOMEM;
1109
1110         return 0;
1111 }
1112
1113 /* When creating an inode that will have a short (DOS) name, we create it using
1114  * the long name associated with the short name.  This ensures that the short
1115  * name gets associated with the correct long name.  */
1116 static struct wim_dentry *
1117 first_extraction_alias(const struct wim_inode *inode)
1118 {
1119         struct wim_dentry *dentry;
1120
1121         inode_for_each_extraction_alias(dentry, inode)
1122                 if (dentry_has_short_name(dentry))
1123                         return dentry;
1124         return inode_first_extraction_dentry(inode);
1125 }
1126
1127 /*
1128  * Set or clear FILE_ATTRIBUTE_COMPRESSED if the inherited value is different
1129  * from the desired value.
1130  *
1131  * Note that you can NOT override the inherited value of
1132  * FILE_ATTRIBUTE_COMPRESSED directly with NtCreateFile().
1133  */
1134 static int
1135 adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
1136                              struct win32_apply_ctx *ctx)
1137 {
1138         const bool compressed = (dentry->d_inode->i_attributes &
1139                                  FILE_ATTRIBUTE_COMPRESSED);
1140         FILE_BASIC_INFORMATION info;
1141         USHORT compression_state;
1142         NTSTATUS status;
1143
1144         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
1145                 return 0;
1146
1147         if (!ctx->common.supported_features.compressed_files)
1148                 return 0;
1149
1150
1151         /* Get current attributes  */
1152         status = NtQueryInformationFile(h, &ctx->iosb, &info, sizeof(info),
1153                                         FileBasicInformation);
1154         if (NT_SUCCESS(status) &&
1155             compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED))
1156         {
1157                 /* Nothing needs to be done.  */
1158                 return 0;
1159         }
1160
1161         /* Set the new compression state  */
1162
1163         if (compressed)
1164                 compression_state = COMPRESSION_FORMAT_DEFAULT;
1165         else
1166                 compression_state = COMPRESSION_FORMAT_NONE;
1167
1168         status = winnt_fsctl(h, FSCTL_SET_COMPRESSION,
1169                              &compression_state, sizeof(USHORT), NULL, 0, NULL);
1170         if (NT_SUCCESS(status))
1171                 return 0;
1172
1173         winnt_error(status, L"Can't %s compression attribute on \"%ls\"",
1174                     (compressed ? "set" : "clear"), current_path(ctx));
1175         return WIMLIB_ERR_SET_ATTRIBUTES;
1176 }
1177
1178 static bool
1179 need_sparse_flag(const struct wim_inode *inode,
1180                  const struct win32_apply_ctx *ctx)
1181 {
1182         return (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) &&
1183                 ctx->common.supported_features.sparse_files;
1184 }
1185
1186 static int
1187 set_sparse_flag(HANDLE h, struct win32_apply_ctx *ctx)
1188 {
1189         NTSTATUS status;
1190
1191         status = winnt_fsctl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, NULL);
1192         if (NT_SUCCESS(status))
1193                 return 0;
1194
1195         winnt_error(status, L"Can't set sparse flag on \"%ls\"",
1196                     current_path(ctx));
1197         return WIMLIB_ERR_SET_ATTRIBUTES;
1198 }
1199
1200 /* Try to enable short name support on the target volume.  If successful, return
1201  * true.  If unsuccessful, issue a warning and return false.  */
1202 static bool
1203 try_to_enable_short_names(const wchar_t *volume)
1204 {
1205         HANDLE h;
1206         FILE_FS_PERSISTENT_VOLUME_INFORMATION info;
1207         BOOL bret;
1208         DWORD bytesReturned;
1209
1210         h = CreateFile(volume, GENERIC_WRITE,
1211                        FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
1212                        FILE_FLAG_BACKUP_SEMANTICS, NULL);
1213         if (h == INVALID_HANDLE_VALUE)
1214                 goto fail;
1215
1216         info.VolumeFlags = 0;
1217         info.FlagMask = PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED;
1218         info.Version = 1;
1219         info.Reserved = 0;
1220
1221         bret = DeviceIoControl(h, FSCTL_SET_PERSISTENT_VOLUME_STATE,
1222                                &info, sizeof(info), NULL, 0,
1223                                &bytesReturned, NULL);
1224
1225         CloseHandle(h);
1226
1227         if (!bret)
1228                 goto fail;
1229         return true;
1230
1231 fail:
1232         win32_warning(GetLastError(),
1233                       L"Failed to enable short name support on %ls",
1234                       volume + 4);
1235         return false;
1236 }
1237
1238 static NTSTATUS
1239 remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
1240 {
1241         wchar_t *name;
1242         wchar_t *end;
1243         NTSTATUS status;
1244         HANDLE h;
1245         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
1246                          (13 * sizeof(wchar_t));
1247         u8 buf[bufsize] _aligned_attribute(8);
1248         bool retried = false;
1249         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
1250
1251         memset(buf, 0, bufsize);
1252
1253         /* Build the path with the short name.  */
1254         name = &ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)];
1255         while (name != ctx->pathbuf.Buffer && *(name - 1) != L'\\')
1256                 name--;
1257         end = mempcpy(name, dentry->d_short_name, dentry->d_short_name_nbytes);
1258         ctx->pathbuf.Length = ((u8 *)end - (u8 *)ctx->pathbuf.Buffer);
1259
1260         /* Open the conflicting file (by short name).  */
1261         status = NtOpenFile(&h, GENERIC_WRITE | DELETE,
1262                             &ctx->attr, &ctx->iosb,
1263                             FILE_SHARE_VALID_FLAGS,
1264                             FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT);
1265         if (!NT_SUCCESS(status)) {
1266                 winnt_warning(status, L"Can't open \"%ls\"", current_path(ctx));
1267                 goto out;
1268         }
1269
1270 #if 0
1271         WARNING("Overriding conflicting short name; path=\"%ls\"",
1272                 current_path(ctx));
1273 #endif
1274
1275         /* Try to remove the short name on the conflicting file.  */
1276
1277 retry:
1278         status = NtSetInformationFile(h, &ctx->iosb, info, bufsize,
1279                                       FileShortNameInformation);
1280
1281         if (status == STATUS_INVALID_PARAMETER && !retried) {
1282                 /* Microsoft forgot to make it possible to remove short names
1283                  * until Windows 7.  Oops.  Use a random short name instead.  */
1284                 get_random_alnum_chars(info->FileName, 8);
1285                 wcscpy(&info->FileName[8], L".WLB");
1286                 info->FileNameLength = 12 * sizeof(wchar_t);
1287                 retried = true;
1288                 goto retry;
1289         }
1290         NtClose(h);
1291 out:
1292         build_extraction_path(dentry, ctx);
1293         return status;
1294 }
1295
1296 /* Set the short name on the open file @h which has been created at the location
1297  * indicated by @dentry.
1298  *
1299  * Note that this may add, change, or remove the short name.
1300  *
1301  * @h must be opened with DELETE access.
1302  *
1303  * Returns 0 or WIMLIB_ERR_SET_SHORT_NAME.  The latter only happens in
1304  * STRICT_SHORT_NAMES mode.
1305  */
1306 static int
1307 set_short_name(HANDLE h, const struct wim_dentry *dentry,
1308                struct win32_apply_ctx *ctx)
1309 {
1310
1311         if (!ctx->common.supported_features.short_names)
1312                 return 0;
1313
1314         /*
1315          * Note: The size of the FILE_NAME_INFORMATION buffer must be such that
1316          * FileName contains at least 2 wide characters (4 bytes).  Otherwise,
1317          * NtSetInformationFile() will return STATUS_INFO_LENGTH_MISMATCH.  This
1318          * is despite the fact that FileNameLength can validly be 0 or 2 bytes,
1319          * with the former case being removing the existing short name if
1320          * present, rather than setting one.
1321          *
1322          * The null terminator is seemingly optional, but to be safe we include
1323          * space for it and zero all unused space.
1324          */
1325
1326         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
1327                          max(dentry->d_short_name_nbytes, sizeof(wchar_t)) +
1328                          sizeof(wchar_t);
1329         u8 buf[bufsize] _aligned_attribute(8);
1330         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
1331         NTSTATUS status;
1332         bool tried_to_remove_existing = false;
1333
1334         memset(buf, 0, bufsize);
1335
1336         info->FileNameLength = dentry->d_short_name_nbytes;
1337         memcpy(info->FileName, dentry->d_short_name, dentry->d_short_name_nbytes);
1338
1339 retry:
1340         status = NtSetInformationFile(h, &ctx->iosb, info, bufsize,
1341                                       FileShortNameInformation);
1342         if (NT_SUCCESS(status))
1343                 return 0;
1344
1345         if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
1346                 if (dentry->d_short_name_nbytes == 0)
1347                         return 0;
1348                 if (!ctx->tried_to_enable_short_names) {
1349                         wchar_t volume[7];
1350                         int ret;
1351
1352                         ctx->tried_to_enable_short_names = true;
1353
1354                         ret = win32_get_drive_path(ctx->common.target,
1355                                                    volume);
1356                         if (ret)
1357                                 return ret;
1358                         if (try_to_enable_short_names(volume))
1359                                 goto retry;
1360                 }
1361         }
1362
1363         /*
1364          * Short names can conflict in several cases:
1365          *
1366          * - a file being extracted has a short name conflicting with an
1367          *   existing file
1368          *
1369          * - a file being extracted has a short name conflicting with another
1370          *   file being extracted (possible, but shouldn't happen)
1371          *
1372          * - a file being extracted has a short name that conflicts with the
1373          *   automatically generated short name of a file we previously
1374          *   extracted, but failed to set the short name for.  Sounds unlikely,
1375          *   but this actually does happen fairly often on versions of Windows
1376          *   prior to Windows 7 because they do not support removing short names
1377          *   from files.
1378          */
1379         if (unlikely(status == STATUS_OBJECT_NAME_COLLISION) &&
1380             dentry->d_short_name_nbytes && !tried_to_remove_existing)
1381         {
1382                 tried_to_remove_existing = true;
1383                 status = remove_conflicting_short_name(dentry, ctx);
1384                 if (NT_SUCCESS(status))
1385                         goto retry;
1386         }
1387
1388         /* By default, failure to set short names is not an error (since short
1389          * names aren't too important anymore...).  */
1390         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES)) {
1391                 if (dentry->d_short_name_nbytes)
1392                         ctx->num_set_short_name_failures++;
1393                 else
1394                         ctx->num_remove_short_name_failures++;
1395                 return 0;
1396         }
1397
1398         winnt_error(status, L"Can't set short name on \"%ls\"", current_path(ctx));
1399         return WIMLIB_ERR_SET_SHORT_NAME;
1400 }
1401
1402 /*
1403  * A wrapper around NtCreateFile() to make it slightly more usable...
1404  * This uses the path currently constructed in ctx->pathbuf.
1405  *
1406  * Also, we always specify SYNCHRONIZE access, FILE_OPEN_FOR_BACKUP_INTENT, and
1407  * FILE_OPEN_REPARSE_POINT.
1408  */
1409 static NTSTATUS
1410 do_create_file(PHANDLE FileHandle,
1411                ACCESS_MASK DesiredAccess,
1412                PLARGE_INTEGER AllocationSize,
1413                ULONG FileAttributes,
1414                ULONG CreateDisposition,
1415                ULONG CreateOptions,
1416                struct win32_apply_ctx *ctx)
1417 {
1418         return NtCreateFile(FileHandle,
1419                             DesiredAccess | SYNCHRONIZE,
1420                             &ctx->attr,
1421                             &ctx->iosb,
1422                             AllocationSize,
1423                             FileAttributes,
1424                             FILE_SHARE_VALID_FLAGS,
1425                             CreateDisposition,
1426                             CreateOptions |
1427                                 FILE_OPEN_FOR_BACKUP_INTENT |
1428                                 FILE_OPEN_REPARSE_POINT,
1429                             NULL,
1430                             0);
1431 }
1432
1433 /* Like do_create_file(), but builds the extraction path of the @dentry first.
1434  */
1435 static NTSTATUS
1436 create_file(PHANDLE FileHandle,
1437             ACCESS_MASK DesiredAccess,
1438             PLARGE_INTEGER AllocationSize,
1439             ULONG FileAttributes,
1440             ULONG CreateDisposition,
1441             ULONG CreateOptions,
1442             const struct wim_dentry *dentry,
1443             struct win32_apply_ctx *ctx)
1444 {
1445         build_extraction_path(dentry, ctx);
1446         return do_create_file(FileHandle,
1447                               DesiredAccess,
1448                               AllocationSize,
1449                               FileAttributes,
1450                               CreateDisposition,
1451                               CreateOptions,
1452                               ctx);
1453 }
1454
1455 static int
1456 delete_file_or_stream(struct win32_apply_ctx *ctx)
1457 {
1458         NTSTATUS status;
1459         HANDLE h;
1460         ULONG perms = DELETE;
1461         ULONG flags = FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE;
1462
1463         /* First try opening the file with FILE_DELETE_ON_CLOSE.  In most cases,
1464          * all we have to do is that plus close the file handle.  */
1465 retry:
1466         status = do_create_file(&h, perms, NULL, 0, FILE_OPEN, flags, ctx);
1467
1468         if (unlikely(status == STATUS_CANNOT_DELETE)) {
1469                 /* This error occurs for files with FILE_ATTRIBUTE_READONLY set.
1470                  * Try an alternate approach: first open the file without
1471                  * FILE_DELETE_ON_CLOSE, then reset the file attributes, then
1472                  * set the "delete" disposition on the handle.  */
1473                 if (flags & FILE_DELETE_ON_CLOSE) {
1474                         flags &= ~FILE_DELETE_ON_CLOSE;
1475                         perms |= FILE_WRITE_ATTRIBUTES;
1476                         goto retry;
1477                 }
1478         }
1479
1480         if (unlikely(!NT_SUCCESS(status))) {
1481                 winnt_error(status, L"Can't open \"%ls\" for deletion "
1482                             "(perms=%x, flags=%x)",
1483                             current_path(ctx), perms, flags);
1484                 return WIMLIB_ERR_OPEN;
1485         }
1486
1487         if (unlikely(!(flags & FILE_DELETE_ON_CLOSE))) {
1488
1489                 FILE_BASIC_INFORMATION basic_info =
1490                         { .FileAttributes = FILE_ATTRIBUTE_NORMAL };
1491                 status = NtSetInformationFile(h, &ctx->iosb, &basic_info,
1492                                               sizeof(basic_info),
1493                                               FileBasicInformation);
1494
1495                 if (!NT_SUCCESS(status)) {
1496                         winnt_error(status, L"Can't reset attributes of \"%ls\" "
1497                                     "to prepare for deletion", current_path(ctx));
1498                         NtClose(h);
1499                         return WIMLIB_ERR_SET_ATTRIBUTES;
1500                 }
1501
1502                 FILE_DISPOSITION_INFORMATION disp_info =
1503                         { .DoDeleteFile = TRUE };
1504                 status = NtSetInformationFile(h, &ctx->iosb, &disp_info,
1505                                               sizeof(disp_info),
1506                                               FileDispositionInformation);
1507                 if (!NT_SUCCESS(status)) {
1508                         winnt_error(status, L"Can't set delete-on-close "
1509                                     "disposition on \"%ls\"", current_path(ctx));
1510                         NtClose(h);
1511                         return WIMLIB_ERR_SET_ATTRIBUTES;
1512                 }
1513         }
1514
1515         status = NtClose(h);
1516         if (unlikely(!NT_SUCCESS(status))) {
1517                 winnt_error(status, L"Error closing \"%ls\" after setting "
1518                             "delete-on-close disposition", current_path(ctx));
1519                 return WIMLIB_ERR_OPEN;
1520         }
1521
1522         return 0;
1523 }
1524
1525 /*
1526  * Create a nondirectory file or named data stream at the current path,
1527  * superseding any that already exists at that path.  If successful, return an
1528  * open handle to the file or named data stream with the requested permissions.
1529  */
1530 static int
1531 supersede_file_or_stream(struct win32_apply_ctx *ctx, DWORD perms,
1532                          HANDLE *h_ret)
1533 {
1534         NTSTATUS status;
1535         bool retried = false;
1536
1537         /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that
1538          * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be.  */
1539 retry:
1540         status = do_create_file(h_ret,
1541                                 perms,
1542                                 NULL,
1543                                 FILE_ATTRIBUTE_SYSTEM,
1544                                 FILE_CREATE,
1545                                 FILE_NON_DIRECTORY_FILE,
1546                                 ctx);
1547         if (likely(NT_SUCCESS(status)))
1548                 return 0;
1549
1550         /* STATUS_OBJECT_NAME_COLLISION means that the file or stream already
1551          * exists.  Delete the existing file or stream, then try again.
1552          *
1553          * Note: we don't use FILE_OVERWRITE_IF or FILE_SUPERSEDE because of
1554          * problems with certain file attributes, especially
1555          * FILE_ATTRIBUTE_ENCRYPTED.  FILE_SUPERSEDE is also broken in the
1556          * Windows PE ramdisk.  */
1557         if (status == STATUS_OBJECT_NAME_COLLISION && !retried) {
1558                 int ret = delete_file_or_stream(ctx);
1559                 if (ret)
1560                         return ret;
1561                 retried = true;
1562                 goto retry;
1563         }
1564         winnt_error(status, L"Can't create \"%ls\"", current_path(ctx));
1565         return WIMLIB_ERR_OPEN;
1566 }
1567
1568 /* Set the reparse point @rpbuf of length @rpbuflen on the extracted file
1569  * corresponding to the WIM dentry @dentry.  */
1570 static int
1571 do_set_reparse_point(const struct wim_dentry *dentry,
1572                      const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
1573                      struct win32_apply_ctx *ctx)
1574 {
1575         NTSTATUS status;
1576         HANDLE h;
1577
1578         status = create_file(&h, GENERIC_WRITE, NULL,
1579                              0, FILE_OPEN, 0, dentry, ctx);
1580         if (!NT_SUCCESS(status))
1581                 goto fail;
1582
1583         status = winnt_fsctl(h, FSCTL_SET_REPARSE_POINT,
1584                              rpbuf, rpbuflen, NULL, 0, NULL);
1585         NtClose(h);
1586
1587         if (NT_SUCCESS(status))
1588                 return 0;
1589
1590         /* On Windows, by default only the Administrator can create symbolic
1591          * links for some reason.  By default we just issue a warning if this
1592          * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
1593          * to get a hard error.  */
1594         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
1595             && (status == STATUS_PRIVILEGE_NOT_HELD ||
1596                 status == STATUS_ACCESS_DENIED)
1597             && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1598                 dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1599         {
1600                 WARNING("Can't create symbolic link \"%ls\"!              \n"
1601                         "          (Need Administrator rights, or at least "
1602                         "the\n"
1603                         "          SeCreateSymbolicLink privilege.)",
1604                         current_path(ctx));
1605                 return 0;
1606         }
1607
1608 fail:
1609         winnt_error(status, L"Can't set reparse data on \"%ls\"",
1610                     current_path(ctx));
1611         return WIMLIB_ERR_SET_REPARSE_DATA;
1612 }
1613
1614 /*
1615  * Create empty named data streams and potentially a reparse point for the
1616  * specified file, if any.
1617  *
1618  * Since these won't have blob descriptors, they won't show up in the call to
1619  * extract_blob_list().  Hence the need for the special case.
1620  */
1621 static int
1622 create_empty_streams(const struct wim_dentry *dentry,
1623                      struct win32_apply_ctx *ctx)
1624 {
1625         const struct wim_inode *inode = dentry->d_inode;
1626         int ret;
1627
1628         for (unsigned i = 0; i < inode->i_num_streams; i++) {
1629                 const struct wim_inode_stream *strm = &inode->i_streams[i];
1630
1631                 if (stream_blob_resolved(strm) != NULL)
1632                         continue;
1633
1634                 if (strm->stream_type == STREAM_TYPE_REPARSE_POINT &&
1635                     ctx->common.supported_features.reparse_points)
1636                 {
1637                         u8 buf[REPARSE_DATA_OFFSET] _aligned_attribute(8);
1638                         struct reparse_buffer_disk *rpbuf =
1639                                 (struct reparse_buffer_disk *)buf;
1640                         complete_reparse_point(rpbuf, inode, 0);
1641                         ret = do_set_reparse_point(dentry, rpbuf,
1642                                                    REPARSE_DATA_OFFSET, ctx);
1643                         if (ret)
1644                                 return ret;
1645                 } else if (stream_is_named_data_stream(strm) &&
1646                            ctx->common.supported_features.named_data_streams)
1647                 {
1648                         HANDLE h;
1649
1650                         build_extraction_path_with_ads(dentry, ctx,
1651                                                        strm->stream_name,
1652                                                        utf16le_len_chars(strm->stream_name));
1653                         /*
1654                          * Note: do not request any permissions on the handle.
1655                          * Otherwise, we may encounter a Windows bug where the
1656                          * parent directory DACL denies read access to the new
1657                          * named data stream, even when using backup semantics!
1658                          */
1659                         ret = supersede_file_or_stream(ctx, 0, &h);
1660
1661                         build_extraction_path(dentry, ctx);
1662
1663                         if (ret)
1664                                 return ret;
1665                         NtClose(h);
1666                 }
1667         }
1668
1669         return 0;
1670 }
1671
1672 /*
1673  * Creates the directory named by @dentry, or uses an existing directory at that
1674  * location.  If necessary, sets the short name and/or fixes compression and
1675  * encryption attributes.
1676  *
1677  * Returns 0, WIMLIB_ERR_MKDIR, or WIMLIB_ERR_SET_SHORT_NAME.
1678  */
1679 static int
1680 create_directory(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
1681 {
1682         DWORD perms;
1683         NTSTATUS status;
1684         HANDLE h;
1685         int ret;
1686
1687         /* DELETE is needed for set_short_name(); GENERIC_READ and GENERIC_WRITE
1688          * are needed for adjust_compression_attribute().  */
1689         perms = GENERIC_READ | GENERIC_WRITE;
1690         if (!dentry_is_root(dentry))
1691                 perms |= DELETE;
1692
1693         /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that
1694          * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be.  */
1695         status = create_file(&h, perms, NULL, FILE_ATTRIBUTE_SYSTEM,
1696                              FILE_OPEN_IF, FILE_DIRECTORY_FILE, dentry, ctx);
1697         if (unlikely(!NT_SUCCESS(status))) {
1698                 const wchar_t *path = current_path(ctx);
1699                 winnt_error(status, L"Can't create directory \"%ls\"", path);
1700
1701                 /* Check for known issue with WindowsApps directory.  */
1702                 if (status == STATUS_ACCESS_DENIED &&
1703                     (wcsstr(path, L"\\WindowsApps\\") ||
1704                      wcsstr(path, L"\\InfusedApps\\"))) {
1705                         ERROR(
1706 "You seem to be trying to extract files to the WindowsApps directory.\n"
1707 "        Windows 8.1 and later use new file permissions in this directory that\n"
1708 "        cannot be overridden, even by backup/restore programs.  To extract your\n"
1709 "        files anyway, you need to choose a different target directory, delete\n"
1710 "        the WindowsApps directory entirely, reformat the volume, do the\n"
1711 "        extraction from a non-broken operating system such as Windows 7 or\n"
1712 "        Linux, or wait for Microsoft to fix the design flaw in their operating\n"
1713 "        system.  This is *not* a bug in wimlib.  See this thread for more\n"
1714 "        information: https://wimlib.net/forums/viewtopic.php?f=1&t=261");
1715                 }
1716                 return WIMLIB_ERR_MKDIR;
1717         }
1718
1719         if (ctx->iosb.Information == FILE_OPENED) {
1720                 /* If we opened an existing directory, try to clear its file
1721                  * attributes.  As far as I know, this only actually makes a
1722                  * difference in the case where a FILE_ATTRIBUTE_READONLY
1723                  * directory has a named data stream which needs to be
1724                  * extracted.  You cannot create a named data stream of such a
1725                  * directory, even though this contradicts Microsoft's
1726                  * documentation for FILE_ATTRIBUTE_READONLY which states it is
1727                  * not honored for directories!  */
1728                 if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) {
1729                         FILE_BASIC_INFORMATION basic_info =
1730                                 { .FileAttributes = FILE_ATTRIBUTE_NORMAL };
1731                         NtSetInformationFile(h, &ctx->iosb, &basic_info,
1732                                              sizeof(basic_info),
1733                                              FileBasicInformation);
1734                 }
1735         }
1736
1737         if (!dentry_is_root(dentry)) {
1738                 ret = set_short_name(h, dentry, ctx);
1739                 if (ret)
1740                         goto out;
1741         }
1742
1743         ret = adjust_compression_attribute(h, dentry, ctx);
1744 out:
1745         NtClose(h);
1746         return ret;
1747 }
1748
1749 /*
1750  * Create all the directories being extracted, other than the target directory
1751  * itself.
1752  *
1753  * Note: we don't honor directory hard links.  However, we don't allow them to
1754  * exist in WIM images anyway (see inode_fixup.c).
1755  */
1756 static int
1757 create_directories(struct list_head *dentry_list,
1758                    struct win32_apply_ctx *ctx)
1759 {
1760         const struct wim_dentry *dentry;
1761         int ret;
1762
1763         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1764
1765                 if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
1766                         continue;
1767
1768                 /* Note: Here we include files with
1769                  * FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT, but we
1770                  * wait until later to actually set the reparse data.  */
1771
1772                 ret = create_directory(dentry, ctx);
1773
1774                 if (!ret)
1775                         ret = create_empty_streams(dentry, ctx);
1776
1777                 ret = check_apply_error(dentry, ctx, ret);
1778                 if (ret)
1779                         return ret;
1780
1781                 ret = report_file_created(&ctx->common);
1782                 if (ret)
1783                         return ret;
1784         }
1785         return 0;
1786 }
1787
1788 /*
1789  * Creates the nondirectory file named by @dentry.
1790  *
1791  * On success, returns an open handle to the file in @h_ret, with GENERIC_READ,
1792  * GENERIC_WRITE, and DELETE access.  Also, the path to the file will be saved
1793  * in ctx->pathbuf.  On failure, returns an error code.
1794  */
1795 static int
1796 create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
1797                           struct win32_apply_ctx *ctx)
1798 {
1799         int ret;
1800         HANDLE h;
1801
1802         build_extraction_path(dentry, ctx);
1803
1804         ret = supersede_file_or_stream(ctx,
1805                                        GENERIC_READ | GENERIC_WRITE | DELETE,
1806                                        &h);
1807         if (ret)
1808                 goto out;
1809
1810         ret = adjust_compression_attribute(h, dentry, ctx);
1811         if (ret)
1812                 goto out_close;
1813
1814         if (need_sparse_flag(dentry->d_inode, ctx)) {
1815                 ret = set_sparse_flag(h, ctx);
1816                 if (ret)
1817                         goto out_close;
1818         }
1819
1820         ret = create_empty_streams(dentry, ctx);
1821         if (ret)
1822                 goto out_close;
1823
1824         *h_ret = h;
1825         return 0;
1826
1827 out_close:
1828         NtClose(h);
1829 out:
1830         return ret;
1831 }
1832
1833 /* Creates a hard link at the location named by @dentry to the file represented
1834  * by the open handle @h.  Or, if the target volume does not support hard links,
1835  * create a separate file instead.  */
1836 static int
1837 create_link(HANDLE h, const struct wim_dentry *dentry,
1838             struct win32_apply_ctx *ctx)
1839 {
1840         if (ctx->common.supported_features.hard_links) {
1841
1842                 build_extraction_path(dentry, ctx);
1843
1844                 size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
1845                                  ctx->pathbuf.Length + sizeof(wchar_t);
1846                 u8 buf[bufsize] _aligned_attribute(8);
1847                 FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
1848                 NTSTATUS status;
1849
1850                 info->ReplaceIfExists = TRUE;
1851                 info->RootDirectory = ctx->attr.RootDirectory;
1852                 info->FileNameLength = ctx->pathbuf.Length;
1853                 memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
1854                 info->FileName[info->FileNameLength / 2] = L'\0';
1855
1856                 /* Note: the null terminator isn't actually necessary,
1857                  * but if you don't add the extra character, you get
1858                  * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
1859                  * happens to be 2  */
1860
1861                 status = NtSetInformationFile(h, &ctx->iosb, info, bufsize,
1862                                               FileLinkInformation);
1863                 if (NT_SUCCESS(status))
1864                         return 0;
1865                 winnt_error(status, L"Failed to create link \"%ls\"",
1866                             current_path(ctx));
1867                 return WIMLIB_ERR_LINK;
1868         } else {
1869                 HANDLE h2;
1870                 int ret;
1871
1872                 ret = create_nondirectory_inode(&h2, dentry, ctx);
1873                 if (ret)
1874                         return ret;
1875
1876                 NtClose(h2);
1877                 return 0;
1878         }
1879 }
1880
1881 /* Given an inode (represented by the open handle @h) for which one link has
1882  * been created (named by @first_dentry), create the other links.
1883  *
1884  * Or, if the target volume does not support hard links, create separate files.
1885  *
1886  * Note: This uses ctx->pathbuf and does not reset it.
1887  */
1888 static int
1889 create_links(HANDLE h, const struct wim_dentry *first_dentry,
1890              struct win32_apply_ctx *ctx)
1891 {
1892         const struct wim_inode *inode = first_dentry->d_inode;
1893         const struct wim_dentry *dentry;
1894         int ret;
1895
1896         inode_for_each_extraction_alias(dentry, inode) {
1897                 if (dentry != first_dentry) {
1898                         ret = create_link(h, dentry, ctx);
1899                         if (ret)
1900                                 return ret;
1901                 }
1902         }
1903         return 0;
1904 }
1905
1906 /* Create a nondirectory file, including all links.  */
1907 static int
1908 create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx)
1909 {
1910         struct wim_dentry *first_dentry;
1911         HANDLE h;
1912         int ret;
1913
1914         first_dentry = first_extraction_alias(inode);
1915
1916         /* Create first link.  */
1917         ret = create_nondirectory_inode(&h, first_dentry, ctx);
1918         if (ret)
1919                 return ret;
1920
1921         /* Set short name.  */
1922         ret = set_short_name(h, first_dentry, ctx);
1923
1924         /* Create additional links, OR if hard links are not supported just
1925          * create more files.  */
1926         if (!ret)
1927                 ret = create_links(h, first_dentry, ctx);
1928
1929         /* "WIMBoot" extraction: set external backing by the WIM file if needed.  */
1930         if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
1931                 ret = set_backed_from_wim(h, inode, ctx);
1932
1933         NtClose(h);
1934         return ret;
1935 }
1936
1937 /* Create all the nondirectory files being extracted, including all aliases
1938  * (hard links).  */
1939 static int
1940 create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1941 {
1942         struct wim_dentry *dentry;
1943         struct wim_inode *inode;
1944         int ret;
1945
1946         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1947                 inode = dentry->d_inode;
1948                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1949                         continue;
1950                 /* Call create_nondirectory() only once per inode  */
1951                 if (dentry == inode_first_extraction_dentry(inode)) {
1952                         ret = create_nondirectory(inode, ctx);
1953                         ret = check_apply_error(dentry, ctx, ret);
1954                         if (ret)
1955                                 return ret;
1956                 }
1957                 ret = report_file_created(&ctx->common);
1958                 if (ret)
1959                         return ret;
1960         }
1961         return 0;
1962 }
1963
1964 static void
1965 close_handles(struct win32_apply_ctx *ctx)
1966 {
1967         for (unsigned i = 0; i < ctx->num_open_handles; i++)
1968                 NtClose(ctx->open_handles[i]);
1969 }
1970
1971 /* Prepare to read the next blob, which has size @blob_size, into an in-memory
1972  * buffer.  */
1973 static bool
1974 prepare_data_buffer(struct win32_apply_ctx *ctx, u64 blob_size)
1975 {
1976         if (blob_size > ctx->data_buffer_size) {
1977                 /* Larger buffer needed.  */
1978                 void *new_buffer;
1979                 if ((size_t)blob_size != blob_size)
1980                         return false;
1981                 new_buffer = REALLOC(ctx->data_buffer, blob_size);
1982                 if (!new_buffer)
1983                         return false;
1984                 ctx->data_buffer = new_buffer;
1985                 ctx->data_buffer_size = blob_size;
1986         }
1987         /* On the first call this changes data_buffer_ptr from NULL, which tells
1988          * extract_chunk() that the data buffer needs to be filled while reading
1989          * the stream data.  */
1990         ctx->data_buffer_ptr = ctx->data_buffer;
1991         return true;
1992 }
1993
1994 static int
1995 begin_extract_blob_instance(const struct blob_descriptor *blob,
1996                             struct wim_dentry *dentry,
1997                             const struct wim_inode_stream *strm,
1998                             struct win32_apply_ctx *ctx)
1999 {
2000         HANDLE h;
2001         NTSTATUS status;
2002
2003         if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) {
2004                 /* We can't write the reparse point stream directly; we must set
2005                  * it with FSCTL_SET_REPARSE_POINT, which requires that all the
2006                  * data be available.  So, stage the data in a buffer.  */
2007                 if (!prepare_data_buffer(ctx, blob->size))
2008                         return WIMLIB_ERR_NOMEM;
2009                 list_add_tail(&dentry->d_tmp_list, &ctx->reparse_dentries);
2010                 return 0;
2011         }
2012
2013         if (unlikely(strm->stream_type == STREAM_TYPE_EFSRPC_RAW_DATA)) {
2014                 /* We can't write encrypted files directly; we must use
2015                  * WriteEncryptedFileRaw(), which requires providing the data
2016                  * through a callback function.  This can't easily be combined
2017                  * with our own callback-based approach.
2018                  *
2019                  * The current workaround is to simply read the blob into memory
2020                  * and write the encrypted file from that.
2021                  *
2022                  * TODO: This isn't sufficient for extremely large encrypted
2023                  * files.  Perhaps we should create an extra thread to write
2024                  * such files...  */
2025                 if (!prepare_data_buffer(ctx, blob->size))
2026                         return WIMLIB_ERR_NOMEM;
2027                 list_add_tail(&dentry->d_tmp_list, &ctx->encrypted_dentries);
2028                 return 0;
2029         }
2030
2031         /* It's a data stream (may be unnamed or named).  */
2032         wimlib_assert(strm->stream_type == STREAM_TYPE_DATA);
2033
2034         if (ctx->num_open_handles == MAX_OPEN_FILES) {
2035                 /* XXX: Fix this.  But because of the checks in
2036                  * extract_blob_list(), this can now only happen on a filesystem
2037                  * that does not support hard links.  */
2038                 ERROR("Can't extract data: too many open files!");
2039                 return WIMLIB_ERR_UNSUPPORTED;
2040         }
2041
2042
2043         if (unlikely(stream_is_named(strm))) {
2044                 build_extraction_path_with_ads(dentry, ctx,
2045                                                strm->stream_name,
2046                                                utf16le_len_chars(strm->stream_name));
2047         } else {
2048                 build_extraction_path(dentry, ctx);
2049         }
2050
2051
2052         /* Open a new handle  */
2053         status = do_create_file(&h,
2054                                 FILE_WRITE_DATA | SYNCHRONIZE,
2055                                 NULL, 0, FILE_OPEN_IF,
2056                                 FILE_SEQUENTIAL_ONLY |
2057                                         FILE_SYNCHRONOUS_IO_NONALERT,
2058                                 ctx);
2059         if (!NT_SUCCESS(status)) {
2060                 winnt_error(status, L"Can't open \"%ls\" for writing",
2061                             current_path(ctx));
2062                 return WIMLIB_ERR_OPEN;
2063         }
2064
2065         ctx->is_sparse_stream[ctx->num_open_handles] = false;
2066         if (need_sparse_flag(dentry->d_inode, ctx)) {
2067                 /* If the stream is unnamed, then the sparse flag was already
2068                  * set when the file was created.  But if the stream is named,
2069                  * then we need to set the sparse flag here. */
2070                 if (unlikely(stream_is_named(strm))) {
2071                         int ret = set_sparse_flag(h, ctx);
2072                         if (ret) {
2073                                 NtClose(h);
2074                                 return ret;
2075                         }
2076                 }
2077                 ctx->is_sparse_stream[ctx->num_open_handles] = true;
2078                 ctx->any_sparse_streams = true;
2079         } else {
2080                 /* Allocate space for the data.  */
2081                 FILE_ALLOCATION_INFORMATION info =
2082                         { .AllocationSize = { .QuadPart = blob->size }};
2083                 NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info),
2084                                      FileAllocationInformation);
2085         }
2086         ctx->open_handles[ctx->num_open_handles++] = h;
2087         return 0;
2088 }
2089
2090 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
2091  * pointer to the suffix of the path that begins with the device directly, such
2092  * as e:\Windows\System32.  */
2093 static const wchar_t *
2094 skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
2095 {
2096         static const wchar_t * const dirs[] = {
2097                 L"\\??\\",
2098                 L"\\DosDevices\\",
2099                 L"\\Device\\",
2100         };
2101         const wchar_t * const end = path + path_nchars;
2102
2103         for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
2104                 size_t len = wcslen(dirs[i]);
2105                 if (len <= (end - path) && !wmemcmp(path, dirs[i], len)) {
2106                         path += len;
2107                         while (path != end && *path == L'\\')
2108                                 path++;
2109                         return path;
2110                 }
2111         }
2112         return path;
2113 }
2114
2115 /*
2116  * Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
2117  * pointer to the suffix of the path that is device-relative but possibly with
2118  * leading slashes, such as \Windows\System32.
2119  *
2120  * The path has an explicit length and is not necessarily null terminated.
2121  */
2122 static const wchar_t *
2123 get_device_relative_path(const wchar_t *path, size_t path_nchars)
2124 {
2125         const wchar_t * const orig_path = path;
2126         const wchar_t * const end = path + path_nchars;
2127
2128         path = skip_nt_toplevel_component(path, path_nchars);
2129         if (path == orig_path)
2130                 return orig_path;
2131
2132         while (path != end && *path != L'\\')
2133                 path++;
2134
2135         return path;
2136 }
2137
2138 /*
2139  * Given a reparse point buffer for an inode for which the absolute link target
2140  * was relativized when it was archived, de-relative the link target to be
2141  * consistent with the actual extraction location.
2142  */
2143 static void
2144 try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p,
2145           struct win32_apply_ctx *ctx)
2146 {
2147         struct link_reparse_point link;
2148         size_t orig_subst_name_nchars;
2149         const wchar_t *relpath;
2150         size_t relpath_nchars;
2151         size_t target_ntpath_nchars;
2152         size_t fixed_subst_name_nchars;
2153         const wchar_t *fixed_print_name;
2154         size_t fixed_print_name_nchars;
2155
2156         /* Do nothing if the reparse data is invalid.  */
2157         if (parse_link_reparse_point(rpbuf, *rpbuflen_p, &link))
2158                 return;
2159
2160         /* Do nothing if the reparse point is a relative symbolic link.  */
2161         if (link_is_relative_symlink(&link))
2162                 return;
2163
2164         /* Build the new substitute name from the NT namespace path to the
2165          * target directory, then a path separator, then the "device relative"
2166          * part of the old substitute name.  */
2167
2168         orig_subst_name_nchars = link.substitute_name_nbytes / sizeof(wchar_t);
2169
2170         relpath = get_device_relative_path(link.substitute_name,
2171                                            orig_subst_name_nchars);
2172         relpath_nchars = orig_subst_name_nchars -
2173                          (relpath - link.substitute_name);
2174
2175         target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
2176
2177         /* If the target directory is a filesystem root, such as \??\C:\, then
2178          * it already will have a trailing slash.  Don't include this slash if
2179          * we are already adding slashes via 'relpath'.  This prevents an extra
2180          * slash from being generated each time the link is extracted.  And
2181          * unlike on UNIX, the number of slashes in paths on Windows can be
2182          * significant; Windows won't understand the link target if it contains
2183          * too many slashes.  */
2184         if (target_ntpath_nchars > 0 && relpath_nchars > 0 &&
2185             ctx->target_ntpath.Buffer[target_ntpath_nchars - 1] == L'\\')
2186                 target_ntpath_nchars--;
2187
2188         /* Also remove extra slashes from the beginning of 'relpath'.  Normally
2189          * this isn't needed, but this is here to make the extra slash(es) added
2190          * by wimlib pre-v1.9.1 get removed automatically.  */
2191         while (relpath_nchars >= 2 &&
2192                relpath[0] == L'\\' && relpath[1] == L'\\') {
2193                 relpath++;
2194                 relpath_nchars--;
2195         }
2196
2197         fixed_subst_name_nchars = target_ntpath_nchars + relpath_nchars;
2198
2199         wchar_t fixed_subst_name[fixed_subst_name_nchars];
2200
2201         wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer, target_ntpath_nchars);
2202         wmemcpy(&fixed_subst_name[target_ntpath_nchars], relpath, relpath_nchars);
2203         /* Doesn't need to be null-terminated.  */
2204
2205         /* Print name should be Win32, but not all NT names can even be
2206          * translated to Win32 names.  But we can at least delete the top-level
2207          * directory, such as \??\, and this will have the expected result in
2208          * the usual case.  */
2209         fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
2210                                                       fixed_subst_name_nchars);
2211         fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
2212                                                              fixed_subst_name);
2213
2214         link.substitute_name = fixed_subst_name;
2215         link.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
2216         link.print_name = (wchar_t *)fixed_print_name;
2217         link.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
2218         make_link_reparse_point(&link, rpbuf, rpbuflen_p);
2219 }
2220
2221 /* Sets the reparse point on the specified file.  This handles "fixing" the
2222  * targets of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX
2223  * was specified.  */
2224 static int
2225 set_reparse_point(const struct wim_dentry *dentry,
2226                   const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
2227                   struct win32_apply_ctx *ctx)
2228 {
2229         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
2230             && !(dentry->d_inode->i_rp_flags & WIM_RP_FLAG_NOT_FIXED))
2231         {
2232                 memcpy(&ctx->rpfixbuf, rpbuf, rpbuflen);
2233                 try_rpfix(&ctx->rpfixbuf, &rpbuflen, ctx);
2234                 rpbuf = &ctx->rpfixbuf;
2235         }
2236         return do_set_reparse_point(dentry, rpbuf, rpbuflen, ctx);
2237
2238 }
2239
2240 /* Import the next block of raw encrypted data  */
2241 static DWORD WINAPI
2242 import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
2243 {
2244         struct win32_apply_ctx *ctx = pvCallbackContext;
2245         ULONG copy_len;
2246
2247         copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
2248         memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
2249         ctx->encrypted_offset += copy_len;
2250         *Length = copy_len;
2251         return ERROR_SUCCESS;
2252 }
2253
2254 /*
2255  * Write the raw encrypted data to the already-created file (or directory)
2256  * corresponding to @dentry.
2257  *
2258  * The raw encrypted data is provided in ctx->data_buffer, and its size is
2259  * ctx->encrypted_size.
2260  *
2261  * This function may close the target directory, in which case the caller needs
2262  * to re-open it if needed.
2263  */
2264 static int
2265 extract_encrypted_file(const struct wim_dentry *dentry,
2266                        struct win32_apply_ctx *ctx)
2267 {
2268         void *rawctx;
2269         DWORD err;
2270         ULONG flags;
2271         bool retried;
2272
2273         /* Temporarily build a Win32 path for OpenEncryptedFileRaw()  */
2274         build_win32_extraction_path(dentry, ctx);
2275
2276         flags = CREATE_FOR_IMPORT | OVERWRITE_HIDDEN;
2277         if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
2278                 flags |= CREATE_FOR_DIR;
2279
2280         retried = false;
2281 retry:
2282         err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, flags, &rawctx);
2283         if (err == ERROR_SHARING_VIOLATION && !retried) {
2284                 /* This can be caused by the handle we have open to the target
2285                  * directory.  Try closing it temporarily.  */
2286                 close_target_directory(ctx);
2287                 retried = true;
2288                 goto retry;
2289         }
2290
2291         /* Restore the NT namespace path  */
2292         build_extraction_path(dentry, ctx);
2293
2294         if (err != ERROR_SUCCESS) {
2295                 win32_error(err, L"Can't open \"%ls\" for encrypted import",
2296                             current_path(ctx));
2297                 return WIMLIB_ERR_OPEN;
2298         }
2299
2300         ctx->encrypted_offset = 0;
2301
2302         err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx);
2303
2304         CloseEncryptedFileRaw(rawctx);
2305
2306         if (err != ERROR_SUCCESS) {
2307                 win32_error(err, L"Can't import encrypted file \"%ls\"",
2308                             current_path(ctx));
2309                 return WIMLIB_ERR_WRITE;
2310         }
2311
2312         return 0;
2313 }
2314
2315 /* Called when starting to read a blob for extraction */
2316 static int
2317 win32_begin_extract_blob(struct blob_descriptor *blob, void *_ctx)
2318 {
2319         struct win32_apply_ctx *ctx = _ctx;
2320         const struct blob_extraction_target *targets = blob_extraction_targets(blob);
2321         int ret;
2322
2323         ctx->num_open_handles = 0;
2324         ctx->data_buffer_ptr = NULL;
2325         ctx->any_sparse_streams = false;
2326         INIT_LIST_HEAD(&ctx->reparse_dentries);
2327         INIT_LIST_HEAD(&ctx->encrypted_dentries);
2328
2329         for (u32 i = 0; i < blob->out_refcnt; i++) {
2330                 const struct wim_inode *inode = targets[i].inode;
2331                 const struct wim_inode_stream *strm = targets[i].stream;
2332                 struct wim_dentry *dentry;
2333
2334                 /* A copy of the blob needs to be extracted to @inode.  */
2335
2336                 if (ctx->common.supported_features.hard_links) {
2337                         dentry = inode_first_extraction_dentry(inode);
2338                         ret = begin_extract_blob_instance(blob, dentry, strm, ctx);
2339                         ret = check_apply_error(dentry, ctx, ret);
2340                         if (ret)
2341                                 goto fail;
2342                 } else {
2343                         /* Hard links not supported.  Extract the blob
2344                          * separately to each alias of the inode.  */
2345                         inode_for_each_extraction_alias(dentry, inode) {
2346                                 ret = begin_extract_blob_instance(blob, dentry, strm, ctx);
2347                                 ret = check_apply_error(dentry, ctx, ret);
2348                                 if (ret)
2349                                         goto fail;
2350                         }
2351                 }
2352         }
2353
2354         return 0;
2355
2356 fail:
2357         close_handles(ctx);
2358         return ret;
2359 }
2360
2361 static int
2362 pwrite_to_handle(HANDLE h, const void *data, size_t size, u64 offset)
2363 {
2364         const void * const end = data + size;
2365         const void *p;
2366         IO_STATUS_BLOCK iosb;
2367         NTSTATUS status;
2368
2369         for (p = data; p != end; p += iosb.Information,
2370                                  offset += iosb.Information)
2371         {
2372                 LARGE_INTEGER offs = { .QuadPart = offset };
2373
2374                 status = NtWriteFile(h, NULL, NULL, NULL, &iosb,
2375                                      (void *)p, min(INT32_MAX, end - p),
2376                                      &offs, NULL);
2377                 if (!NT_SUCCESS(status)) {
2378                         winnt_error(status,
2379                                     L"Error writing data to target volume");
2380                         return WIMLIB_ERR_WRITE;
2381                 }
2382         }
2383         return 0;
2384 }
2385
2386 /* Called when the next chunk of a blob has been read for extraction */
2387 static int
2388 win32_extract_chunk(const struct blob_descriptor *blob, u64 offset,
2389                     const void *chunk, size_t size, void *_ctx)
2390 {
2391         struct win32_apply_ctx *ctx = _ctx;
2392         const void * const end = chunk + size;
2393         const void *p;
2394         bool zeroes;
2395         size_t len;
2396         unsigned i;
2397         int ret;
2398
2399         /*
2400          * For sparse streams, only write nonzero regions.  This lets the
2401          * filesystem use holes to represent zero regions.
2402          */
2403         for (p = chunk; p != end; p += len, offset += len) {
2404                 zeroes = maybe_detect_sparse_region(p, end - p, &len,
2405                                                     ctx->any_sparse_streams);
2406                 for (i = 0; i < ctx->num_open_handles; i++) {
2407                         if (!zeroes || !ctx->is_sparse_stream[i]) {
2408                                 ret = pwrite_to_handle(ctx->open_handles[i],
2409                                                        p, len, offset);
2410                                 if (ret)
2411                                         return ret;
2412                         }
2413                 }
2414         }
2415
2416         /* Copy the data chunk into the buffer (if needed)  */
2417         if (ctx->data_buffer_ptr)
2418                 ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
2419                                                chunk, size);
2420         return 0;
2421 }
2422
2423 static int
2424 get_system_compression_format(int extract_flags)
2425 {
2426         if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K)
2427                 return FILE_PROVIDER_COMPRESSION_XPRESS4K;
2428
2429         if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K)
2430                 return FILE_PROVIDER_COMPRESSION_XPRESS8K;
2431
2432         if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K)
2433                 return FILE_PROVIDER_COMPRESSION_XPRESS16K;
2434
2435         return FILE_PROVIDER_COMPRESSION_LZX;
2436 }
2437
2438
2439 static const wchar_t *
2440 get_system_compression_format_string(int format)
2441 {
2442         switch (format) {
2443         case FILE_PROVIDER_COMPRESSION_XPRESS4K:
2444                 return L"XPRESS4K";
2445         case FILE_PROVIDER_COMPRESSION_XPRESS8K:
2446                 return L"XPRESS8K";
2447         case FILE_PROVIDER_COMPRESSION_XPRESS16K:
2448                 return L"XPRESS16K";
2449         default:
2450                 return L"LZX";
2451         }
2452 }
2453
2454 static NTSTATUS
2455 set_system_compression(HANDLE h, int format)
2456 {
2457         NTSTATUS status;
2458         struct {
2459                 WOF_EXTERNAL_INFO wof_info;
2460                 FILE_PROVIDER_EXTERNAL_INFO_V1 file_info;
2461         } in = {
2462                 .wof_info = {
2463                         .Version = WOF_CURRENT_VERSION,
2464                         .Provider = WOF_PROVIDER_FILE,
2465                 },
2466                 .file_info = {
2467                         .Version = FILE_PROVIDER_CURRENT_VERSION,
2468                         .Algorithm = format,
2469                 },
2470         };
2471
2472         /* We intentionally use NtFsControlFile() rather than DeviceIoControl()
2473          * here because the "compressing this object would not save space"
2474          * status code does not map to a valid Win32 error code on older
2475          * versions of Windows (before Windows 10?).  This can be a problem if
2476          * the WOFADK driver is being used rather than the regular WOF, since
2477          * WOFADK can be used on older versions of Windows.  */
2478         status = winnt_fsctl(h, FSCTL_SET_EXTERNAL_BACKING,
2479                              &in, sizeof(in), NULL, 0, NULL);
2480
2481         if (status == 0xC000046F) /* "Compressing this object would not save space."  */
2482                 return STATUS_SUCCESS;
2483
2484         return status;
2485 }
2486
2487 /* Hard-coded list of files which the Windows bootloader may need to access
2488  * before the WOF driver has been loaded.  */
2489 static const wchar_t * const bootloader_pattern_strings[] = {
2490         L"*winload.*",
2491         L"*winresume.*",
2492         L"\\Windows\\AppPatch\\drvmain.sdb",
2493         L"\\Windows\\Boot\\DVD\\*",
2494         L"\\Windows\\Boot\\EFI\\*",
2495         L"\\Windows\\bootstat.dat",
2496         L"\\Windows\\Fonts\\vgaoem.fon",
2497         L"\\Windows\\Fonts\\vgasys.fon",
2498         L"\\Windows\\INF\\errata.inf",
2499         L"\\Windows\\System32\\config\\*",
2500         L"\\Windows\\System32\\ntkrnlpa.exe",
2501         L"\\Windows\\System32\\ntoskrnl.exe",
2502         L"\\Windows\\System32\\bootvid.dll",
2503         L"\\Windows\\System32\\ci.dll",
2504         L"\\Windows\\System32\\hal*.dll",
2505         L"\\Windows\\System32\\mcupdate_AuthenticAMD.dll",
2506         L"\\Windows\\System32\\mcupdate_GenuineIntel.dll",
2507         L"\\Windows\\System32\\pshed.dll",
2508         L"\\Windows\\System32\\apisetschema.dll",
2509         L"\\Windows\\System32\\api-ms-win*.dll",
2510         L"\\Windows\\System32\\ext-ms-win*.dll",
2511         L"\\Windows\\System32\\KernelBase.dll",
2512         L"\\Windows\\System32\\drivers\\*.sys",
2513         L"\\Windows\\System32\\*.nls",
2514         L"\\Windows\\System32\\kbd*.dll",
2515         L"\\Windows\\System32\\kd*.dll",
2516         L"\\Windows\\System32\\clfs.sys",
2517         L"\\Windows\\System32\\CodeIntegrity\\driver.stl",
2518 };
2519
2520 static const struct string_list bootloader_patterns = {
2521         .strings = (wchar_t **)bootloader_pattern_strings,
2522         .num_strings = ARRAY_LEN(bootloader_pattern_strings),
2523 };
2524
2525 /* Returns true if the specified system compression format is supported by the
2526  * bootloader of the image being applied.  */
2527 static bool
2528 bootloader_supports_compression_format(struct win32_apply_ctx *ctx, int format)
2529 {
2530         /* Windows 10 and later support XPRESS4K */
2531         if (format == FILE_PROVIDER_COMPRESSION_XPRESS4K)
2532                 return ctx->windows_build_number >= 10240;
2533
2534         /*
2535          * Windows 10 version 1903 and later support the other formats;
2536          * see https://wimlib.net/forums/viewtopic.php?f=1&t=444
2537          */
2538         return ctx->windows_build_number >= 18362;
2539 }
2540
2541 static NTSTATUS
2542 set_system_compression_on_inode(struct wim_inode *inode, int format,
2543                                 struct win32_apply_ctx *ctx)
2544 {
2545         bool retried = false;
2546         NTSTATUS status;
2547         HANDLE h;
2548
2549         /* If it may be needed for compatibility with the Windows bootloader,
2550          * force this file to XPRESS4K or uncompressed format.  */
2551         if (!bootloader_supports_compression_format(ctx, format)) {
2552                 /* We need to check the patterns against every name of the
2553                  * inode, in case any of them match.  */
2554                 struct wim_dentry *dentry;
2555                 inode_for_each_extraction_alias(dentry, inode) {
2556                         bool incompatible;
2557                         bool warned;
2558
2559                         if (calculate_dentry_full_path(dentry)) {
2560                                 ERROR("Unable to compute file path!");
2561                                 return STATUS_NO_MEMORY;
2562                         }
2563
2564                         incompatible = match_pattern_list(dentry->d_full_path,
2565                                                           &bootloader_patterns,
2566                                                           MATCH_RECURSIVELY);
2567                         FREE(dentry->d_full_path);
2568                         dentry->d_full_path = NULL;
2569
2570                         if (!incompatible)
2571                                 continue;
2572
2573                         warned = (ctx->num_system_compression_exclusions++ > 0);
2574
2575                         if (bootloader_supports_compression_format(ctx,
2576                                    FILE_PROVIDER_COMPRESSION_XPRESS4K))
2577                         {
2578                                 /* Force to XPRESS4K  */
2579                                 if (!warned) {
2580                                         WARNING("For compatibility with the "
2581                                                 "Windows bootloader, some "
2582                                                 "files are being\n"
2583                                                 "          compacted "
2584                                                 "using the XPRESS4K format "
2585                                                 "instead of the %"TS" format\n"
2586                                                 "          you requested.",
2587                                                 get_system_compression_format_string(format));
2588                                 }
2589                                 format = FILE_PROVIDER_COMPRESSION_XPRESS4K;
2590                                 break;
2591                         } else {
2592                                 /* Force to uncompressed  */
2593                                 if (!warned) {
2594                                         WARNING("For compatibility with the "
2595                                                 "Windows bootloader, some "
2596                                                 "files will not\n"
2597                                                 "          be compressed with"
2598                                                 " system compression "
2599                                                 "(\"compacted\").");
2600                                 }
2601                                 return STATUS_SUCCESS;
2602                         }
2603
2604                 }
2605         }
2606
2607         /* Open the extracted file.  */
2608         status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL,
2609                              0, FILE_OPEN, 0,
2610                              inode_first_extraction_dentry(inode), ctx);
2611
2612         if (!NT_SUCCESS(status))
2613                 return status;
2614 retry:
2615         /* Compress the file.  If the attempt fails with "invalid device
2616          * request", then attach wof.sys (or wofadk.sys) and retry.  */
2617         status = set_system_compression(h, format);
2618         if (unlikely(status == STATUS_INVALID_DEVICE_REQUEST && !retried)) {
2619                 wchar_t drive_path[7];
2620                 if (!win32_get_drive_path(ctx->common.target, drive_path) &&
2621                     win32_try_to_attach_wof(drive_path + 4)) {
2622                         retried = true;
2623                         goto retry;
2624                 }
2625         }
2626
2627         NtClose(h);
2628         return status;
2629 }
2630
2631 /*
2632  * This function is called when doing a "compact-mode" extraction and we just
2633  * finished extracting a blob to one or more locations.  For each location that
2634  * was the unnamed data stream of a file, this function compresses the
2635  * corresponding file using System Compression, if allowed.
2636  *
2637  * Note: we're doing the compression immediately after extracting the data
2638  * rather than during a separate compression pass.  This way should be faster
2639  * since the operating system should still have the file's data cached.
2640  *
2641  * Note: we're having the operating system do the compression, which is not
2642  * ideal because wimlib could create the compressed data faster and more
2643  * efficiently (the compressed data format is identical to a WIM resource).  But
2644  * we seemingly don't have a choice because WOF prevents applications from
2645  * creating its reparse points.
2646  */
2647 static void
2648 handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *ctx)
2649 {
2650         const struct blob_extraction_target *targets = blob_extraction_targets(blob);
2651
2652         const int format = get_system_compression_format(ctx->common.extract_flags);
2653
2654         for (u32 i = 0; i < blob->out_refcnt; i++) {
2655                 struct wim_inode *inode = targets[i].inode;
2656                 struct wim_inode_stream *strm = targets[i].stream;
2657                 NTSTATUS status;
2658
2659                 if (!stream_is_unnamed_data_stream(strm))
2660                         continue;
2661
2662                 if (will_externally_back_inode(inode, ctx, NULL, false) != 0)
2663                         continue;
2664
2665                 status = set_system_compression_on_inode(inode, format, ctx);
2666                 if (likely(NT_SUCCESS(status)))
2667                         continue;
2668
2669                 if (status == STATUS_INVALID_DEVICE_REQUEST) {
2670                         WARNING(
2671           "The request to compress the extracted files using System Compression\n"
2672 "          will not be honored because the operating system or target volume\n"
2673 "          does not support it.  System Compression is only supported on\n"
2674 "          Windows 10 and later, and only on NTFS volumes.");
2675                         ctx->common.extract_flags &= ~COMPACT_FLAGS;
2676                         return;
2677                 }
2678
2679                 ctx->num_system_compression_failures++;
2680                 if (ctx->num_system_compression_failures < 10) {
2681                         winnt_warning(status, L"\"%ls\": Failed to compress "
2682                                       "extracted file using System Compression",
2683                                       current_path(ctx));
2684                 } else if (ctx->num_system_compression_failures == 10) {
2685                         WARNING("Suppressing further warnings about "
2686                                 "System Compression failures.");
2687                 }
2688         }
2689 }
2690
2691 /* Called when a blob has been fully read for extraction */
2692 static int
2693 win32_end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
2694 {
2695         struct win32_apply_ctx *ctx = _ctx;
2696         int ret;
2697         const struct wim_dentry *dentry;
2698
2699         /* Extend sparse streams to their final size. */
2700         if (ctx->any_sparse_streams && !status) {
2701                 for (unsigned i = 0; i < ctx->num_open_handles; i++) {
2702                         FILE_END_OF_FILE_INFORMATION info =
2703                                 { .EndOfFile = { .QuadPart = blob->size } };
2704                         NTSTATUS ntstatus;
2705
2706                         if (!ctx->is_sparse_stream[i])
2707                                 continue;
2708
2709                         ntstatus = NtSetInformationFile(ctx->open_handles[i],
2710                                                         &ctx->iosb,
2711                                                         &info, sizeof(info),
2712                                                         FileEndOfFileInformation);
2713                         if (!NT_SUCCESS(ntstatus)) {
2714                                 winnt_error(ntstatus, L"Error writing data to "
2715                                             "target volume (while extending)");
2716                                 status = WIMLIB_ERR_WRITE;
2717                                 break;
2718                         }
2719                 }
2720         }
2721
2722         close_handles(ctx);
2723
2724         if (status)
2725                 return status;
2726
2727         if (unlikely(ctx->common.extract_flags & COMPACT_FLAGS))
2728                 handle_system_compression(blob, ctx);
2729
2730         if (likely(!ctx->data_buffer_ptr))
2731                 return 0;
2732
2733         if (!list_empty(&ctx->reparse_dentries)) {
2734                 if (blob->size > REPARSE_DATA_MAX_SIZE) {
2735                         dentry = list_first_entry(&ctx->reparse_dentries,
2736                                                   struct wim_dentry, d_tmp_list);
2737                         build_extraction_path(dentry, ctx);
2738                         ERROR("Reparse data of \"%ls\" has size "
2739                               "%"PRIu64" bytes (exceeds %u bytes)",
2740                               current_path(ctx), blob->size,
2741                               REPARSE_DATA_MAX_SIZE);
2742                         ret = WIMLIB_ERR_INVALID_REPARSE_DATA;
2743                         return check_apply_error(dentry, ctx, ret);
2744                 }
2745                 /* Reparse data  */
2746                 memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, blob->size);
2747
2748                 list_for_each_entry(dentry, &ctx->reparse_dentries, d_tmp_list) {
2749
2750                         /* Reparse point header  */
2751                         complete_reparse_point(&ctx->rpbuf, dentry->d_inode,
2752                                                blob->size);
2753
2754                         ret = set_reparse_point(dentry, &ctx->rpbuf,
2755                                                 REPARSE_DATA_OFFSET + blob->size,
2756                                                 ctx);
2757                         ret = check_apply_error(dentry, ctx, ret);
2758                         if (ret)
2759                                 return ret;
2760                 }
2761         }
2762
2763         if (!list_empty(&ctx->encrypted_dentries)) {
2764                 ctx->encrypted_size = blob->size;
2765                 list_for_each_entry(dentry, &ctx->encrypted_dentries, d_tmp_list) {
2766                         ret = extract_encrypted_file(dentry, ctx);
2767                         ret = check_apply_error(dentry, ctx, ret);
2768                         if (ret)
2769                                 return ret;
2770                         /* Re-open the target directory if needed.  */
2771                         ret = open_target_directory(ctx);
2772                         if (ret)
2773                                 return ret;
2774                 }
2775         }
2776
2777         return 0;
2778 }
2779
2780 /* Attributes that can't be set directly  */
2781 #define SPECIAL_ATTRIBUTES                      \
2782         (FILE_ATTRIBUTE_REPARSE_POINT   |       \
2783          FILE_ATTRIBUTE_DIRECTORY       |       \
2784          FILE_ATTRIBUTE_ENCRYPTED       |       \
2785          FILE_ATTRIBUTE_SPARSE_FILE     |       \
2786          FILE_ATTRIBUTE_COMPRESSED)
2787
2788 static void
2789 set_object_id(HANDLE h, const struct wim_inode *inode,
2790               struct win32_apply_ctx *ctx)
2791 {
2792         const void *object_id;
2793         u32 len;
2794         NTSTATUS status;
2795
2796         if (!ctx->common.supported_features.object_ids)
2797                 return;
2798
2799         object_id = inode_get_object_id(inode, &len);
2800         if (likely(object_id == NULL))  /* No object ID?  */
2801                 return;
2802
2803         status = winnt_fsctl(h, FSCTL_SET_OBJECT_ID,
2804                              object_id, len, NULL, 0, NULL);
2805         if (NT_SUCCESS(status))
2806                 return;
2807
2808         /* Object IDs must be unique within the filesystem.  A duplicate might
2809          * occur if an image containing object IDs is applied twice to the same
2810          * filesystem.  Arguably, the user should be warned in this case; but
2811          * the reality seems to be that nothing important cares about object IDs
2812          * except the Distributed Link Tracking Service... so for now these
2813          * failures are just ignored.  */
2814         if (status == STATUS_DUPLICATE_NAME ||
2815             status == STATUS_OBJECT_NAME_COLLISION)
2816                 return;
2817
2818         ctx->num_object_id_failures++;
2819         if (ctx->num_object_id_failures < 10) {
2820                 winnt_warning(status, L"Can't set object ID on \"%ls\"",
2821                               current_path(ctx));
2822         } else if (ctx->num_object_id_failures == 10) {
2823                 WARNING("Suppressing further warnings about failure to set "
2824                         "object IDs.");
2825         }
2826 }
2827
2828 static int
2829 set_xattrs(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx)
2830 {
2831         const void *entries, *entries_end;
2832         u32 len;
2833         const struct wim_xattr_entry *entry;
2834         size_t bufsize = 0;
2835         u8 _buf[1024] _aligned_attribute(4);
2836         u8 *buf = _buf;
2837         FILE_FULL_EA_INFORMATION *ea, *ea_prev;
2838         NTSTATUS status;
2839         int ret;
2840
2841         if (!ctx->common.supported_features.xattrs)
2842                 return 0;
2843
2844         entries = inode_get_xattrs(inode, &len);
2845         if (likely(entries == NULL || len == 0))  /* No extended attributes? */
2846                 return 0;
2847         entries_end = entries + len;
2848
2849         entry = entries;
2850         for (entry = entries; (void *)entry < entries_end;
2851              entry = xattr_entry_next(entry)) {
2852                 if (!valid_xattr_entry(entry, entries_end - (void *)entry)) {
2853                         ERROR("\"%"TS"\": extended attribute is corrupt or unsupported",
2854                               inode_any_full_path(inode));
2855                         return WIMLIB_ERR_INVALID_XATTR;
2856                 }
2857
2858                 bufsize += ALIGN(offsetof(FILE_FULL_EA_INFORMATION, EaName) +
2859                                  entry->name_len + 1 +
2860                                  le16_to_cpu(entry->value_len), 4);
2861         }
2862
2863         if (unlikely(bufsize != (u32)bufsize)) {
2864                 ERROR("\"%"TS"\": too many extended attributes to extract!",
2865                       inode_any_full_path(inode));
2866                 return WIMLIB_ERR_INVALID_XATTR;
2867         }
2868
2869         if (unlikely(bufsize > sizeof(_buf))) {
2870                 buf = MALLOC(bufsize);
2871                 if (!buf)
2872                         return WIMLIB_ERR_NOMEM;
2873         }
2874
2875         ea_prev = NULL;
2876         ea = (FILE_FULL_EA_INFORMATION *)buf;
2877         for (entry = entries; (void *)entry < entries_end;
2878              entry = xattr_entry_next(entry)) {
2879                 u8 *p;
2880
2881                 if (ea_prev)
2882                         ea_prev->NextEntryOffset = (u8 *)ea - (u8 *)ea_prev;
2883                 ea->Flags = entry->flags;
2884                 ea->EaNameLength = entry->name_len;
2885                 ea->EaValueLength = le16_to_cpu(entry->value_len);
2886                 p = mempcpy(ea->EaName, entry->name,
2887                             ea->EaNameLength + 1 + ea->EaValueLength);
2888                 while ((uintptr_t)p & 3)
2889                         *p++ = 0;
2890                 ea_prev = ea;
2891                 ea = (FILE_FULL_EA_INFORMATION *)p;
2892         }
2893         ea_prev->NextEntryOffset = 0;
2894         wimlib_assert((u8 *)ea - buf == bufsize);
2895
2896         status = NtSetEaFile(h, &ctx->iosb, buf, bufsize);
2897         if (unlikely(!NT_SUCCESS(status))) {
2898                 if (status == STATUS_EAS_NOT_SUPPORTED) {
2899                         /* This happens with Samba. */
2900                         WARNING("Filesystem advertised extended attribute (EA) support, but it doesn't\n"
2901                                 "          work.  EAs will not be extracted.");
2902                         ctx->common.supported_features.xattrs = 0;
2903                 } else if (status == STATUS_INVALID_EA_NAME) {
2904                         ctx->num_xattr_failures++;
2905                         if (ctx->num_xattr_failures < 5) {
2906                                 winnt_warning(status,
2907                                               L"Can't set extended attributes on \"%ls\"",
2908                                               current_path(ctx));
2909                         } else if (ctx->num_xattr_failures == 5) {
2910                                 WARNING("Suppressing further warnings about "
2911                                         "failure to set extended attributes.");
2912                         }
2913                 } else {
2914                         winnt_error(status, L"Can't set extended attributes on \"%ls\"",
2915                                     current_path(ctx));
2916                         ret = WIMLIB_ERR_SET_XATTR;
2917                         goto out;
2918                 }
2919         }
2920         ret = 0;
2921 out:
2922         if (buf != _buf)
2923                 FREE(buf);
2924         return ret;
2925 }
2926
2927 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
2928  * handle @h.  */
2929 static NTSTATUS
2930 set_security_descriptor(HANDLE h, const void *_desc,
2931                         size_t desc_size, struct win32_apply_ctx *ctx)
2932 {
2933         SECURITY_INFORMATION info;
2934         NTSTATUS status;
2935         SECURITY_DESCRIPTOR_RELATIVE *desc;
2936
2937         /*
2938          * Ideally, we would just pass in the security descriptor buffer as-is.
2939          * But it turns out that Windows can mess up the security descriptor
2940          * even when using the low-level NtSetSecurityObject() function:
2941          *
2942          * - Windows will clear SE_DACL_AUTO_INHERITED if it is set in the
2943          *   passed buffer.  To actually get Windows to set
2944          *   SE_DACL_AUTO_INHERITED, the application must set the non-persistent
2945          *   flag SE_DACL_AUTO_INHERIT_REQ.  As usual, Microsoft didn't bother
2946          *   to properly document either of these flags.  It's unclear how
2947          *   important SE_DACL_AUTO_INHERITED actually is, but to be safe we use
2948          *   the SE_DACL_AUTO_INHERIT_REQ workaround to set it if needed.
2949          *
2950          * - The above also applies to the equivalent SACL flags,
2951          *   SE_SACL_AUTO_INHERITED and SE_SACL_AUTO_INHERIT_REQ.
2952          *
2953          * - If the application says that it's setting
2954          *   DACL_SECURITY_INFORMATION, then Windows sets SE_DACL_PRESENT in the
2955          *   resulting security descriptor, even if the security descriptor the
2956          *   application provided did not have a DACL.  This seems to be
2957          *   unavoidable, since omitting DACL_SECURITY_INFORMATION would cause a
2958          *   default DACL to remain.  Fortunately, this behavior seems harmless,
2959          *   since the resulting DACL will still be "null" --- but it will be
2960          *   "the other representation of null".
2961          *
2962          * - The above also applies to SACL_SECURITY_INFORMATION and
2963          *   SE_SACL_PRESENT.  Again, it's seemingly unavoidable but "harmless"
2964          *   that Windows changes the representation of a "null SACL".
2965          */
2966         if (likely(desc_size <= STACK_MAX)) {
2967                 desc = alloca(desc_size);
2968         } else {
2969                 desc = MALLOC(desc_size);
2970                 if (!desc)
2971                         return STATUS_NO_MEMORY;
2972         }
2973
2974         memcpy(desc, _desc, desc_size);
2975
2976         if (likely(desc_size >= 4)) {
2977
2978                 if (desc->Control & SE_DACL_AUTO_INHERITED)
2979                         desc->Control |= SE_DACL_AUTO_INHERIT_REQ;
2980
2981                 if (desc->Control & SE_SACL_AUTO_INHERITED)
2982                         desc->Control |= SE_SACL_AUTO_INHERIT_REQ;
2983         }
2984
2985         /*
2986          * More API insanity.  We want to set the entire security descriptor
2987          * as-is.  But all available APIs require specifying the specific parts
2988          * of the security descriptor being set.  Especially annoying is that
2989          * mandatory integrity labels are part of the SACL, but they aren't set
2990          * with SACL_SECURITY_INFORMATION.  Instead, applications must also
2991          * specify LABEL_SECURITY_INFORMATION (Windows Vista, Windows 7) or
2992          * BACKUP_SECURITY_INFORMATION (Windows 8).  But at least older versions
2993          * of Windows don't error out if you provide these newer flags...
2994          *
2995          * Also, if the process isn't running as Administrator, then it probably
2996          * doesn't have SE_RESTORE_PRIVILEGE.  In this case, it will always get
2997          * the STATUS_PRIVILEGE_NOT_HELD error by trying to set the SACL, even
2998          * if the security descriptor it provided did not have a SACL.  By
2999          * default, in this case we try to recover and set as much of the
3000          * security descriptor as possible --- potentially excluding the DACL, and
3001          * even the owner, as well as the SACL.
3002          */
3003
3004         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
3005                DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION |
3006                LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION;
3007
3008
3009         /*
3010          * It's also worth noting that SetFileSecurity() is unusable because it
3011          * doesn't request "backup semantics" when it opens the file internally.
3012          * NtSetSecurityObject() seems to be the best function to use in backup
3013          * applications.  (SetSecurityInfo() should also work, but it's harder
3014          * to use and must call NtSetSecurityObject() internally anyway.
3015          * BackupWrite() is theoretically usable as well, but it's inflexible
3016          * and poorly documented.)
3017          */
3018
3019 retry:
3020         status = NtSetSecurityObject(h, info, desc);
3021         if (NT_SUCCESS(status))
3022                 goto out_maybe_free_desc;
3023
3024         /* Failed to set the requested parts of the security descriptor.  If the
3025          * error was permissions-related, try to set fewer parts of the security
3026          * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
3027         if ((status == STATUS_PRIVILEGE_NOT_HELD ||
3028              status == STATUS_ACCESS_DENIED) &&
3029             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
3030         {
3031                 if (info & SACL_SECURITY_INFORMATION) {
3032                         info &= ~(SACL_SECURITY_INFORMATION |
3033                                   LABEL_SECURITY_INFORMATION |
3034                                   BACKUP_SECURITY_INFORMATION);
3035                         ctx->partial_security_descriptors++;
3036                         goto retry;
3037                 }
3038                 if (info & DACL_SECURITY_INFORMATION) {
3039                         info &= ~DACL_SECURITY_INFORMATION;
3040                         goto retry;
3041                 }
3042                 if (info & OWNER_SECURITY_INFORMATION) {
3043                         info &= ~OWNER_SECURITY_INFORMATION;
3044                         goto retry;
3045                 }
3046                 /* Nothing left except GROUP, and if we removed it we
3047                  * wouldn't have anything at all.  */
3048         }
3049
3050         /* No part of the security descriptor could be set, or
3051          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security
3052          * descriptor could not be set.  */
3053         if (!(info & SACL_SECURITY_INFORMATION))
3054                 ctx->partial_security_descriptors--;
3055         ctx->no_security_descriptors++;
3056
3057 out_maybe_free_desc:
3058         if (unlikely(desc_size > STACK_MAX))
3059                 FREE(desc);
3060         return status;
3061 }
3062
3063 /* Set metadata on the open file @h from the WIM inode @inode.  */
3064 static int
3065 do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
3066                           struct win32_apply_ctx *ctx)
3067 {
3068         FILE_BASIC_INFORMATION info;
3069         NTSTATUS status;
3070         int ret;
3071
3072         /* Set the file's object ID if present and object IDs are supported by
3073          * the filesystem.  */
3074         set_object_id(h, inode, ctx);
3075
3076         /* Set the file's extended attributes (EAs) if present and EAs are
3077          * supported by the filesystem.  */
3078         ret = set_xattrs(h, inode, ctx);
3079         if (ret)
3080                 return ret;
3081
3082         /* Set the file's security descriptor if present and we're not in
3083          * NO_ACLS mode  */
3084         if (inode_has_security_descriptor(inode) &&
3085             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
3086         {
3087                 const struct wim_security_data *sd;
3088                 const void *desc;
3089                 size_t desc_size;
3090
3091                 sd = wim_get_current_security_data(ctx->common.wim);
3092                 desc = sd->descriptors[inode->i_security_id];
3093                 desc_size = sd->sizes[inode->i_security_id];
3094
3095                 status = set_security_descriptor(h, desc, desc_size, ctx);
3096                 if (!NT_SUCCESS(status) &&
3097                     (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
3098                 {
3099                         winnt_error(status,
3100                                     L"Can't set security descriptor on \"%ls\"",
3101                                     current_path(ctx));
3102                         return WIMLIB_ERR_SET_SECURITY;
3103                 }
3104         }
3105
3106         /* Set attributes and timestamps  */
3107         info.CreationTime.QuadPart = inode->i_creation_time;
3108         info.LastAccessTime.QuadPart = inode->i_last_access_time;
3109         info.LastWriteTime.QuadPart = inode->i_last_write_time;
3110         info.ChangeTime.QuadPart = 0;
3111         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) {
3112                 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
3113         } else {
3114                 info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES;
3115                 if (info.FileAttributes == 0)
3116                         info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
3117         }
3118
3119         status = NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info),
3120                                       FileBasicInformation);
3121         /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set
3122          * attributes on the root directory.  (Apparently because FAT doesn't
3123          * actually have a place to store those attributes!)  */
3124         if (!NT_SUCCESS(status)
3125             && !(status == STATUS_INVALID_PARAMETER &&
3126                  dentry_is_root(inode_first_extraction_dentry(inode))))
3127         {
3128                 winnt_error(status, L"Can't set basic metadata on \"%ls\"",
3129                             current_path(ctx));
3130                 return WIMLIB_ERR_SET_ATTRIBUTES;
3131         }
3132
3133         return 0;
3134 }
3135
3136 static int
3137 apply_metadata_to_file(const struct wim_dentry *dentry,
3138                        struct win32_apply_ctx *ctx)
3139 {
3140         const struct wim_inode *inode = dentry->d_inode;
3141         DWORD perms;
3142         HANDLE h;
3143         NTSTATUS status;
3144         int ret;
3145
3146         perms = FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | WRITE_DAC |
3147                 WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
3148
3149         build_extraction_path(dentry, ctx);
3150
3151         /* Open a handle with as many relevant permissions as possible.  */
3152         while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL,
3153                                                    0, FILE_OPEN, 0, ctx)))
3154         {
3155                 if (status == STATUS_PRIVILEGE_NOT_HELD ||
3156                     status == STATUS_ACCESS_DENIED)
3157                 {
3158                         if (perms & ACCESS_SYSTEM_SECURITY) {
3159                                 perms &= ~ACCESS_SYSTEM_SECURITY;
3160                                 continue;
3161                         }
3162                         if (perms & WRITE_DAC) {
3163                                 perms &= ~WRITE_DAC;
3164                                 continue;
3165                         }
3166                         if (perms & WRITE_OWNER) {
3167                                 perms &= ~WRITE_OWNER;
3168                                 continue;
3169                         }
3170                 }
3171                 winnt_error(status, L"Can't open \"%ls\" to set metadata",
3172                             current_path(ctx));
3173                 return WIMLIB_ERR_OPEN;
3174         }
3175
3176         ret = do_apply_metadata_to_file(h, inode, ctx);
3177
3178         NtClose(h);
3179
3180         return ret;
3181 }
3182
3183 static int
3184 apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
3185 {
3186         const struct wim_dentry *dentry;
3187         int ret;
3188
3189         /* We go in reverse so that metadata is set on all a directory's
3190          * children before the directory itself.  This avoids any potential
3191          * problems with attributes, timestamps, or security descriptors.  */
3192         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node)
3193         {
3194                 ret = apply_metadata_to_file(dentry, ctx);
3195                 ret = check_apply_error(dentry, ctx, ret);
3196                 if (ret)
3197                         return ret;
3198                 ret = report_file_metadata_applied(&ctx->common);
3199                 if (ret)
3200                         return ret;
3201         }
3202         return 0;
3203 }
3204
3205 /* Issue warnings about problems during the extraction for which warnings were
3206  * not already issued (due to the high number of potential warnings if we issued
3207  * them per-file).  */
3208 static void
3209 do_warnings(const struct win32_apply_ctx *ctx)
3210 {
3211         if (ctx->partial_security_descriptors == 0
3212             && ctx->no_security_descriptors == 0
3213             && ctx->num_set_short_name_failures == 0
3214         #if 0
3215             && ctx->num_remove_short_name_failures == 0
3216         #endif
3217             )
3218                 return;
3219
3220         WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
3221                 ctx->common.target);
3222         if (ctx->num_set_short_name_failures) {
3223                 WARNING("- Could not set short names on %lu files or directories",
3224                         ctx->num_set_short_name_failures);
3225         }
3226 #if 0
3227         if (ctx->num_remove_short_name_failures) {
3228                 WARNING("- Could not remove short names on %lu files or directories"
3229                         "          (This is expected on Vista and earlier)",
3230                         ctx->num_remove_short_name_failures);
3231         }
3232 #endif
3233         if (ctx->partial_security_descriptors) {
3234                 WARNING("- Could only partially set the security descriptor\n"
3235                         "            on %lu files or directories.",
3236                         ctx->partial_security_descriptors);
3237         }
3238         if (ctx->no_security_descriptors) {
3239                 WARNING("- Could not set security descriptor at all\n"
3240                         "            on %lu files or directories.",
3241                         ctx->no_security_descriptors);
3242         }
3243         if (ctx->partial_security_descriptors || ctx->no_security_descriptors) {
3244                 WARNING("To fully restore all security descriptors, run the program\n"
3245                         "          with Administrator rights.");
3246         }
3247 }
3248
3249 static u64
3250 count_dentries(const struct list_head *dentry_list)
3251 {
3252         const struct list_head *cur;
3253         u64 count = 0;
3254
3255         list_for_each(cur, dentry_list)
3256                 count++;
3257
3258         return count;
3259 }
3260
3261 /* Extract files from a WIM image to a directory on Windows  */
3262 static int
3263 win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
3264 {
3265         int ret;
3266         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
3267         u64 dentry_count;
3268
3269         ret = prepare_target(dentry_list, ctx);
3270         if (ret)
3271                 goto out;
3272
3273         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
3274                 ret = start_wimboot_extraction(dentry_list, ctx);
3275                 if (ret)
3276                         goto out;
3277         }
3278
3279         ctx->windows_build_number = xml_get_windows_build_number(ctx->common.wim->xml_info,
3280                                                                  ctx->common.wim->current_image);
3281
3282         dentry_count = count_dentries(dentry_list);
3283
3284         ret = start_file_structure_phase(&ctx->common, dentry_count);
3285         if (ret)
3286                 goto out;
3287
3288         ret = create_directories(dentry_list, ctx);
3289         if (ret)
3290                 goto out;
3291
3292         ret = create_nondirectories(dentry_list, ctx);
3293         if (ret)
3294                 goto out;
3295
3296         ret = end_file_structure_phase(&ctx->common);
3297         if (ret)
3298                 goto out;
3299
3300         struct read_blob_callbacks cbs = {
3301                 .begin_blob     = win32_begin_extract_blob,
3302                 .continue_blob  = win32_extract_chunk,
3303                 .end_blob       = win32_end_extract_blob,
3304                 .ctx            = ctx,
3305         };
3306         ret = extract_blob_list(&ctx->common, &cbs);
3307         if (ret)
3308                 goto out;
3309
3310         ret = start_file_metadata_phase(&ctx->common, dentry_count);
3311         if (ret)
3312                 goto out;
3313
3314         ret = apply_metadata(dentry_list, ctx);
3315         if (ret)
3316                 goto out;
3317
3318         ret = end_file_metadata_phase(&ctx->common);
3319         if (ret)
3320                 goto out;
3321
3322         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
3323                 ret = end_wimboot_extraction(ctx);
3324                 if (ret)
3325                         goto out;
3326         }
3327
3328         do_warnings(ctx);
3329 out:
3330         close_target_directory(ctx);
3331         if (ctx->target_ntpath.Buffer)
3332                 HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
3333         FREE(ctx->pathbuf.Buffer);
3334         FREE(ctx->print_buffer);
3335         FREE(ctx->wimboot.wims);
3336         if (ctx->prepopulate_pats) {
3337                 FREE(ctx->prepopulate_pats->strings);
3338                 FREE(ctx->prepopulate_pats);
3339         }
3340         FREE(ctx->mem_prepopulate_pats);
3341         FREE(ctx->data_buffer);
3342         return ret;
3343 }
3344
3345 const struct apply_operations win32_apply_ops = {
3346         .name                   = "Windows",
3347         .get_supported_features = win32_get_supported_features,
3348         .extract                = win32_extract,
3349         .will_back_from_wim     = win32_will_back_from_wim,
3350         .context_size           = sizeof(struct win32_apply_ctx),
3351 };
3352
3353 #endif /* __WIN32__ */