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