]> wimlib.net Git - wimlib/blob - src/win32_apply.c
Add a clang-format file
[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 2013-2023 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 https://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] __attribute__((aligned(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] __attribute__((aligned(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), (u32)perms, (u32)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] __attribute__((aligned(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] __attribute__((aligned(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, but if
1857                  * you don't add the extra character, you get
1858                  * STATUS_INFO_LENGTH_MISMATCH when FileNameLength is 2.
1859                  */
1860
1861                 /*
1862                  * When fuzzing with wlfuzz.exe, creating a hard link sometimes
1863                  * fails with STATUS_ACCESS_DENIED.  However, it eventually
1864                  * succeeds when re-attempted...
1865                  */
1866                 int i = 0;
1867                 do {
1868                         status = NtSetInformationFile(h, &ctx->iosb, info,
1869                                                       bufsize,
1870                                                       FileLinkInformation);
1871                         if (NT_SUCCESS(status))
1872                                 return 0;
1873                 } while (++i < 32);
1874                 winnt_error(status, L"Failed to create link \"%ls\"",
1875                             current_path(ctx));
1876                 return WIMLIB_ERR_LINK;
1877         } else {
1878                 HANDLE h2;
1879                 int ret;
1880
1881                 ret = create_nondirectory_inode(&h2, dentry, ctx);
1882                 if (ret)
1883                         return ret;
1884
1885                 NtClose(h2);
1886                 return 0;
1887         }
1888 }
1889
1890 /* Given an inode (represented by the open handle @h) for which one link has
1891  * been created (named by @first_dentry), create the other links.
1892  *
1893  * Or, if the target volume does not support hard links, create separate files.
1894  *
1895  * Note: This uses ctx->pathbuf and does not reset it.
1896  */
1897 static int
1898 create_links(HANDLE h, const struct wim_dentry *first_dentry,
1899              struct win32_apply_ctx *ctx)
1900 {
1901         const struct wim_inode *inode = first_dentry->d_inode;
1902         const struct wim_dentry *dentry;
1903         int ret;
1904
1905         inode_for_each_extraction_alias(dentry, inode) {
1906                 if (dentry != first_dentry) {
1907                         ret = create_link(h, dentry, ctx);
1908                         if (ret)
1909                                 return ret;
1910                 }
1911         }
1912         return 0;
1913 }
1914
1915 /* Create a nondirectory file, including all links.  */
1916 static int
1917 create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx)
1918 {
1919         struct wim_dentry *first_dentry;
1920         HANDLE h;
1921         int ret;
1922
1923         first_dentry = first_extraction_alias(inode);
1924
1925         /* Create first link.  */
1926         ret = create_nondirectory_inode(&h, first_dentry, ctx);
1927         if (ret)
1928                 return ret;
1929
1930         /* Set short name.  */
1931         ret = set_short_name(h, first_dentry, ctx);
1932
1933         /* Create additional links, OR if hard links are not supported just
1934          * create more files.  */
1935         if (!ret)
1936                 ret = create_links(h, first_dentry, ctx);
1937
1938         /* "WIMBoot" extraction: set external backing by the WIM file if needed.  */
1939         if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
1940                 ret = set_backed_from_wim(h, inode, ctx);
1941
1942         NtClose(h);
1943         return ret;
1944 }
1945
1946 /* Create all the nondirectory files being extracted, including all aliases
1947  * (hard links).  */
1948 static int
1949 create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1950 {
1951         struct wim_dentry *dentry;
1952         struct wim_inode *inode;
1953         int ret;
1954
1955         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1956                 inode = dentry->d_inode;
1957                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1958                         continue;
1959                 /* Call create_nondirectory() only once per inode  */
1960                 if (dentry == inode_first_extraction_dentry(inode)) {
1961                         ret = create_nondirectory(inode, ctx);
1962                         ret = check_apply_error(dentry, ctx, ret);
1963                         if (ret)
1964                                 return ret;
1965                 }
1966                 ret = report_file_created(&ctx->common);
1967                 if (ret)
1968                         return ret;
1969         }
1970         return 0;
1971 }
1972
1973 static void
1974 close_handles(struct win32_apply_ctx *ctx)
1975 {
1976         for (unsigned i = 0; i < ctx->num_open_handles; i++)
1977                 NtClose(ctx->open_handles[i]);
1978 }
1979
1980 /* Prepare to read the next blob, which has size @blob_size, into an in-memory
1981  * buffer.  */
1982 static bool
1983 prepare_data_buffer(struct win32_apply_ctx *ctx, u64 blob_size)
1984 {
1985         if (blob_size > ctx->data_buffer_size) {
1986                 /* Larger buffer needed.  */
1987                 void *new_buffer;
1988                 if ((size_t)blob_size != blob_size)
1989                         return false;
1990                 new_buffer = REALLOC(ctx->data_buffer, blob_size);
1991                 if (!new_buffer)
1992                         return false;
1993                 ctx->data_buffer = new_buffer;
1994                 ctx->data_buffer_size = blob_size;
1995         }
1996         /* On the first call this changes data_buffer_ptr from NULL, which tells
1997          * extract_chunk() that the data buffer needs to be filled while reading
1998          * the stream data.  */
1999         ctx->data_buffer_ptr = ctx->data_buffer;
2000         return true;
2001 }
2002
2003 static int
2004 begin_extract_blob_instance(const struct blob_descriptor *blob,
2005                             struct wim_dentry *dentry,
2006                             const struct wim_inode_stream *strm,
2007                             struct win32_apply_ctx *ctx)
2008 {
2009         HANDLE h;
2010         NTSTATUS status;
2011
2012         if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) {
2013                 /* We can't write the reparse point stream directly; we must set
2014                  * it with FSCTL_SET_REPARSE_POINT, which requires that all the
2015                  * data be available.  So, stage the data in a buffer.  */
2016                 if (!prepare_data_buffer(ctx, blob->size))
2017                         return WIMLIB_ERR_NOMEM;
2018                 list_add_tail(&dentry->d_tmp_list, &ctx->reparse_dentries);
2019                 return 0;
2020         }
2021
2022         if (unlikely(strm->stream_type == STREAM_TYPE_EFSRPC_RAW_DATA)) {
2023                 /* We can't write encrypted files directly; we must use
2024                  * WriteEncryptedFileRaw(), which requires providing the data
2025                  * through a callback function.  This can't easily be combined
2026                  * with our own callback-based approach.
2027                  *
2028                  * The current workaround is to simply read the blob into memory
2029                  * and write the encrypted file from that.
2030                  *
2031                  * TODO: This isn't sufficient for extremely large encrypted
2032                  * files.  Perhaps we should create an extra thread to write
2033                  * such files...  */
2034                 if (!prepare_data_buffer(ctx, blob->size))
2035                         return WIMLIB_ERR_NOMEM;
2036                 list_add_tail(&dentry->d_tmp_list, &ctx->encrypted_dentries);
2037                 return 0;
2038         }
2039
2040         /* It's a data stream (may be unnamed or named).  */
2041         wimlib_assert(strm->stream_type == STREAM_TYPE_DATA);
2042
2043         if (ctx->num_open_handles == MAX_OPEN_FILES) {
2044                 /* XXX: Fix this.  But because of the checks in
2045                  * extract_blob_list(), this can now only happen on a filesystem
2046                  * that does not support hard links.  */
2047                 ERROR("Can't extract data: too many open files!");
2048                 return WIMLIB_ERR_UNSUPPORTED;
2049         }
2050
2051
2052         if (unlikely(stream_is_named(strm))) {
2053                 build_extraction_path_with_ads(dentry, ctx,
2054                                                strm->stream_name,
2055                                                utf16le_len_chars(strm->stream_name));
2056         } else {
2057                 build_extraction_path(dentry, ctx);
2058         }
2059
2060
2061         /* Open a new handle  */
2062         status = do_create_file(&h,
2063                                 FILE_WRITE_DATA | SYNCHRONIZE,
2064                                 NULL, 0, FILE_OPEN_IF,
2065                                 FILE_SEQUENTIAL_ONLY |
2066                                         FILE_SYNCHRONOUS_IO_NONALERT,
2067                                 ctx);
2068         if (!NT_SUCCESS(status)) {
2069                 winnt_error(status, L"Can't open \"%ls\" for writing",
2070                             current_path(ctx));
2071                 return WIMLIB_ERR_OPEN;
2072         }
2073
2074         ctx->is_sparse_stream[ctx->num_open_handles] = false;
2075         if (need_sparse_flag(dentry->d_inode, ctx)) {
2076                 /* If the stream is unnamed, then the sparse flag was already
2077                  * set when the file was created.  But if the stream is named,
2078                  * then we need to set the sparse flag here. */
2079                 if (unlikely(stream_is_named(strm))) {
2080                         int ret = set_sparse_flag(h, ctx);
2081                         if (ret) {
2082                                 NtClose(h);
2083                                 return ret;
2084                         }
2085                 }
2086                 ctx->is_sparse_stream[ctx->num_open_handles] = true;
2087                 ctx->any_sparse_streams = true;
2088         } else {
2089                 /* Allocate space for the data.  */
2090                 FILE_ALLOCATION_INFORMATION info =
2091                         { .AllocationSize = { .QuadPart = blob->size }};
2092                 NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info),
2093                                      FileAllocationInformation);
2094         }
2095         ctx->open_handles[ctx->num_open_handles++] = h;
2096         return 0;
2097 }
2098
2099 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
2100  * pointer to the suffix of the path that begins with the device directly, such
2101  * as e:\Windows\System32.  */
2102 static const wchar_t *
2103 skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
2104 {
2105         static const wchar_t * const dirs[] = {
2106                 L"\\??\\",
2107                 L"\\DosDevices\\",
2108                 L"\\Device\\",
2109         };
2110         const wchar_t * const end = path + path_nchars;
2111
2112         for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
2113                 size_t len = wcslen(dirs[i]);
2114                 if (len <= (end - path) && !wmemcmp(path, dirs[i], len)) {
2115                         path += len;
2116                         while (path != end && *path == L'\\')
2117                                 path++;
2118                         return path;
2119                 }
2120         }
2121         return path;
2122 }
2123
2124 /*
2125  * Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
2126  * pointer to the suffix of the path that is device-relative but possibly with
2127  * leading slashes, such as \Windows\System32.
2128  *
2129  * The path has an explicit length and is not necessarily null terminated.
2130  */
2131 static const wchar_t *
2132 get_device_relative_path(const wchar_t *path, size_t path_nchars)
2133 {
2134         const wchar_t * const orig_path = path;
2135         const wchar_t * const end = path + path_nchars;
2136
2137         path = skip_nt_toplevel_component(path, path_nchars);
2138         if (path == orig_path)
2139                 return orig_path;
2140
2141         while (path != end && *path != L'\\')
2142                 path++;
2143
2144         return path;
2145 }
2146
2147 /*
2148  * Given a reparse point buffer for an inode for which the absolute link target
2149  * was relativized when it was archived, de-relative the link target to be
2150  * consistent with the actual extraction location.
2151  */
2152 static void
2153 try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p,
2154           struct win32_apply_ctx *ctx)
2155 {
2156         struct link_reparse_point link;
2157         size_t orig_subst_name_nchars;
2158         const wchar_t *relpath;
2159         size_t relpath_nchars;
2160         size_t target_ntpath_nchars;
2161         size_t fixed_subst_name_nchars;
2162         const wchar_t *fixed_print_name;
2163         size_t fixed_print_name_nchars;
2164
2165         /* Do nothing if the reparse data is invalid.  */
2166         if (parse_link_reparse_point(rpbuf, *rpbuflen_p, &link))
2167                 return;
2168
2169         /* Do nothing if the reparse point is a relative symbolic link.  */
2170         if (link_is_relative_symlink(&link))
2171                 return;
2172
2173         /* Build the new substitute name from the NT namespace path to the
2174          * target directory, then a path separator, then the "device relative"
2175          * part of the old substitute name.  */
2176
2177         orig_subst_name_nchars = link.substitute_name_nbytes / sizeof(wchar_t);
2178
2179         relpath = get_device_relative_path(link.substitute_name,
2180                                            orig_subst_name_nchars);
2181         relpath_nchars = orig_subst_name_nchars -
2182                          (relpath - link.substitute_name);
2183
2184         target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
2185
2186         /* If the target directory is a filesystem root, such as \??\C:\, then
2187          * it already will have a trailing slash.  Don't include this slash if
2188          * we are already adding slashes via 'relpath'.  This prevents an extra
2189          * slash from being generated each time the link is extracted.  And
2190          * unlike on UNIX, the number of slashes in paths on Windows can be
2191          * significant; Windows won't understand the link target if it contains
2192          * too many slashes.  */
2193         if (target_ntpath_nchars > 0 && relpath_nchars > 0 &&
2194             ctx->target_ntpath.Buffer[target_ntpath_nchars - 1] == L'\\')
2195                 target_ntpath_nchars--;
2196
2197         /* Also remove extra slashes from the beginning of 'relpath'.  Normally
2198          * this isn't needed, but this is here to make the extra slash(es) added
2199          * by wimlib pre-v1.9.1 get removed automatically.  */
2200         while (relpath_nchars >= 2 &&
2201                relpath[0] == L'\\' && relpath[1] == L'\\') {
2202                 relpath++;
2203                 relpath_nchars--;
2204         }
2205
2206         fixed_subst_name_nchars = target_ntpath_nchars + relpath_nchars;
2207
2208         wchar_t fixed_subst_name[fixed_subst_name_nchars];
2209
2210         wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer, target_ntpath_nchars);
2211         wmemcpy(&fixed_subst_name[target_ntpath_nchars], relpath, relpath_nchars);
2212         /* Doesn't need to be null-terminated.  */
2213
2214         /* Print name should be Win32, but not all NT names can even be
2215          * translated to Win32 names.  But we can at least delete the top-level
2216          * directory, such as \??\, and this will have the expected result in
2217          * the usual case.  */
2218         fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
2219                                                       fixed_subst_name_nchars);
2220         fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
2221                                                              fixed_subst_name);
2222
2223         link.substitute_name = fixed_subst_name;
2224         link.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
2225         link.print_name = (wchar_t *)fixed_print_name;
2226         link.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
2227         make_link_reparse_point(&link, rpbuf, rpbuflen_p);
2228 }
2229
2230 /* Sets the reparse point on the specified file.  This handles "fixing" the
2231  * targets of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX
2232  * was specified.  */
2233 static int
2234 set_reparse_point(const struct wim_dentry *dentry,
2235                   const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
2236                   struct win32_apply_ctx *ctx)
2237 {
2238         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
2239             && !(dentry->d_inode->i_rp_flags & WIM_RP_FLAG_NOT_FIXED))
2240         {
2241                 memcpy(&ctx->rpfixbuf, rpbuf, rpbuflen);
2242                 try_rpfix(&ctx->rpfixbuf, &rpbuflen, ctx);
2243                 rpbuf = &ctx->rpfixbuf;
2244         }
2245         return do_set_reparse_point(dentry, rpbuf, rpbuflen, ctx);
2246
2247 }
2248
2249 /* Import the next block of raw encrypted data  */
2250 static DWORD WINAPI
2251 import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
2252 {
2253         struct win32_apply_ctx *ctx = pvCallbackContext;
2254         ULONG copy_len;
2255
2256         copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
2257         memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
2258         ctx->encrypted_offset += copy_len;
2259         *Length = copy_len;
2260         return ERROR_SUCCESS;
2261 }
2262
2263 /*
2264  * Write the raw encrypted data to the already-created file (or directory)
2265  * corresponding to @dentry.
2266  *
2267  * The raw encrypted data is provided in ctx->data_buffer, and its size is
2268  * ctx->encrypted_size.
2269  *
2270  * This function may close the target directory, in which case the caller needs
2271  * to re-open it if needed.
2272  */
2273 static int
2274 extract_encrypted_file(const struct wim_dentry *dentry,
2275                        struct win32_apply_ctx *ctx)
2276 {
2277         void *rawctx;
2278         DWORD err;
2279         ULONG flags;
2280         bool retried;
2281
2282         /* Temporarily build a Win32 path for OpenEncryptedFileRaw()  */
2283         build_win32_extraction_path(dentry, ctx);
2284
2285         flags = CREATE_FOR_IMPORT | OVERWRITE_HIDDEN;
2286         if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
2287                 flags |= CREATE_FOR_DIR;
2288
2289         retried = false;
2290 retry:
2291         err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, flags, &rawctx);
2292         if (err == ERROR_SHARING_VIOLATION && !retried) {
2293                 /* This can be caused by the handle we have open to the target
2294                  * directory.  Try closing it temporarily.  */
2295                 close_target_directory(ctx);
2296                 retried = true;
2297                 goto retry;
2298         }
2299
2300         /* Restore the NT namespace path  */
2301         build_extraction_path(dentry, ctx);
2302
2303         if (err != ERROR_SUCCESS) {
2304                 win32_error(err, L"Can't open \"%ls\" for encrypted import",
2305                             current_path(ctx));
2306                 return WIMLIB_ERR_OPEN;
2307         }
2308
2309         ctx->encrypted_offset = 0;
2310
2311         err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx);
2312
2313         CloseEncryptedFileRaw(rawctx);
2314
2315         if (err != ERROR_SUCCESS) {
2316                 win32_error(err, L"Can't import encrypted file \"%ls\"",
2317                             current_path(ctx));
2318                 return WIMLIB_ERR_WRITE;
2319         }
2320
2321         return 0;
2322 }
2323
2324 /* Called when starting to read a blob for extraction */
2325 static int
2326 win32_begin_extract_blob(struct blob_descriptor *blob, void *_ctx)
2327 {
2328         struct win32_apply_ctx *ctx = _ctx;
2329         const struct blob_extraction_target *targets = blob_extraction_targets(blob);
2330         int ret;
2331
2332         ctx->num_open_handles = 0;
2333         ctx->data_buffer_ptr = NULL;
2334         ctx->any_sparse_streams = false;
2335         INIT_LIST_HEAD(&ctx->reparse_dentries);
2336         INIT_LIST_HEAD(&ctx->encrypted_dentries);
2337
2338         for (u32 i = 0; i < blob->out_refcnt; i++) {
2339                 const struct wim_inode *inode = targets[i].inode;
2340                 const struct wim_inode_stream *strm = targets[i].stream;
2341                 struct wim_dentry *dentry;
2342
2343                 /* A copy of the blob needs to be extracted to @inode.  */
2344
2345                 if (ctx->common.supported_features.hard_links) {
2346                         dentry = inode_first_extraction_dentry(inode);
2347                         ret = begin_extract_blob_instance(blob, dentry, strm, ctx);
2348                         ret = check_apply_error(dentry, ctx, ret);
2349                         if (ret)
2350                                 goto fail;
2351                 } else {
2352                         /* Hard links not supported.  Extract the blob
2353                          * separately to each alias of the inode.  */
2354                         inode_for_each_extraction_alias(dentry, inode) {
2355                                 ret = begin_extract_blob_instance(blob, dentry, strm, ctx);
2356                                 ret = check_apply_error(dentry, ctx, ret);
2357                                 if (ret)
2358                                         goto fail;
2359                         }
2360                 }
2361         }
2362
2363         return 0;
2364
2365 fail:
2366         close_handles(ctx);
2367         return ret;
2368 }
2369
2370 static int
2371 pwrite_to_handle(HANDLE h, const void *data, size_t size, u64 offset)
2372 {
2373         const void * const end = data + size;
2374         const void *p;
2375         IO_STATUS_BLOCK iosb;
2376         NTSTATUS status;
2377
2378         for (p = data; p != end; p += iosb.Information,
2379                                  offset += iosb.Information)
2380         {
2381                 LARGE_INTEGER offs = { .QuadPart = offset };
2382
2383                 status = NtWriteFile(h, NULL, NULL, NULL, &iosb,
2384                                      (void *)p, min(INT32_MAX, end - p),
2385                                      &offs, NULL);
2386                 if (!NT_SUCCESS(status)) {
2387                         winnt_error(status,
2388                                     L"Error writing data to target volume");
2389                         return WIMLIB_ERR_WRITE;
2390                 }
2391         }
2392         return 0;
2393 }
2394
2395 /* Called when the next chunk of a blob has been read for extraction */
2396 static int
2397 win32_extract_chunk(const struct blob_descriptor *blob, u64 offset,
2398                     const void *chunk, size_t size, void *_ctx)
2399 {
2400         struct win32_apply_ctx *ctx = _ctx;
2401         const void * const end = chunk + size;
2402         const void *p;
2403         bool zeroes;
2404         size_t len;
2405         unsigned i;
2406         int ret;
2407
2408         /*
2409          * For sparse streams, only write nonzero regions.  This lets the
2410          * filesystem use holes to represent zero regions.
2411          */
2412         for (p = chunk; p != end; p += len, offset += len) {
2413                 zeroes = maybe_detect_sparse_region(p, end - p, &len,
2414                                                     ctx->any_sparse_streams);
2415                 for (i = 0; i < ctx->num_open_handles; i++) {
2416                         if (!zeroes || !ctx->is_sparse_stream[i]) {
2417                                 ret = pwrite_to_handle(ctx->open_handles[i],
2418                                                        p, len, offset);
2419                                 if (ret)
2420                                         return ret;
2421                         }
2422                 }
2423         }
2424
2425         /* Copy the data chunk into the buffer (if needed)  */
2426         if (ctx->data_buffer_ptr)
2427                 ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
2428                                                chunk, size);
2429         return 0;
2430 }
2431
2432 static int
2433 get_system_compression_format(int extract_flags)
2434 {
2435         if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K)
2436                 return FILE_PROVIDER_COMPRESSION_XPRESS4K;
2437
2438         if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K)
2439                 return FILE_PROVIDER_COMPRESSION_XPRESS8K;
2440
2441         if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K)
2442                 return FILE_PROVIDER_COMPRESSION_XPRESS16K;
2443
2444         return FILE_PROVIDER_COMPRESSION_LZX;
2445 }
2446
2447
2448 static const wchar_t *
2449 get_system_compression_format_string(int format)
2450 {
2451         switch (format) {
2452         case FILE_PROVIDER_COMPRESSION_XPRESS4K:
2453                 return L"XPRESS4K";
2454         case FILE_PROVIDER_COMPRESSION_XPRESS8K:
2455                 return L"XPRESS8K";
2456         case FILE_PROVIDER_COMPRESSION_XPRESS16K:
2457                 return L"XPRESS16K";
2458         default:
2459                 return L"LZX";
2460         }
2461 }
2462
2463 static NTSTATUS
2464 set_system_compression(HANDLE h, int format)
2465 {
2466         NTSTATUS status;
2467         struct {
2468                 WOF_EXTERNAL_INFO wof_info;
2469                 FILE_PROVIDER_EXTERNAL_INFO_V1 file_info;
2470         } in = {
2471                 .wof_info = {
2472                         .Version = WOF_CURRENT_VERSION,
2473                         .Provider = WOF_PROVIDER_FILE,
2474                 },
2475                 .file_info = {
2476                         .Version = FILE_PROVIDER_CURRENT_VERSION,
2477                         .Algorithm = format,
2478                 },
2479         };
2480
2481         /* We intentionally use NtFsControlFile() rather than DeviceIoControl()
2482          * here because the "compressing this object would not save space"
2483          * status code does not map to a valid Win32 error code on older
2484          * versions of Windows (before Windows 10?).  This can be a problem if
2485          * the WOFADK driver is being used rather than the regular WOF, since
2486          * WOFADK can be used on older versions of Windows.  */
2487         status = winnt_fsctl(h, FSCTL_SET_EXTERNAL_BACKING,
2488                              &in, sizeof(in), NULL, 0, NULL);
2489
2490         if (status == 0xC000046F) /* "Compressing this object would not save space."  */
2491                 return STATUS_SUCCESS;
2492
2493         return status;
2494 }
2495
2496 /* Hard-coded list of files which the Windows bootloader may need to access
2497  * before the WOF driver has been loaded.  */
2498 static const wchar_t * const bootloader_pattern_strings[] = {
2499         L"*winload.*",
2500         L"*winresume.*",
2501         L"\\Windows\\AppPatch\\drvmain.sdb",
2502         L"\\Windows\\Boot\\DVD\\*",
2503         L"\\Windows\\Boot\\EFI\\*",
2504         L"\\Windows\\bootstat.dat",
2505         L"\\Windows\\Fonts\\vgaoem.fon",
2506         L"\\Windows\\Fonts\\vgasys.fon",
2507         L"\\Windows\\INF\\errata.inf",
2508         L"\\Windows\\System32\\config\\*",
2509         L"\\Windows\\System32\\ntkrnlpa.exe",
2510         L"\\Windows\\System32\\ntoskrnl.exe",
2511         L"\\Windows\\System32\\bootvid.dll",
2512         L"\\Windows\\System32\\ci.dll",
2513         L"\\Windows\\System32\\hal*.dll",
2514         L"\\Windows\\System32\\mcupdate_AuthenticAMD.dll",
2515         L"\\Windows\\System32\\mcupdate_GenuineIntel.dll",
2516         L"\\Windows\\System32\\pshed.dll",
2517         L"\\Windows\\System32\\apisetschema.dll",
2518         L"\\Windows\\System32\\api-ms-win*.dll",
2519         L"\\Windows\\System32\\ext-ms-win*.dll",
2520         L"\\Windows\\System32\\KernelBase.dll",
2521         L"\\Windows\\System32\\drivers\\*.sys",
2522         L"\\Windows\\System32\\*.nls",
2523         L"\\Windows\\System32\\kbd*.dll",
2524         L"\\Windows\\System32\\kd*.dll",
2525         L"\\Windows\\System32\\clfs.sys",
2526         L"\\Windows\\System32\\CodeIntegrity\\driver.stl",
2527 };
2528
2529 static const struct string_list bootloader_patterns = {
2530         .strings = (wchar_t **)bootloader_pattern_strings,
2531         .num_strings = ARRAY_LEN(bootloader_pattern_strings),
2532 };
2533
2534 /* Returns true if the specified system compression format is supported by the
2535  * bootloader of the image being applied.  */
2536 static bool
2537 bootloader_supports_compression_format(struct win32_apply_ctx *ctx, int format)
2538 {
2539         /* Windows 10 and later support XPRESS4K */
2540         if (format == FILE_PROVIDER_COMPRESSION_XPRESS4K)
2541                 return ctx->windows_build_number >= 10240;
2542
2543         /*
2544          * Windows 10 version 1903 and later support the other formats;
2545          * see https://wimlib.net/forums/viewtopic.php?f=1&t=444
2546          */
2547         return ctx->windows_build_number >= 18362;
2548 }
2549
2550 static NTSTATUS
2551 set_system_compression_on_inode(struct wim_inode *inode, int format,
2552                                 struct win32_apply_ctx *ctx)
2553 {
2554         bool retried = false;
2555         NTSTATUS status;
2556         HANDLE h;
2557
2558         /* If it may be needed for compatibility with the Windows bootloader,
2559          * force this file to XPRESS4K or uncompressed format.  */
2560         if (!bootloader_supports_compression_format(ctx, format)) {
2561                 /* We need to check the patterns against every name of the
2562                  * inode, in case any of them match.  */
2563                 struct wim_dentry *dentry;
2564                 inode_for_each_extraction_alias(dentry, inode) {
2565                         bool incompatible;
2566                         bool warned;
2567
2568                         if (calculate_dentry_full_path(dentry)) {
2569                                 ERROR("Unable to compute file path!");
2570                                 return STATUS_NO_MEMORY;
2571                         }
2572
2573                         incompatible = match_pattern_list(dentry->d_full_path,
2574                                                           &bootloader_patterns,
2575                                                           MATCH_RECURSIVELY);
2576                         FREE(dentry->d_full_path);
2577                         dentry->d_full_path = NULL;
2578
2579                         if (!incompatible)
2580                                 continue;
2581
2582                         warned = (ctx->num_system_compression_exclusions++ > 0);
2583
2584                         if (bootloader_supports_compression_format(ctx,
2585                                    FILE_PROVIDER_COMPRESSION_XPRESS4K))
2586                         {
2587                                 /* Force to XPRESS4K  */
2588                                 if (!warned) {
2589                                         WARNING("For compatibility with the "
2590                                                 "Windows bootloader, some "
2591                                                 "files are being\n"
2592                                                 "          compacted "
2593                                                 "using the XPRESS4K format "
2594                                                 "instead of the %"TS" format\n"
2595                                                 "          you requested.",
2596                                                 get_system_compression_format_string(format));
2597                                 }
2598                                 format = FILE_PROVIDER_COMPRESSION_XPRESS4K;
2599                                 break;
2600                         } else {
2601                                 /* Force to uncompressed  */
2602                                 if (!warned) {
2603                                         WARNING("For compatibility with the "
2604                                                 "Windows bootloader, some "
2605                                                 "files will not\n"
2606                                                 "          be compressed with"
2607                                                 " system compression "
2608                                                 "(\"compacted\").");
2609                                 }
2610                                 return STATUS_SUCCESS;
2611                         }
2612
2613                 }
2614         }
2615
2616         /* Open the extracted file.  */
2617         status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL,
2618                              0, FILE_OPEN, 0,
2619                              inode_first_extraction_dentry(inode), ctx);
2620
2621         if (!NT_SUCCESS(status))
2622                 return status;
2623 retry:
2624         /* Compress the file.  If the attempt fails with "invalid device
2625          * request", then attach wof.sys (or wofadk.sys) and retry.  */
2626         status = set_system_compression(h, format);
2627         if (unlikely(status == STATUS_INVALID_DEVICE_REQUEST && !retried)) {
2628                 wchar_t drive_path[7];
2629                 if (!win32_get_drive_path(ctx->common.target, drive_path) &&
2630                     win32_try_to_attach_wof(drive_path + 4)) {
2631                         retried = true;
2632                         goto retry;
2633                 }
2634         }
2635
2636         NtClose(h);
2637         return status;
2638 }
2639
2640 /*
2641  * This function is called when doing a "compact-mode" extraction and we just
2642  * finished extracting a blob to one or more locations.  For each location that
2643  * was the unnamed data stream of a file, this function compresses the
2644  * corresponding file using System Compression, if allowed.
2645  *
2646  * Note: we're doing the compression immediately after extracting the data
2647  * rather than during a separate compression pass.  This way should be faster
2648  * since the operating system should still have the file's data cached.
2649  *
2650  * Note: we're having the operating system do the compression, which is not
2651  * ideal because wimlib could create the compressed data faster and more
2652  * efficiently (the compressed data format is identical to a WIM resource).  But
2653  * we seemingly don't have a choice because WOF prevents applications from
2654  * creating its reparse points.
2655  */
2656 static void
2657 handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *ctx)
2658 {
2659         const struct blob_extraction_target *targets = blob_extraction_targets(blob);
2660
2661         const int format = get_system_compression_format(ctx->common.extract_flags);
2662
2663         for (u32 i = 0; i < blob->out_refcnt; i++) {
2664                 struct wim_inode *inode = targets[i].inode;
2665                 struct wim_inode_stream *strm = targets[i].stream;
2666                 NTSTATUS status;
2667
2668                 if (!stream_is_unnamed_data_stream(strm))
2669                         continue;
2670
2671                 if (will_externally_back_inode(inode, ctx, NULL, false) != 0)
2672                         continue;
2673
2674                 status = set_system_compression_on_inode(inode, format, ctx);
2675                 if (likely(NT_SUCCESS(status)))
2676                         continue;
2677
2678                 if (status == STATUS_INVALID_DEVICE_REQUEST) {
2679                         WARNING(
2680           "The request to compress the extracted files using System Compression\n"
2681 "          will not be honored because the operating system or target volume\n"
2682 "          does not support it.  System Compression is only supported on\n"
2683 "          Windows 10 and later, and only on NTFS volumes.");
2684                         ctx->common.extract_flags &= ~COMPACT_FLAGS;
2685                         return;
2686                 }
2687
2688                 ctx->num_system_compression_failures++;
2689                 if (ctx->num_system_compression_failures < 10) {
2690                         winnt_warning(status, L"\"%ls\": Failed to compress "
2691                                       "extracted file using System Compression",
2692                                       current_path(ctx));
2693                 } else if (ctx->num_system_compression_failures == 10) {
2694                         WARNING("Suppressing further warnings about "
2695                                 "System Compression failures.");
2696                 }
2697         }
2698 }
2699
2700 /* Called when a blob has been fully read for extraction */
2701 static int
2702 win32_end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
2703 {
2704         struct win32_apply_ctx *ctx = _ctx;
2705         int ret;
2706         const struct wim_dentry *dentry;
2707
2708         /* Extend sparse streams to their final size. */
2709         if (ctx->any_sparse_streams && !status) {
2710                 for (unsigned i = 0; i < ctx->num_open_handles; i++) {
2711                         FILE_END_OF_FILE_INFORMATION info =
2712                                 { .EndOfFile = { .QuadPart = blob->size } };
2713                         NTSTATUS ntstatus;
2714
2715                         if (!ctx->is_sparse_stream[i])
2716                                 continue;
2717
2718                         ntstatus = NtSetInformationFile(ctx->open_handles[i],
2719                                                         &ctx->iosb,
2720                                                         &info, sizeof(info),
2721                                                         FileEndOfFileInformation);
2722                         if (!NT_SUCCESS(ntstatus)) {
2723                                 winnt_error(ntstatus, L"Error writing data to "
2724                                             "target volume (while extending)");
2725                                 status = WIMLIB_ERR_WRITE;
2726                                 break;
2727                         }
2728                 }
2729         }
2730
2731         close_handles(ctx);
2732
2733         if (status)
2734                 return status;
2735
2736         if (unlikely(ctx->common.extract_flags & COMPACT_FLAGS))
2737                 handle_system_compression(blob, ctx);
2738
2739         if (likely(!ctx->data_buffer_ptr))
2740                 return 0;
2741
2742         if (!list_empty(&ctx->reparse_dentries)) {
2743                 if (blob->size > REPARSE_DATA_MAX_SIZE) {
2744                         dentry = list_first_entry(&ctx->reparse_dentries,
2745                                                   struct wim_dentry, d_tmp_list);
2746                         build_extraction_path(dentry, ctx);
2747                         ERROR("Reparse data of \"%ls\" has size "
2748                               "%"PRIu64" bytes (exceeds %u bytes)",
2749                               current_path(ctx), blob->size,
2750                               REPARSE_DATA_MAX_SIZE);
2751                         ret = WIMLIB_ERR_INVALID_REPARSE_DATA;
2752                         return check_apply_error(dentry, ctx, ret);
2753                 }
2754                 /* Reparse data  */
2755                 memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, blob->size);
2756
2757                 list_for_each_entry(dentry, &ctx->reparse_dentries, d_tmp_list) {
2758
2759                         /* Reparse point header  */
2760                         complete_reparse_point(&ctx->rpbuf, dentry->d_inode,
2761                                                blob->size);
2762
2763                         ret = set_reparse_point(dentry, &ctx->rpbuf,
2764                                                 REPARSE_DATA_OFFSET + blob->size,
2765                                                 ctx);
2766                         ret = check_apply_error(dentry, ctx, ret);
2767                         if (ret)
2768                                 return ret;
2769                 }
2770         }
2771
2772         if (!list_empty(&ctx->encrypted_dentries)) {
2773                 ctx->encrypted_size = blob->size;
2774                 list_for_each_entry(dentry, &ctx->encrypted_dentries, d_tmp_list) {
2775                         ret = extract_encrypted_file(dentry, ctx);
2776                         ret = check_apply_error(dentry, ctx, ret);
2777                         if (ret)
2778                                 return ret;
2779                         /* Re-open the target directory if needed.  */
2780                         ret = open_target_directory(ctx);
2781                         if (ret)
2782                                 return ret;
2783                 }
2784         }
2785
2786         return 0;
2787 }
2788
2789 /* Attributes that can't be set directly  */
2790 #define SPECIAL_ATTRIBUTES                      \
2791         (FILE_ATTRIBUTE_REPARSE_POINT   |       \
2792          FILE_ATTRIBUTE_DIRECTORY       |       \
2793          FILE_ATTRIBUTE_ENCRYPTED       |       \
2794          FILE_ATTRIBUTE_SPARSE_FILE     |       \
2795          FILE_ATTRIBUTE_COMPRESSED)
2796
2797 static void
2798 set_object_id(HANDLE h, const struct wim_inode *inode,
2799               struct win32_apply_ctx *ctx)
2800 {
2801         const void *object_id;
2802         u32 len;
2803         NTSTATUS status;
2804
2805         if (!ctx->common.supported_features.object_ids)
2806                 return;
2807
2808         object_id = inode_get_object_id(inode, &len);
2809         if (likely(object_id == NULL))  /* No object ID?  */
2810                 return;
2811
2812         status = winnt_fsctl(h, FSCTL_SET_OBJECT_ID,
2813                              object_id, len, NULL, 0, NULL);
2814         if (NT_SUCCESS(status))
2815                 return;
2816
2817         /* Object IDs must be unique within the filesystem.  A duplicate might
2818          * occur if an image containing object IDs is applied twice to the same
2819          * filesystem.  Arguably, the user should be warned in this case; but
2820          * the reality seems to be that nothing important cares about object IDs
2821          * except the Distributed Link Tracking Service... so for now these
2822          * failures are just ignored.  */
2823         if (status == STATUS_DUPLICATE_NAME ||
2824             status == STATUS_OBJECT_NAME_COLLISION)
2825                 return;
2826
2827         ctx->num_object_id_failures++;
2828         if (ctx->num_object_id_failures < 10) {
2829                 winnt_warning(status, L"Can't set object ID on \"%ls\"",
2830                               current_path(ctx));
2831         } else if (ctx->num_object_id_failures == 10) {
2832                 WARNING("Suppressing further warnings about failure to set "
2833                         "object IDs.");
2834         }
2835 }
2836
2837 static int
2838 set_xattrs(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx)
2839 {
2840         const void *entries, *entries_end;
2841         u32 len;
2842         const struct wim_xattr_entry *entry;
2843         size_t bufsize = 0;
2844         u8 _buf[1024] __attribute__((aligned(4)));
2845         u8 *buf = _buf;
2846         FILE_FULL_EA_INFORMATION *ea, *ea_prev;
2847         NTSTATUS status;
2848         int ret;
2849
2850         if (!ctx->common.supported_features.xattrs)
2851                 return 0;
2852
2853         entries = inode_get_xattrs(inode, &len);
2854         if (likely(entries == NULL || len == 0))  /* No extended attributes? */
2855                 return 0;
2856         entries_end = entries + len;
2857
2858         entry = entries;
2859         for (entry = entries; (void *)entry < entries_end;
2860              entry = xattr_entry_next(entry)) {
2861                 if (!valid_xattr_entry(entry, entries_end - (void *)entry)) {
2862                         ERROR("\"%"TS"\": extended attribute is corrupt or unsupported",
2863                               inode_any_full_path(inode));
2864                         return WIMLIB_ERR_INVALID_XATTR;
2865                 }
2866
2867                 bufsize += ALIGN(offsetof(FILE_FULL_EA_INFORMATION, EaName) +
2868                                  entry->name_len + 1 +
2869                                  le16_to_cpu(entry->value_len), 4);
2870         }
2871
2872         if (unlikely(bufsize != (u32)bufsize)) {
2873                 ERROR("\"%"TS"\": too many extended attributes to extract!",
2874                       inode_any_full_path(inode));
2875                 return WIMLIB_ERR_INVALID_XATTR;
2876         }
2877
2878         if (unlikely(bufsize > sizeof(_buf))) {
2879                 buf = MALLOC(bufsize);
2880                 if (!buf)
2881                         return WIMLIB_ERR_NOMEM;
2882         }
2883
2884         ea_prev = NULL;
2885         ea = (FILE_FULL_EA_INFORMATION *)buf;
2886         for (entry = entries; (void *)entry < entries_end;
2887              entry = xattr_entry_next(entry)) {
2888                 u8 *p;
2889
2890                 if (ea_prev)
2891                         ea_prev->NextEntryOffset = (u8 *)ea - (u8 *)ea_prev;
2892                 ea->Flags = entry->flags;
2893                 ea->EaNameLength = entry->name_len;
2894                 ea->EaValueLength = le16_to_cpu(entry->value_len);
2895                 p = mempcpy(ea->EaName, entry->name,
2896                             ea->EaNameLength + 1 + ea->EaValueLength);
2897                 while ((uintptr_t)p & 3)
2898                         *p++ = 0;
2899                 ea_prev = ea;
2900                 ea = (FILE_FULL_EA_INFORMATION *)p;
2901         }
2902         ea_prev->NextEntryOffset = 0;
2903         wimlib_assert((u8 *)ea - buf == bufsize);
2904
2905         status = NtSetEaFile(h, &ctx->iosb, buf, bufsize);
2906         if (unlikely(!NT_SUCCESS(status))) {
2907                 if (status == STATUS_EAS_NOT_SUPPORTED) {
2908                         /* This happens with Samba. */
2909                         WARNING("Filesystem advertised extended attribute (EA) support, but it doesn't\n"
2910                                 "          work.  EAs will not be extracted.");
2911                         ctx->common.supported_features.xattrs = 0;
2912                 } else if (status == STATUS_INVALID_EA_NAME) {
2913                         ctx->num_xattr_failures++;
2914                         if (ctx->num_xattr_failures < 5) {
2915                                 winnt_warning(status,
2916                                               L"Can't set extended attributes on \"%ls\"",
2917                                               current_path(ctx));
2918                         } else if (ctx->num_xattr_failures == 5) {
2919                                 WARNING("Suppressing further warnings about "
2920                                         "failure to set extended attributes.");
2921                         }
2922                 } else {
2923                         winnt_error(status, L"Can't set extended attributes on \"%ls\"",
2924                                     current_path(ctx));
2925                         ret = WIMLIB_ERR_SET_XATTR;
2926                         goto out;
2927                 }
2928         }
2929         ret = 0;
2930 out:
2931         if (buf != _buf)
2932                 FREE(buf);
2933         return ret;
2934 }
2935
2936 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
2937  * handle @h.  */
2938 static NTSTATUS
2939 set_security_descriptor(HANDLE h, const void *_desc,
2940                         size_t desc_size, struct win32_apply_ctx *ctx)
2941 {
2942         SECURITY_INFORMATION info;
2943         NTSTATUS status;
2944         SECURITY_DESCRIPTOR_RELATIVE *desc;
2945
2946         /*
2947          * Ideally, we would just pass in the security descriptor buffer as-is.
2948          * But it turns out that Windows can mess up the security descriptor
2949          * even when using the low-level NtSetSecurityObject() function:
2950          *
2951          * - Windows will clear SE_DACL_AUTO_INHERITED if it is set in the
2952          *   passed buffer.  To actually get Windows to set
2953          *   SE_DACL_AUTO_INHERITED, the application must set the non-persistent
2954          *   flag SE_DACL_AUTO_INHERIT_REQ.  As usual, Microsoft didn't bother
2955          *   to properly document either of these flags.  It's unclear how
2956          *   important SE_DACL_AUTO_INHERITED actually is, but to be safe we use
2957          *   the SE_DACL_AUTO_INHERIT_REQ workaround to set it if needed.
2958          *
2959          * - The above also applies to the equivalent SACL flags,
2960          *   SE_SACL_AUTO_INHERITED and SE_SACL_AUTO_INHERIT_REQ.
2961          *
2962          * - If the application says that it's setting
2963          *   DACL_SECURITY_INFORMATION, then Windows sets SE_DACL_PRESENT in the
2964          *   resulting security descriptor, even if the security descriptor the
2965          *   application provided did not have a DACL.  This seems to be
2966          *   unavoidable, since omitting DACL_SECURITY_INFORMATION would cause a
2967          *   default DACL to remain.  Fortunately, this behavior seems harmless,
2968          *   since the resulting DACL will still be "null" --- but it will be
2969          *   "the other representation of null".
2970          *
2971          * - The above also applies to SACL_SECURITY_INFORMATION and
2972          *   SE_SACL_PRESENT.  Again, it's seemingly unavoidable but "harmless"
2973          *   that Windows changes the representation of a "null SACL".
2974          */
2975         if (likely(desc_size <= STACK_MAX)) {
2976                 desc = alloca(desc_size);
2977         } else {
2978                 desc = MALLOC(desc_size);
2979                 if (!desc)
2980                         return STATUS_NO_MEMORY;
2981         }
2982
2983         memcpy(desc, _desc, desc_size);
2984
2985         if (likely(desc_size >= 4)) {
2986
2987                 if (desc->Control & SE_DACL_AUTO_INHERITED)
2988                         desc->Control |= SE_DACL_AUTO_INHERIT_REQ;
2989
2990                 if (desc->Control & SE_SACL_AUTO_INHERITED)
2991                         desc->Control |= SE_SACL_AUTO_INHERIT_REQ;
2992         }
2993
2994         /*
2995          * More API insanity.  We want to set the entire security descriptor
2996          * as-is.  But all available APIs require specifying the specific parts
2997          * of the security descriptor being set.  Especially annoying is that
2998          * mandatory integrity labels are part of the SACL, but they aren't set
2999          * with SACL_SECURITY_INFORMATION.  Instead, applications must also
3000          * specify LABEL_SECURITY_INFORMATION (Windows Vista, Windows 7) or
3001          * BACKUP_SECURITY_INFORMATION (Windows 8).  But at least older versions
3002          * of Windows don't error out if you provide these newer flags...
3003          *
3004          * Also, if the process isn't running as Administrator, then it probably
3005          * doesn't have SE_RESTORE_PRIVILEGE.  In this case, it will always get
3006          * the STATUS_PRIVILEGE_NOT_HELD error by trying to set the SACL, even
3007          * if the security descriptor it provided did not have a SACL.  By
3008          * default, in this case we try to recover and set as much of the
3009          * security descriptor as possible --- potentially excluding the DACL, and
3010          * even the owner, as well as the SACL.
3011          */
3012
3013         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
3014                DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION |
3015                LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION;
3016
3017
3018         /*
3019          * It's also worth noting that SetFileSecurity() is unusable because it
3020          * doesn't request "backup semantics" when it opens the file internally.
3021          * NtSetSecurityObject() seems to be the best function to use in backup
3022          * applications.  (SetSecurityInfo() should also work, but it's harder
3023          * to use and must call NtSetSecurityObject() internally anyway.
3024          * BackupWrite() is theoretically usable as well, but it's inflexible
3025          * and poorly documented.)
3026          */
3027
3028 retry:
3029         status = NtSetSecurityObject(h, info, desc);
3030         if (NT_SUCCESS(status))
3031                 goto out_maybe_free_desc;
3032
3033         /* Failed to set the requested parts of the security descriptor.  If the
3034          * error was permissions-related, try to set fewer parts of the security
3035          * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
3036         if ((status == STATUS_PRIVILEGE_NOT_HELD ||
3037              status == STATUS_ACCESS_DENIED) &&
3038             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
3039         {
3040                 if (info & SACL_SECURITY_INFORMATION) {
3041                         info &= ~(SACL_SECURITY_INFORMATION |
3042                                   LABEL_SECURITY_INFORMATION |
3043                                   BACKUP_SECURITY_INFORMATION);
3044                         ctx->partial_security_descriptors++;
3045                         goto retry;
3046                 }
3047                 if (info & DACL_SECURITY_INFORMATION) {
3048                         info &= ~DACL_SECURITY_INFORMATION;
3049                         goto retry;
3050                 }
3051                 if (info & OWNER_SECURITY_INFORMATION) {
3052                         info &= ~OWNER_SECURITY_INFORMATION;
3053                         goto retry;
3054                 }
3055                 /* Nothing left except GROUP, and if we removed it we
3056                  * wouldn't have anything at all.  */
3057         }
3058
3059         /* No part of the security descriptor could be set, or
3060          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security
3061          * descriptor could not be set.  */
3062         if (!(info & SACL_SECURITY_INFORMATION))
3063                 ctx->partial_security_descriptors--;
3064         ctx->no_security_descriptors++;
3065
3066 out_maybe_free_desc:
3067         if (unlikely(desc_size > STACK_MAX))
3068                 FREE(desc);
3069         return status;
3070 }
3071
3072 /* Set metadata on the open file @h from the WIM inode @inode.  */
3073 static int
3074 do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
3075                           struct win32_apply_ctx *ctx)
3076 {
3077         FILE_BASIC_INFORMATION info;
3078         NTSTATUS status;
3079         int ret;
3080
3081         /* Set the file's object ID if present and object IDs are supported by
3082          * the filesystem.  */
3083         set_object_id(h, inode, ctx);
3084
3085         /* Set the file's extended attributes (EAs) if present and EAs are
3086          * supported by the filesystem.  */
3087         ret = set_xattrs(h, inode, ctx);
3088         if (ret)
3089                 return ret;
3090
3091         /* Set the file's security descriptor if present and we're not in
3092          * NO_ACLS mode  */
3093         if (inode_has_security_descriptor(inode) &&
3094             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
3095         {
3096                 const struct wim_security_data *sd;
3097                 const void *desc;
3098                 size_t desc_size;
3099
3100                 sd = wim_get_current_security_data(ctx->common.wim);
3101                 desc = sd->descriptors[inode->i_security_id];
3102                 desc_size = sd->sizes[inode->i_security_id];
3103
3104                 status = set_security_descriptor(h, desc, desc_size, ctx);
3105                 if (!NT_SUCCESS(status) &&
3106                     (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
3107                 {
3108                         winnt_error(status,
3109                                     L"Can't set security descriptor on \"%ls\"",
3110                                     current_path(ctx));
3111                         return WIMLIB_ERR_SET_SECURITY;
3112                 }
3113         }
3114
3115         /* Set attributes and timestamps  */
3116         info.CreationTime.QuadPart = inode->i_creation_time;
3117         info.LastAccessTime.QuadPart = inode->i_last_access_time;
3118         info.LastWriteTime.QuadPart = inode->i_last_write_time;
3119         info.ChangeTime.QuadPart = 0;
3120         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) {
3121                 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
3122         } else {
3123                 info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES;
3124                 if (info.FileAttributes == 0)
3125                         info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
3126         }
3127
3128         status = NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info),
3129                                       FileBasicInformation);
3130         /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set
3131          * attributes on the root directory.  (Apparently because FAT doesn't
3132          * actually have a place to store those attributes!)  */
3133         if (!NT_SUCCESS(status)
3134             && !(status == STATUS_INVALID_PARAMETER &&
3135                  dentry_is_root(inode_first_extraction_dentry(inode))))
3136         {
3137                 winnt_error(status, L"Can't set basic metadata on \"%ls\"",
3138                             current_path(ctx));
3139                 return WIMLIB_ERR_SET_ATTRIBUTES;
3140         }
3141
3142         return 0;
3143 }
3144
3145 static int
3146 apply_metadata_to_file(const struct wim_dentry *dentry,
3147                        struct win32_apply_ctx *ctx)
3148 {
3149         const struct wim_inode *inode = dentry->d_inode;
3150         DWORD perms;
3151         HANDLE h;
3152         NTSTATUS status;
3153         int ret;
3154
3155         perms = FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | WRITE_DAC |
3156                 WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
3157
3158         build_extraction_path(dentry, ctx);
3159
3160         /* Open a handle with as many relevant permissions as possible.  */
3161         while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL,
3162                                                    0, FILE_OPEN, 0, ctx)))
3163         {
3164                 if (status == STATUS_PRIVILEGE_NOT_HELD ||
3165                     status == STATUS_ACCESS_DENIED)
3166                 {
3167                         if (perms & ACCESS_SYSTEM_SECURITY) {
3168                                 perms &= ~ACCESS_SYSTEM_SECURITY;
3169                                 continue;
3170                         }
3171                         if (perms & WRITE_DAC) {
3172                                 perms &= ~WRITE_DAC;
3173                                 continue;
3174                         }
3175                         if (perms & WRITE_OWNER) {
3176                                 perms &= ~WRITE_OWNER;
3177                                 continue;
3178                         }
3179                 }
3180                 winnt_error(status, L"Can't open \"%ls\" to set metadata",
3181                             current_path(ctx));
3182                 return WIMLIB_ERR_OPEN;
3183         }
3184
3185         ret = do_apply_metadata_to_file(h, inode, ctx);
3186
3187         NtClose(h);
3188
3189         return ret;
3190 }
3191
3192 static int
3193 apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
3194 {
3195         const struct wim_dentry *dentry;
3196         int ret;
3197
3198         /* We go in reverse so that metadata is set on all a directory's
3199          * children before the directory itself.  This avoids any potential
3200          * problems with attributes, timestamps, or security descriptors.  */
3201         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node)
3202         {
3203                 ret = apply_metadata_to_file(dentry, ctx);
3204                 ret = check_apply_error(dentry, ctx, ret);
3205                 if (ret)
3206                         return ret;
3207                 ret = report_file_metadata_applied(&ctx->common);
3208                 if (ret)
3209                         return ret;
3210         }
3211         return 0;
3212 }
3213
3214 /* Issue warnings about problems during the extraction for which warnings were
3215  * not already issued (due to the high number of potential warnings if we issued
3216  * them per-file).  */
3217 static void
3218 do_warnings(const struct win32_apply_ctx *ctx)
3219 {
3220         if (ctx->partial_security_descriptors == 0
3221             && ctx->no_security_descriptors == 0
3222             && ctx->num_set_short_name_failures == 0
3223         #if 0
3224             && ctx->num_remove_short_name_failures == 0
3225         #endif
3226             )
3227                 return;
3228
3229         WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
3230                 ctx->common.target);
3231         if (ctx->num_set_short_name_failures) {
3232                 WARNING("- Could not set short names on %lu files or directories",
3233                         ctx->num_set_short_name_failures);
3234         }
3235 #if 0
3236         if (ctx->num_remove_short_name_failures) {
3237                 WARNING("- Could not remove short names on %lu files or directories"
3238                         "          (This is expected on Vista and earlier)",
3239                         ctx->num_remove_short_name_failures);
3240         }
3241 #endif
3242         if (ctx->partial_security_descriptors) {
3243                 WARNING("- Could only partially set the security descriptor\n"
3244                         "            on %lu files or directories.",
3245                         ctx->partial_security_descriptors);
3246         }
3247         if (ctx->no_security_descriptors) {
3248                 WARNING("- Could not set security descriptor at all\n"
3249                         "            on %lu files or directories.",
3250                         ctx->no_security_descriptors);
3251         }
3252         if (ctx->partial_security_descriptors || ctx->no_security_descriptors) {
3253                 WARNING("To fully restore all security descriptors, run the program\n"
3254                         "          with Administrator rights.");
3255         }
3256 }
3257
3258 static u64
3259 count_dentries(const struct list_head *dentry_list)
3260 {
3261         const struct list_head *cur;
3262         u64 count = 0;
3263
3264         list_for_each(cur, dentry_list)
3265                 count++;
3266
3267         return count;
3268 }
3269
3270 /* Extract files from a WIM image to a directory on Windows  */
3271 static int
3272 win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
3273 {
3274         int ret;
3275         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
3276         u64 dentry_count;
3277
3278         ret = prepare_target(dentry_list, ctx);
3279         if (ret)
3280                 goto out;
3281
3282         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
3283                 ret = start_wimboot_extraction(dentry_list, ctx);
3284                 if (ret)
3285                         goto out;
3286         }
3287
3288         ctx->windows_build_number = xml_get_windows_build_number(ctx->common.wim->xml_info,
3289                                                                  ctx->common.wim->current_image);
3290
3291         dentry_count = count_dentries(dentry_list);
3292
3293         ret = start_file_structure_phase(&ctx->common, dentry_count);
3294         if (ret)
3295                 goto out;
3296
3297         ret = create_directories(dentry_list, ctx);
3298         if (ret)
3299                 goto out;
3300
3301         ret = create_nondirectories(dentry_list, ctx);
3302         if (ret)
3303                 goto out;
3304
3305         ret = end_file_structure_phase(&ctx->common);
3306         if (ret)
3307                 goto out;
3308
3309         struct read_blob_callbacks cbs = {
3310                 .begin_blob     = win32_begin_extract_blob,
3311                 .continue_blob  = win32_extract_chunk,
3312                 .end_blob       = win32_end_extract_blob,
3313                 .ctx            = ctx,
3314         };
3315         ret = extract_blob_list(&ctx->common, &cbs);
3316         if (ret)
3317                 goto out;
3318
3319         ret = start_file_metadata_phase(&ctx->common, dentry_count);
3320         if (ret)
3321                 goto out;
3322
3323         ret = apply_metadata(dentry_list, ctx);
3324         if (ret)
3325                 goto out;
3326
3327         ret = end_file_metadata_phase(&ctx->common);
3328         if (ret)
3329                 goto out;
3330
3331         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
3332                 ret = end_wimboot_extraction(ctx);
3333                 if (ret)
3334                         goto out;
3335         }
3336
3337         do_warnings(ctx);
3338 out:
3339         close_target_directory(ctx);
3340         if (ctx->target_ntpath.Buffer)
3341                 HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
3342         FREE(ctx->pathbuf.Buffer);
3343         FREE(ctx->print_buffer);
3344         FREE(ctx->wimboot.wims);
3345         if (ctx->prepopulate_pats) {
3346                 FREE(ctx->prepopulate_pats->strings);
3347                 FREE(ctx->prepopulate_pats);
3348         }
3349         FREE(ctx->mem_prepopulate_pats);
3350         FREE(ctx->data_buffer);
3351         return ret;
3352 }
3353
3354 const struct apply_operations win32_apply_ops = {
3355         .name                   = "Windows",
3356         .get_supported_features = win32_get_supported_features,
3357         .extract                = win32_extract,
3358         .will_back_from_wim     = win32_will_back_from_wim,
3359         .context_size           = sizeof(struct win32_apply_ctx),
3360 };
3361
3362 #endif /* _WIN32 */