]> wimlib.net Git - wimlib/blob - src/win32_apply.c
win32_apply.c: Don't externally back very large files
[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, 2014, 2015 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/capture.h" /* for mangle_pat() and match_pattern_list()  */
33 #include "wimlib/dentry.h"
34 #include "wimlib/error.h"
35 #include "wimlib/lookup_table.h"
36 #include "wimlib/metadata.h"
37 #include "wimlib/paths.h"
38 #include "wimlib/reparse.h"
39 #include "wimlib/textfile.h"
40 #include "wimlib/xml.h"
41 #include "wimlib/wildcard.h"
42 #include "wimlib/wimboot.h"
43
44 struct win32_apply_ctx {
45
46         /* Extract flags, the pointer to the WIMStruct, etc.  */
47         struct apply_ctx common;
48
49         /* WIMBoot information, only filled in if WIMLIB_EXTRACT_FLAG_WIMBOOT
50          * was provided  */
51         struct {
52                 u64 data_source_id;
53                 struct string_set *prepopulate_pats;
54                 void *mem_prepopulate_pats;
55                 u8 wim_lookup_table_hash[SHA1_HASH_SIZE];
56                 bool wof_running;
57                 bool tried_to_load_prepopulate_list;
58         } wimboot;
59
60         /* Open handle to the target directory  */
61         HANDLE h_target;
62
63         /* NT namespace path to the target directory (buffer allocated)  */
64         UNICODE_STRING target_ntpath;
65
66         /* Temporary buffer for building paths (buffer allocated)  */
67         UNICODE_STRING pathbuf;
68
69         /* Object attributes to reuse for opening files in the target directory.
70          * (attr.ObjectName == &pathbuf) and (attr.RootDirectory == h_target).
71          */
72         OBJECT_ATTRIBUTES attr;
73
74         /* Temporary I/O status block for system calls  */
75         IO_STATUS_BLOCK iosb;
76
77         /* Allocated buffer for creating "printable" paths from our
78          * target-relative NT paths  */
79         wchar_t *print_buffer;
80
81         /* Allocated buffer for reading stream data when it cannot be extracted
82          * directly  */
83         u8 *data_buffer;
84
85         /* Pointer to the next byte in @data_buffer to fill  */
86         u8 *data_buffer_ptr;
87
88         /* Size allocated in @data_buffer  */
89         size_t data_buffer_size;
90
91         /* Current offset in the raw encrypted file being written  */
92         size_t encrypted_offset;
93
94         /* Current size of the raw encrypted file being written  */
95         size_t encrypted_size;
96
97         /* Temporary buffer for reparse data  */
98         struct reparse_buffer_disk rpbuf;
99
100         /* Temporary buffer for reparse data of "fixed" absolute symbolic links
101          * and junctions  */
102         struct reparse_buffer_disk rpfixbuf;
103
104         /* Array of open handles to filesystem streams currently being written
105          */
106         HANDLE open_handles[MAX_OPEN_STREAMS];
107
108         /* Number of handles in @open_handles currently open (filled in from the
109          * beginning of the array)  */
110         unsigned num_open_handles;
111
112         /* List of dentries, joined by @tmp_list, that need to have reparse data
113          * extracted as soon as the whole stream has been read into
114          * @data_buffer.  */
115         struct list_head reparse_dentries;
116
117         /* List of dentries, joined by @tmp_list, that need to have raw
118          * encrypted data extracted as soon as the whole stream has been read
119          * into @data_buffer.  */
120         struct list_head encrypted_dentries;
121
122         /* Number of files for which we didn't have permission to set the full
123          * security descriptor.  */
124         unsigned long partial_security_descriptors;
125
126         /* Number of files for which we didn't have permission to set any part
127          * of the security descriptor.  */
128         unsigned long no_security_descriptors;
129
130         /* Number of files for which we couldn't set the short name.  */
131         unsigned long num_set_short_name_failures;
132
133         /* Number of files for which we couldn't remove the short name.  */
134         unsigned long num_remove_short_name_failures;
135
136         /* Have we tried to enable short name support on the target volume yet?
137          */
138         bool tried_to_enable_short_names;
139 };
140
141 /* Get the drive letter from a Windows path, or return the null character if the
142  * path is relative.  */
143 static wchar_t
144 get_drive_letter(const wchar_t *path)
145 {
146         /* Skip \\?\ prefix  */
147         if (!wcsncmp(path, L"\\\\?\\", 4))
148                 path += 4;
149
150         /* Return drive letter if valid  */
151         if (((path[0] >= L'a' && path[0] <= L'z') ||
152              (path[0] >= L'A' && path[0] <= L'Z')) && path[1] == L':')
153                 return path[0];
154
155         return L'\0';
156 }
157
158 static void
159 get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
160               bool *short_names_supported_ret)
161 {
162         wchar_t filesystem_name[MAX_PATH + 1];
163         wchar_t drive[4];
164         wchar_t *volume = NULL;
165
166         *vol_flags_ret = 0;
167         *short_names_supported_ret = false;
168
169         drive[0] = get_drive_letter(target);
170         if (drive[0]) {
171                 drive[1] = L':';
172                 drive[2] = L'\\';
173                 drive[3] = L'\0';
174                 volume = drive;
175         }
176
177         if (!GetVolumeInformation(volume, NULL, 0, NULL, NULL,
178                                   vol_flags_ret, filesystem_name,
179                                   ARRAY_LEN(filesystem_name)))
180         {
181                 win32_warning(GetLastError(),
182                               L"Failed to get volume information for \"%ls\"",
183                               target);
184                 return;
185         }
186
187         if (wcsstr(filesystem_name, L"NTFS")) {
188                 /* FILE_SUPPORTS_HARD_LINKS is only supported on Windows 7 and
189                  * later.  Force it on anyway if filesystem is NTFS.  */
190                 *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS;
191
192                 /* There's no volume flag for short names, but according to the
193                  * MS documentation they are only user-settable on NTFS.  */
194                 *short_names_supported_ret = true;
195         }
196 }
197
198 static const wchar_t *
199 current_path(struct win32_apply_ctx *ctx);
200
201 static void
202 build_extraction_path(const struct wim_dentry *dentry,
203                       struct win32_apply_ctx *ctx);
204
205 static int
206 report_dentry_apply_error(const struct wim_dentry *dentry,
207                           struct win32_apply_ctx *ctx, int ret)
208 {
209         build_extraction_path(dentry, ctx);
210         return report_apply_error(&ctx->common, ret, current_path(ctx));
211 }
212
213 static inline int
214 check_apply_error(const struct wim_dentry *dentry,
215                   struct win32_apply_ctx *ctx, int ret)
216 {
217         if (unlikely(ret))
218                 ret = report_dentry_apply_error(dentry, ctx, ret);
219         return ret;
220 }
221
222 static int
223 win32_get_supported_features(const wchar_t *target,
224                              struct wim_features *supported_features)
225 {
226         DWORD vol_flags;
227         bool short_names_supported;
228
229         /* Query the features of the target volume.  */
230
231         get_vol_flags(target, &vol_flags, &short_names_supported);
232
233         supported_features->archive_files = 1;
234         supported_features->hidden_files = 1;
235         supported_features->system_files = 1;
236
237         if (vol_flags & FILE_FILE_COMPRESSION)
238                 supported_features->compressed_files = 1;
239
240         if (vol_flags & FILE_SUPPORTS_ENCRYPTION) {
241                 supported_features->encrypted_files = 1;
242                 supported_features->encrypted_directories = 1;
243         }
244
245         supported_features->not_context_indexed_files = 1;
246
247         /* Don't do anything with FILE_SUPPORTS_SPARSE_FILES.  */
248
249         if (vol_flags & FILE_NAMED_STREAMS)
250                 supported_features->named_data_streams = 1;
251
252         if (vol_flags & FILE_SUPPORTS_HARD_LINKS)
253                 supported_features->hard_links = 1;
254
255         if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS)
256                 supported_features->reparse_points = 1;
257
258         if (vol_flags & FILE_PERSISTENT_ACLS)
259                 supported_features->security_descriptors = 1;
260
261         if (short_names_supported)
262                 supported_features->short_names = 1;
263
264         supported_features->timestamps = 1;
265
266         /* Note: Windows does not support case sensitive filenames!  At least
267          * not without changing the registry and rebooting...  */
268
269         return 0;
270 }
271
272 /* Load the patterns from [PrepopulateList] of WimBootCompress.ini in the WIM
273  * image being extracted.  */
274 static int
275 load_prepopulate_pats(struct win32_apply_ctx *ctx)
276 {
277         const wchar_t *path = L"\\Windows\\System32\\WimBootCompress.ini";
278         struct wim_dentry *dentry;
279         struct wim_lookup_table_entry *lte;
280         int ret;
281         void *buf;
282         struct string_set *s;
283         void *mem;
284         struct text_file_section sec;
285
286         ctx->wimboot.tried_to_load_prepopulate_list = true;
287
288         dentry = get_dentry(ctx->common.wim, path, WIMLIB_CASE_INSENSITIVE);
289         if (!dentry ||
290             (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
291                                               FILE_ATTRIBUTE_REPARSE_POINT |
292                                               FILE_ATTRIBUTE_ENCRYPTED)) ||
293             !(lte = inode_unnamed_lte(dentry->d_inode, ctx->common.wim->lookup_table)))
294         {
295                 WARNING("%ls does not exist in WIM image!", path);
296                 return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
297         }
298
299         ret = read_full_stream_into_alloc_buf(lte, &buf);
300         if (ret)
301                 return ret;
302
303         s = CALLOC(1, sizeof(struct string_set));
304         if (!s) {
305                 FREE(buf);
306                 return WIMLIB_ERR_NOMEM;
307         }
308
309         sec.name = T("PrepopulateList");
310         sec.strings = s;
311
312         ret = do_load_text_file(path, buf, lte->size, &mem, &sec, 1,
313                                 LOAD_TEXT_FILE_REMOVE_QUOTES |
314                                         LOAD_TEXT_FILE_NO_WARNINGS,
315                                 mangle_pat);
316         BUILD_BUG_ON(OS_PREFERRED_PATH_SEPARATOR != WIM_PATH_SEPARATOR);
317         FREE(buf);
318         if (ret) {
319                 FREE(s);
320                 return ret;
321         }
322         ctx->wimboot.prepopulate_pats = s;
323         ctx->wimboot.mem_prepopulate_pats = mem;
324         return 0;
325 }
326
327 /* Returns %true if the specified absolute path to a file in the WIM image
328  * matches a pattern in [PrepopulateList] of WimBootCompress.ini.  Otherwise
329  * returns %false.  */
330 static bool
331 in_prepopulate_list(const wchar_t *path, size_t path_nchars,
332                     const struct win32_apply_ctx *ctx)
333 {
334         const struct string_set *pats = ctx->wimboot.prepopulate_pats;
335
336         if (!pats || !pats->num_strings)
337                 return false;
338
339         return match_pattern_list(path, path_nchars, pats);
340 }
341
342 /* Returns %true if the specified absolute path to a file in the WIM image can
343  * be subject to external backing when extracted.  Otherwise returns %false.  */
344 static bool
345 can_externally_back_path(const wchar_t *path, size_t path_nchars,
346                          const struct win32_apply_ctx *ctx)
347 {
348         if (in_prepopulate_list(path, path_nchars, ctx))
349                 return false;
350
351         /* Since we attempt to modify the SYSTEM registry after it's extracted
352          * (see end_wimboot_extraction()), it can't be extracted as externally
353          * backed.  This extends to associated files such as SYSTEM.LOG that
354          * also must be writable in order to write to the registry.  Normally,
355          * SYSTEM is in [PrepopulateList], and the SYSTEM.* files match patterns
356          * in [ExclusionList] and therefore are not captured in the WIM at all.
357          * However, a WIM that wasn't specifically captured in "WIMBoot mode"
358          * may contain SYSTEM.* files.  So to make things "just work", hard-code
359          * the pattern.  */
360         if (match_path(path, path_nchars, L"\\Windows\\System32\\config\\SYSTEM*",
361                        OS_PREFERRED_PATH_SEPARATOR, false))
362                 return false;
363
364         return true;
365 }
366
367 #define WIM_BACKING_NOT_ENABLED         -1
368 #define WIM_BACKING_NOT_POSSIBLE        -2
369 #define WIM_BACKING_EXCLUDED            -3
370
371 static int
372 will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx,
373                            const struct wim_dentry **excluded_dentry_ret)
374 {
375         struct list_head *next;
376         struct wim_dentry *dentry;
377         struct wim_lookup_table_entry *stream;
378         int ret;
379
380         if (inode->i_can_externally_back)
381                 return 0;
382
383         /* This may do redundant checks because the cached value
384          * i_can_externally_back is 2-state (as opposed to 3-state:
385          * unknown/no/yes).  But most files can be externally backed, so this
386          * way is fine.  */
387
388         if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
389                                    FILE_ATTRIBUTE_REPARSE_POINT |
390                                    FILE_ATTRIBUTE_ENCRYPTED))
391                 return WIM_BACKING_NOT_POSSIBLE;
392
393         stream = inode_unnamed_lte_resolved(inode);
394
395         /* Note: Microsoft's WoF driver errors out if it tries to satisfy a
396          * read, with ending offset >= 4 GiB, from an externally backed file. */
397         if (!stream ||
398             stream->resource_location != RESOURCE_IN_WIM ||
399             stream->rspec->wim != ctx->common.wim ||
400             stream->size != stream->rspec->uncompressed_size ||
401             stream->size > 4200000000)
402                 return WIM_BACKING_NOT_POSSIBLE;
403
404         /*
405          * We need to check the patterns in [PrepopulateList] against every name
406          * of the inode, in case any of them match.
407          */
408         next = inode->i_extraction_aliases.next;
409         do {
410                 dentry = list_entry(next, struct wim_dentry,
411                                     d_extraction_alias_node);
412
413                 ret = calculate_dentry_full_path(dentry);
414                 if (ret)
415                         return ret;
416
417                 if (!can_externally_back_path(dentry->_full_path,
418                                               wcslen(dentry->_full_path), ctx))
419                 {
420                         if (excluded_dentry_ret)
421                                 *excluded_dentry_ret = dentry;
422                         return WIM_BACKING_EXCLUDED;
423                 }
424                 next = next->next;
425         } while (next != &inode->i_extraction_aliases);
426
427         inode->i_can_externally_back = 1;
428         return 0;
429 }
430
431 /*
432  * Determines if the unnamed data stream of a file will be created as an
433  * external backing, as opposed to a standard extraction.
434  */
435 static int
436 win32_will_externally_back(struct wim_dentry *dentry, struct apply_ctx *_ctx)
437 {
438         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
439
440         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
441                 return WIM_BACKING_NOT_ENABLED;
442
443         if (!ctx->wimboot.tried_to_load_prepopulate_list)
444                 if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM)
445                         return WIMLIB_ERR_NOMEM;
446
447         return will_externally_back_inode(dentry->d_inode, ctx, NULL);
448 }
449
450 static int
451 set_external_backing(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *ctx)
452 {
453         int ret;
454         const struct wim_dentry *excluded_dentry;
455
456         ret = will_externally_back_inode(inode, ctx, &excluded_dentry);
457         if (ret > 0) /* Error.  */
458                 return ret;
459
460         if (ret < 0 && ret != WIM_BACKING_EXCLUDED)
461                 return 0; /* Not externally backing, other than due to exclusion.  */
462
463         if (unlikely(ret == WIM_BACKING_EXCLUDED)) {
464                 /* Not externally backing due to exclusion.  */
465                 union wimlib_progress_info info;
466
467                 build_extraction_path(excluded_dentry, ctx);
468
469                 info.wimboot_exclude.path_in_wim = excluded_dentry->_full_path;
470                 info.wimboot_exclude.extraction_path = current_path(ctx);
471
472                 return call_progress(ctx->common.progfunc,
473                                      WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
474                                      &info, ctx->common.progctx);
475         } else {
476                 /* Externally backing.  */
477                 if (unlikely(!wimboot_set_pointer(h,
478                                                   inode_unnamed_lte_resolved(inode),
479                                                   ctx->wimboot.data_source_id,
480                                                   ctx->wimboot.wim_lookup_table_hash,
481                                                   ctx->wimboot.wof_running)))
482                 {
483                         const DWORD err = GetLastError();
484
485                         build_extraction_path(inode_first_extraction_dentry(inode), ctx);
486                         win32_error(err, L"\"%ls\": Couldn't set WIMBoot pointer data",
487                                     current_path(ctx));
488                         return WIMLIB_ERR_WIMBOOT;
489                 }
490                 return 0;
491         }
492 }
493
494 /* Calculates the SHA-1 message digest of the WIM's lookup table.  */
495 static int
496 hash_lookup_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE])
497 {
498         return wim_reshdr_to_hash(&wim->hdr.lookup_table_reshdr, wim, hash);
499 }
500
501 /* Prepare for doing a "WIMBoot" extraction by loading patterns from
502  * [PrepopulateList] of WimBootCompress.ini and allocating a WOF data source ID
503  * on the target volume.  */
504 static int
505 start_wimboot_extraction(struct win32_apply_ctx *ctx)
506 {
507         int ret;
508         WIMStruct *wim = ctx->common.wim;
509
510         if (!ctx->wimboot.tried_to_load_prepopulate_list)
511                 if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM)
512                         return WIMLIB_ERR_NOMEM;
513
514         if (!wim_info_get_wimboot(wim->wim_info, wim->current_image))
515                 WARNING("Image is not marked as WIMBoot compatible!");
516
517         ret = hash_lookup_table(ctx->common.wim,
518                                 ctx->wimboot.wim_lookup_table_hash);
519         if (ret)
520                 return ret;
521
522         return wimboot_alloc_data_source_id(wim->filename,
523                                             wim->hdr.guid,
524                                             wim->current_image,
525                                             ctx->common.target,
526                                             &ctx->wimboot.data_source_id,
527                                             &ctx->wimboot.wof_running);
528 }
529
530 static void
531 build_win32_extraction_path(const struct wim_dentry *dentry,
532                             struct win32_apply_ctx *ctx);
533
534 /* Sets WimBoot=1 in the extracted SYSTEM registry hive.
535  *
536  * WIMGAPI does this, and it's possible that it's important.
537  * But I don't know exactly what this value means to Windows.  */
538 static int
539 end_wimboot_extraction(struct win32_apply_ctx *ctx)
540 {
541         struct wim_dentry *dentry;
542         wchar_t subkeyname[32];
543         LONG res;
544         LONG res2;
545         HKEY key;
546         DWORD value;
547
548         dentry = get_dentry(ctx->common.wim, L"\\Windows\\System32\\config\\SYSTEM",
549                             WIMLIB_CASE_INSENSITIVE);
550
551         if (!dentry || !will_extract_dentry(dentry))
552                 goto out;
553
554         if (!will_extract_dentry(wim_get_current_root_dentry(ctx->common.wim)))
555                 goto out;
556
557         /* Not bothering to use the native routines (e.g. NtLoadKey()) for this.
558          * If this doesn't work, you probably also have many other problems.  */
559
560         build_win32_extraction_path(dentry, ctx);
561
562         randomize_char_array_with_alnum(subkeyname, 20);
563         subkeyname[20] = L'\0';
564
565         res = RegLoadKey(HKEY_LOCAL_MACHINE, subkeyname, ctx->pathbuf.Buffer);
566         if (res)
567                 goto out_check_res;
568
569         wcscpy(&subkeyname[20], L"\\Setup");
570
571         res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, subkeyname, 0, NULL,
572                              REG_OPTION_BACKUP_RESTORE, 0, NULL, &key, NULL);
573         if (res)
574                 goto out_unload_key;
575
576         value = 1;
577
578         res = RegSetValueEx(key, L"WimBoot", 0, REG_DWORD,
579                             (const BYTE *)&value, sizeof(DWORD));
580         if (res)
581                 goto out_close_key;
582
583         res = RegFlushKey(key);
584
585 out_close_key:
586         res2 = RegCloseKey(key);
587         if (!res)
588                 res = res2;
589 out_unload_key:
590         subkeyname[20] = L'\0';
591         RegUnLoadKey(HKEY_LOCAL_MACHINE, subkeyname);
592 out_check_res:
593         if (res) {
594                 /* Warning only.  */
595                 win32_warning(res, L"Failed to set \\Setup: dword \"WimBoot\"=1 "
596                               "value in registry hive \"%ls\"",
597                               ctx->pathbuf.Buffer);
598         }
599 out:
600         return 0;
601 }
602
603 /* Returns the number of wide characters needed to represent the path to the
604  * specified @dentry, relative to the target directory, when extracted.
605  *
606  * Does not include null terminator (not needed for NtCreateFile).  */
607 static size_t
608 dentry_extraction_path_length(const struct wim_dentry *dentry)
609 {
610         size_t len = 0;
611         const struct wim_dentry *d;
612
613         d = dentry;
614         do {
615                 len += d->d_extraction_name_nchars + 1;
616                 d = d->d_parent;
617         } while (!dentry_is_root(d) && will_extract_dentry(d));
618
619         return --len;  /* No leading slash  */
620 }
621
622 /* Returns the length of the longest string that might need to be appended to
623  * the path to an alias of an inode to open or create a named data stream.
624  *
625  * If the inode has no named data streams, this will be 0.  Otherwise, this will
626  * be 1 plus the length of the longest-named data stream, since the data stream
627  * name must be separated from the path by the ':' character.  */
628 static size_t
629 inode_longest_named_data_stream_spec(const struct wim_inode *inode)
630 {
631         size_t max = 0;
632         for (u16 i = 0; i < inode->i_num_ads; i++) {
633                 size_t len = inode->i_ads_entries[i].stream_name_nbytes;
634                 if (len > max)
635                         max = len;
636         }
637         if (max)
638                 max = 1 + (max / sizeof(wchar_t));
639         return max;
640 }
641
642 /* Find the length, in wide characters, of the longest path needed for
643  * extraction of any file in @dentry_list relative to the target directory.
644  *
645  * Accounts for named data streams, but does not include null terminator (not
646  * needed for NtCreateFile).  */
647 static size_t
648 compute_path_max(struct list_head *dentry_list)
649 {
650         size_t max = 0;
651         const struct wim_dentry *dentry;
652
653         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
654                 size_t len;
655
656                 len = dentry_extraction_path_length(dentry);
657
658                 /* Account for named data streams  */
659                 len += inode_longest_named_data_stream_spec(dentry->d_inode);
660
661                 if (len > max)
662                         max = len;
663         }
664
665         return max;
666 }
667
668 /* Build the path at which to extract the @dentry, relative to the target
669  * directory.
670  *
671  * The path is saved in ctx->pathbuf.  */
672 static void
673 build_extraction_path(const struct wim_dentry *dentry,
674                       struct win32_apply_ctx *ctx)
675 {
676         size_t len;
677         wchar_t *p;
678         const struct wim_dentry *d;
679
680         len = dentry_extraction_path_length(dentry);
681
682         ctx->pathbuf.Length = len * sizeof(wchar_t);
683         p = ctx->pathbuf.Buffer + len;
684         for (d = dentry;
685              !dentry_is_root(d->d_parent) && will_extract_dentry(d->d_parent);
686              d = d->d_parent)
687         {
688                 p -= d->d_extraction_name_nchars;
689                 wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
690                 *--p = '\\';
691         }
692         /* No leading slash  */
693         p -= d->d_extraction_name_nchars;
694         wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
695 }
696
697 /* Build the path at which to extract the @dentry, relative to the target
698  * directory, adding the suffix for a named data stream.
699  *
700  * The path is saved in ctx->pathbuf.  */
701 static void
702 build_extraction_path_with_ads(const struct wim_dentry *dentry,
703                                struct win32_apply_ctx *ctx,
704                                const wchar_t *stream_name,
705                                size_t stream_name_nchars)
706 {
707         wchar_t *p;
708
709         build_extraction_path(dentry, ctx);
710
711         /* Add :NAME for named data stream  */
712         p = ctx->pathbuf.Buffer + (ctx->pathbuf.Length / sizeof(wchar_t));
713         *p++ = L':';
714         wmemcpy(p, stream_name, stream_name_nchars);
715         ctx->pathbuf.Length += (1 + stream_name_nchars) * sizeof(wchar_t);
716 }
717
718 /* Build the Win32 namespace path to the specified @dentry when extracted.
719  *
720  * The path is saved in ctx->pathbuf and will be null terminated.
721  *
722  * XXX: We could get rid of this if it wasn't needed for the file encryption
723  * APIs, and the registry manipulation in WIMBoot mode.  */
724 static void
725 build_win32_extraction_path(const struct wim_dentry *dentry,
726                             struct win32_apply_ctx *ctx)
727 {
728         build_extraction_path(dentry, ctx);
729
730         /* Prepend target_ntpath to our relative path, then change \??\ into \\?\  */
731
732         memmove(ctx->pathbuf.Buffer +
733                         (ctx->target_ntpath.Length / sizeof(wchar_t)) + 1,
734                 ctx->pathbuf.Buffer, ctx->pathbuf.Length);
735         memcpy(ctx->pathbuf.Buffer, ctx->target_ntpath.Buffer,
736                 ctx->target_ntpath.Length);
737         ctx->pathbuf.Buffer[ctx->target_ntpath.Length / sizeof(wchar_t)] = L'\\';
738         ctx->pathbuf.Length += ctx->target_ntpath.Length + sizeof(wchar_t);
739         ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)] = L'\0';
740
741         wimlib_assert(ctx->pathbuf.Length >= 4 * sizeof(wchar_t) &&
742                       !wmemcmp(ctx->pathbuf.Buffer, L"\\??\\", 4));
743
744         ctx->pathbuf.Buffer[1] = L'\\';
745
746 }
747
748 /* Returns a "printable" representation of the last relative NT path that was
749  * constructed with build_extraction_path() or build_extraction_path_with_ads().
750  *
751  * This will be overwritten by the next call to this function.  */
752 static const wchar_t *
753 current_path(struct win32_apply_ctx *ctx)
754 {
755         wchar_t *p = ctx->print_buffer;
756
757         p = wmempcpy(p, ctx->common.target, ctx->common.target_nchars);
758         *p++ = L'\\';
759         p = wmempcpy(p, ctx->pathbuf.Buffer, ctx->pathbuf.Length / sizeof(wchar_t));
760         *p = L'\0';
761         return ctx->print_buffer;
762 }
763
764 /* Open handle to the target directory if it is not already open.  If the target
765  * directory does not exist, this creates it.  */
766 static int
767 open_target_directory(struct win32_apply_ctx *ctx)
768 {
769         NTSTATUS status;
770
771         if (ctx->h_target)
772                 return 0;
773
774         ctx->attr.Length = sizeof(ctx->attr);
775         ctx->attr.RootDirectory = NULL;
776         ctx->attr.ObjectName = &ctx->target_ntpath;
777         status = (*func_NtCreateFile)(&ctx->h_target,
778                                       FILE_TRAVERSE,
779                                       &ctx->attr,
780                                       &ctx->iosb,
781                                       NULL,
782                                       0,
783                                       FILE_SHARE_VALID_FLAGS,
784                                       FILE_OPEN_IF,
785                                       FILE_DIRECTORY_FILE |
786                                               FILE_OPEN_REPARSE_POINT |
787                                               FILE_OPEN_FOR_BACKUP_INTENT,
788                                       NULL,
789                                       0);
790         if (!NT_SUCCESS(status)) {
791                 winnt_error(status, L"Can't open or create directory \"%ls\"",
792                             ctx->common.target);
793                 return WIMLIB_ERR_OPENDIR;
794         }
795         ctx->attr.RootDirectory = ctx->h_target;
796         ctx->attr.ObjectName = &ctx->pathbuf;
797         return 0;
798 }
799
800 static void
801 close_target_directory(struct win32_apply_ctx *ctx)
802 {
803         if (ctx->h_target) {
804                 (*func_NtClose)(ctx->h_target);
805                 ctx->h_target = NULL;
806                 ctx->attr.RootDirectory = NULL;
807         }
808 }
809
810 /*
811  * Ensures the target directory exists and opens a handle to it, in preparation
812  * of using paths relative to it.
813  */
814 static int
815 prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
816 {
817         int ret;
818         size_t path_max;
819
820         ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath);
821         if (ret)
822                 return ret;
823
824         ret = open_target_directory(ctx);
825         if (ret)
826                 return ret;
827
828         path_max = compute_path_max(dentry_list);
829         /* Add some extra for building Win32 paths for the file encryption APIs,
830          * and ensure we have at least enough to potentially use a 8.3 name for
831          * the last component.  */
832         path_max += max(2 + (ctx->target_ntpath.Length / sizeof(wchar_t)),
833                         8 + 1 + 3);
834
835         ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t);
836         ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength);
837         if (!ctx->pathbuf.Buffer)
838                 return WIMLIB_ERR_NOMEM;
839
840         ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) *
841                                    sizeof(wchar_t));
842         if (!ctx->print_buffer)
843                 return WIMLIB_ERR_NOMEM;
844
845         return 0;
846 }
847
848 /* When creating an inode that will have a short (DOS) name, we create it using
849  * the long name associated with the short name.  This ensures that the short
850  * name gets associated with the correct long name.  */
851 static struct wim_dentry *
852 first_extraction_alias(const struct wim_inode *inode)
853 {
854         struct list_head *next = inode->i_extraction_aliases.next;
855         struct wim_dentry *dentry;
856
857         do {
858                 dentry = list_entry(next, struct wim_dentry,
859                                     d_extraction_alias_node);
860                 if (dentry_has_short_name(dentry))
861                         break;
862                 next = next->next;
863         } while (next != &inode->i_extraction_aliases);
864         return dentry;
865 }
866
867 /*
868  * Set or clear FILE_ATTRIBUTE_COMPRESSED if the inherited value is different
869  * from the desired value.
870  *
871  * Note that you can NOT override the inherited value of
872  * FILE_ATTRIBUTE_COMPRESSED directly with NtCreateFile().
873  */
874 static int
875 adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
876                              struct win32_apply_ctx *ctx)
877 {
878         const bool compressed = (dentry->d_inode->i_attributes &
879                                  FILE_ATTRIBUTE_COMPRESSED);
880
881         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
882                 return 0;
883
884         if (!ctx->common.supported_features.compressed_files)
885                 return 0;
886
887         FILE_BASIC_INFORMATION info;
888         NTSTATUS status;
889         USHORT compression_state;
890
891         /* Get current attributes  */
892         status = (*func_NtQueryInformationFile)(h, &ctx->iosb,
893                                                 &info, sizeof(info),
894                                                 FileBasicInformation);
895         if (NT_SUCCESS(status) &&
896             compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED))
897         {
898                 /* Nothing needs to be done.  */
899                 return 0;
900         }
901
902         /* Set the new compression state  */
903
904         if (compressed)
905                 compression_state = COMPRESSION_FORMAT_DEFAULT;
906         else
907                 compression_state = COMPRESSION_FORMAT_NONE;
908
909         status = (*func_NtFsControlFile)(h,
910                                          NULL,
911                                          NULL,
912                                          NULL,
913                                          &ctx->iosb,
914                                          FSCTL_SET_COMPRESSION,
915                                          &compression_state,
916                                          sizeof(USHORT),
917                                          NULL,
918                                          0);
919         if (NT_SUCCESS(status))
920                 return 0;
921
922         winnt_error(status, L"Can't %s compression attribute on \"%ls\"",
923                     (compressed ? "set" : "clear"), current_path(ctx));
924         return WIMLIB_ERR_SET_ATTRIBUTES;
925 }
926
927 /* Try to enable short name support on the target volume.  If successful, return
928  * true.  If unsuccessful, issue a warning and return false.  */
929 static bool
930 try_to_enable_short_names(const wchar_t *volume)
931 {
932         HANDLE h;
933         FILE_FS_PERSISTENT_VOLUME_INFORMATION info;
934         BOOL bret;
935         DWORD bytesReturned;
936
937         h = CreateFile(volume, GENERIC_WRITE,
938                        FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
939                        FILE_FLAG_BACKUP_SEMANTICS, NULL);
940         if (h == INVALID_HANDLE_VALUE)
941                 goto fail;
942
943         info.VolumeFlags = 0;
944         info.FlagMask = PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED;
945         info.Version = 1;
946         info.Reserved = 0;
947
948         bret = DeviceIoControl(h, FSCTL_SET_PERSISTENT_VOLUME_STATE,
949                                &info, sizeof(info), NULL, 0,
950                                &bytesReturned, NULL);
951
952         CloseHandle(h);
953
954         if (!bret)
955                 goto fail;
956         return true;
957
958 fail:
959         win32_warning(GetLastError(),
960                       L"Failed to enable short name support on %ls",
961                       volume + 4);
962         return false;
963 }
964
965 static NTSTATUS
966 remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
967 {
968         wchar_t *name;
969         wchar_t *end;
970         NTSTATUS status;
971         HANDLE h;
972         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
973                          (13 * sizeof(wchar_t));
974         u8 buf[bufsize] _aligned_attribute(8);
975         bool retried = false;
976         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
977
978         memset(buf, 0, bufsize);
979
980         /* Build the path with the short name.  */
981         name = &ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)];
982         while (name != ctx->pathbuf.Buffer && *(name - 1) != L'\\')
983                 name--;
984         end = mempcpy(name, dentry->short_name, dentry->short_name_nbytes);
985         ctx->pathbuf.Length = ((u8 *)end - (u8 *)ctx->pathbuf.Buffer);
986
987         /* Open the conflicting file (by short name).  */
988         status = (*func_NtOpenFile)(&h, GENERIC_WRITE | DELETE,
989                                     &ctx->attr, &ctx->iosb,
990                                     FILE_SHARE_VALID_FLAGS,
991                                     FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT);
992         if (!NT_SUCCESS(status)) {
993                 winnt_warning(status, L"Can't open \"%ls\"", current_path(ctx));
994                 goto out;
995         }
996
997 #if 0
998         WARNING("Overriding conflicting short name; path=\"%ls\"",
999                 current_path(ctx));
1000 #endif
1001
1002         /* Try to remove the short name on the conflicting file.  */
1003
1004 retry:
1005         status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
1006                                               FileShortNameInformation);
1007
1008         if (status == STATUS_INVALID_PARAMETER && !retried) {
1009
1010                 /* Microsoft forgot to make it possible to remove short names
1011                  * until Windows 7.  Oops.  Use a random short name instead.  */
1012
1013                 info->FileNameLength = 12 * sizeof(wchar_t);
1014                 for (int i = 0; i < 8; i++)
1015                         info->FileName[i] = 'A' + (rand() % 26);
1016                 info->FileName[8] = L'.';
1017                 info->FileName[9] = L'W';
1018                 info->FileName[10] = L'L';
1019                 info->FileName[11] = L'B';
1020                 info->FileName[12] = L'\0';
1021                 retried = true;
1022                 goto retry;
1023         }
1024         (*func_NtClose)(h);
1025 out:
1026         build_extraction_path(dentry, ctx);
1027         return status;
1028 }
1029
1030 /* Set the short name on the open file @h which has been created at the location
1031  * indicated by @dentry.
1032  *
1033  * Note that this may add, change, or remove the short name.
1034  *
1035  * @h must be opened with DELETE access.
1036  *
1037  * Returns 0 or WIMLIB_ERR_SET_SHORT_NAME.  The latter only happens in
1038  * STRICT_SHORT_NAMES mode.
1039  */
1040 static int
1041 set_short_name(HANDLE h, const struct wim_dentry *dentry,
1042                struct win32_apply_ctx *ctx)
1043 {
1044
1045         if (!ctx->common.supported_features.short_names)
1046                 return 0;
1047
1048         /*
1049          * Note: The size of the FILE_NAME_INFORMATION buffer must be such that
1050          * FileName contains at least 2 wide characters (4 bytes).  Otherwise,
1051          * NtSetInformationFile() will return STATUS_INFO_LENGTH_MISMATCH.  This
1052          * is despite the fact that FileNameLength can validly be 0 or 2 bytes,
1053          * with the former case being removing the existing short name if
1054          * present, rather than setting one.
1055          *
1056          * The null terminator is seemingly optional, but to be safe we include
1057          * space for it and zero all unused space.
1058          */
1059
1060         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
1061                          max(dentry->short_name_nbytes, sizeof(wchar_t)) +
1062                          sizeof(wchar_t);
1063         u8 buf[bufsize] _aligned_attribute(8);
1064         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
1065         NTSTATUS status;
1066         bool tried_to_remove_existing = false;
1067
1068         memset(buf, 0, bufsize);
1069
1070         info->FileNameLength = dentry->short_name_nbytes;
1071         memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
1072
1073 retry:
1074         status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
1075                                               FileShortNameInformation);
1076         if (NT_SUCCESS(status))
1077                 return 0;
1078
1079         if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
1080                 if (dentry->short_name_nbytes == 0)
1081                         return 0;
1082                 if (!ctx->tried_to_enable_short_names) {
1083                         wchar_t volume[7];
1084                         int ret;
1085
1086                         ctx->tried_to_enable_short_names = true;
1087
1088                         ret = win32_get_drive_path(ctx->common.target,
1089                                                    volume);
1090                         if (ret)
1091                                 return ret;
1092                         if (try_to_enable_short_names(volume))
1093                                 goto retry;
1094                 }
1095         }
1096
1097         /*
1098          * Short names can conflict in several cases:
1099          *
1100          * - a file being extracted has a short name conflicting with an
1101          *   existing file
1102          *
1103          * - a file being extracted has a short name conflicting with another
1104          *   file being extracted (possible, but shouldn't happen)
1105          *
1106          * - a file being extracted has a short name that conflicts with the
1107          *   automatically generated short name of a file we previously
1108          *   extracted, but failed to set the short name for.  Sounds unlikely,
1109          *   but this actually does happen fairly often on versions of Windows
1110          *   prior to Windows 7 because they do not support removing short names
1111          *   from files.
1112          */
1113         if (unlikely(status == STATUS_OBJECT_NAME_COLLISION) &&
1114             dentry->short_name_nbytes && !tried_to_remove_existing)
1115         {
1116                 tried_to_remove_existing = true;
1117                 status = remove_conflicting_short_name(dentry, ctx);
1118                 if (NT_SUCCESS(status))
1119                         goto retry;
1120         }
1121
1122         /* By default, failure to set short names is not an error (since short
1123          * names aren't too important anymore...).  */
1124         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES)) {
1125                 if (dentry->short_name_nbytes)
1126                         ctx->num_set_short_name_failures++;
1127                 else
1128                         ctx->num_remove_short_name_failures++;
1129                 return 0;
1130         }
1131
1132         winnt_error(status, L"Can't set short name on \"%ls\"", current_path(ctx));
1133         return WIMLIB_ERR_SET_SHORT_NAME;
1134 }
1135
1136 /*
1137  * A wrapper around NtCreateFile() to make it slightly more usable...
1138  * This uses the path currently constructed in ctx->pathbuf.
1139  *
1140  * Also, we always specify FILE_OPEN_FOR_BACKUP_INTENT and
1141  * FILE_OPEN_REPARSE_POINT.
1142  */
1143 static NTSTATUS
1144 do_create_file(PHANDLE FileHandle,
1145                ACCESS_MASK DesiredAccess,
1146                PLARGE_INTEGER AllocationSize,
1147                ULONG FileAttributes,
1148                ULONG CreateDisposition,
1149                ULONG CreateOptions,
1150                struct win32_apply_ctx *ctx)
1151 {
1152         return (*func_NtCreateFile)(FileHandle,
1153                                     DesiredAccess,
1154                                     &ctx->attr,
1155                                     &ctx->iosb,
1156                                     AllocationSize,
1157                                     FileAttributes,
1158                                     FILE_SHARE_VALID_FLAGS,
1159                                     CreateDisposition,
1160                                     CreateOptions |
1161                                         FILE_OPEN_FOR_BACKUP_INTENT |
1162                                         FILE_OPEN_REPARSE_POINT,
1163                                     NULL,
1164                                     0);
1165 }
1166
1167 /* Like do_create_file(), but builds the extraction path of the @dentry first.
1168  */
1169 static NTSTATUS
1170 create_file(PHANDLE FileHandle,
1171             ACCESS_MASK DesiredAccess,
1172             PLARGE_INTEGER AllocationSize,
1173             ULONG FileAttributes,
1174             ULONG CreateDisposition,
1175             ULONG CreateOptions,
1176             const struct wim_dentry *dentry,
1177             struct win32_apply_ctx *ctx)
1178 {
1179         build_extraction_path(dentry, ctx);
1180         return do_create_file(FileHandle,
1181                               DesiredAccess,
1182                               AllocationSize,
1183                               FileAttributes,
1184                               CreateDisposition,
1185                               CreateOptions,
1186                               ctx);
1187 }
1188
1189 static int
1190 delete_file_or_stream(struct win32_apply_ctx *ctx)
1191 {
1192         NTSTATUS status;
1193         HANDLE h;
1194         FILE_DISPOSITION_INFORMATION disposition_info;
1195         FILE_BASIC_INFORMATION basic_info;
1196         bool retried = false;
1197
1198         status = do_create_file(&h,
1199                                 DELETE,
1200                                 NULL,
1201                                 0,
1202                                 FILE_OPEN,
1203                                 FILE_NON_DIRECTORY_FILE,
1204                                 ctx);
1205         if (unlikely(!NT_SUCCESS(status))) {
1206                 winnt_error(status, L"Can't open \"%ls\" for deletion",
1207                             current_path(ctx));
1208                 return WIMLIB_ERR_OPEN;
1209         }
1210
1211 retry:
1212         disposition_info.DoDeleteFile = TRUE;
1213         status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1214                                               &disposition_info,
1215                                               sizeof(disposition_info),
1216                                               FileDispositionInformation);
1217         (*func_NtClose)(h);
1218         if (likely(NT_SUCCESS(status)))
1219                 return 0;
1220
1221         if (status == STATUS_CANNOT_DELETE && !retried) {
1222                 /* Clear file attributes and try again.  This is necessary for
1223                  * FILE_ATTRIBUTE_READONLY files.  */
1224                 status = do_create_file(&h,
1225                                         FILE_WRITE_ATTRIBUTES | DELETE,
1226                                         NULL,
1227                                         0,
1228                                         FILE_OPEN,
1229                                         FILE_NON_DIRECTORY_FILE,
1230                                         ctx);
1231                 if (!NT_SUCCESS(status)) {
1232                         winnt_error(status,
1233                                     L"Can't open \"%ls\" to reset attributes",
1234                                     current_path(ctx));
1235                         return WIMLIB_ERR_OPEN;
1236                 }
1237                 memset(&basic_info, 0, sizeof(basic_info));
1238                 basic_info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
1239                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1240                                                       &basic_info,
1241                                                       sizeof(basic_info),
1242                                                       FileBasicInformation);
1243                 if (!NT_SUCCESS(status)) {
1244                         winnt_error(status,
1245                                     L"Can't reset file attributes on \"%ls\"",
1246                                     current_path(ctx));
1247                         (*func_NtClose)(h);
1248                         return WIMLIB_ERR_SET_ATTRIBUTES;
1249                 }
1250                 retried = true;
1251                 goto retry;
1252         }
1253         winnt_error(status, L"Can't delete \"%ls\"", current_path(ctx));
1254         return WIMLIB_ERR_OPEN;
1255 }
1256
1257 /*
1258  * Create a nondirectory file or named data stream at the current path,
1259  * superseding any that already exists at that path.  If successful, return an
1260  * open handle to the file or named data stream.
1261  */
1262 static int
1263 supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret)
1264 {
1265         NTSTATUS status;
1266         bool retried = false;
1267
1268         /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that
1269          * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be.  */
1270 retry:
1271         status = do_create_file(h_ret,
1272                                 GENERIC_READ | GENERIC_WRITE | DELETE,
1273                                 NULL,
1274                                 FILE_ATTRIBUTE_SYSTEM,
1275                                 FILE_CREATE,
1276                                 FILE_NON_DIRECTORY_FILE,
1277                                 ctx);
1278         if (likely(NT_SUCCESS(status)))
1279                 return 0;
1280
1281         /* STATUS_OBJECT_NAME_COLLISION means that the file or stream already
1282          * exists.  Delete the existing file or stream, then try again.
1283          *
1284          * Note: we don't use FILE_OVERWRITE_IF or FILE_SUPERSEDE because of
1285          * problems with certain file attributes, especially
1286          * FILE_ATTRIBUTE_ENCRYPTED.  FILE_SUPERSEDE is also broken in the
1287          * Windows PE ramdisk.  */
1288         if (status == STATUS_OBJECT_NAME_COLLISION && !retried) {
1289                 int ret = delete_file_or_stream(ctx);
1290                 if (ret)
1291                         return ret;
1292                 retried = true;
1293                 goto retry;
1294         }
1295         winnt_error(status, L"Can't create \"%ls\"", current_path(ctx));
1296         return WIMLIB_ERR_OPEN;
1297 }
1298
1299 /* Create empty named data streams.
1300  *
1301  * Since these won't have 'struct wim_lookup_table_entry's, they won't show up
1302  * in the call to extract_stream_list().  Hence the need for the special case.
1303  */
1304 static int
1305 create_any_empty_ads(const struct wim_dentry *dentry,
1306                      struct win32_apply_ctx *ctx)
1307 {
1308         const struct wim_inode *inode = dentry->d_inode;
1309         bool path_modified = false;
1310         int ret = 0;
1311
1312         if (!ctx->common.supported_features.named_data_streams)
1313                 return 0;
1314
1315         for (u16 i = 0; i < inode->i_num_ads; i++) {
1316                 const struct wim_ads_entry *entry;
1317                 HANDLE h;
1318
1319                 entry = &inode->i_ads_entries[i];
1320
1321                 /* Not named?  */
1322                 if (!entry->stream_name_nbytes)
1323                         continue;
1324
1325                 /* Not empty?  */
1326                 if (entry->lte)
1327                         continue;
1328
1329                 build_extraction_path_with_ads(dentry, ctx,
1330                                                entry->stream_name,
1331                                                entry->stream_name_nbytes /
1332                                                         sizeof(wchar_t));
1333                 path_modified = true;
1334                 ret = supersede_file_or_stream(ctx, &h);
1335                 if (ret)
1336                         break;
1337                 (*func_NtClose)(h);
1338         }
1339         /* Restore the path to the dentry itself  */
1340         if (path_modified)
1341                 build_extraction_path(dentry, ctx);
1342         return ret;
1343 }
1344
1345 /*
1346  * Creates the directory named by @dentry, or uses an existing directory at that
1347  * location.  If necessary, sets the short name and/or fixes compression and
1348  * encryption attributes.
1349  *
1350  * Returns 0, WIMLIB_ERR_MKDIR, or WIMLIB_ERR_SET_SHORT_NAME.
1351  */
1352 static int
1353 create_directory(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
1354 {
1355         DWORD perms;
1356         NTSTATUS status;
1357         HANDLE h;
1358         int ret;
1359
1360         /* DELETE is needed for set_short_name(); GENERIC_READ and GENERIC_WRITE
1361          * are needed for adjust_compression_attribute().  */
1362         perms = GENERIC_READ | GENERIC_WRITE;
1363         if (!dentry_is_root(dentry))
1364                 perms |= DELETE;
1365
1366         /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that
1367          * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be.  */
1368         status = create_file(&h, perms, NULL, FILE_ATTRIBUTE_SYSTEM,
1369                              FILE_OPEN_IF, FILE_DIRECTORY_FILE, dentry, ctx);
1370         if (!NT_SUCCESS(status)) {
1371                 winnt_error(status, L"Can't create directory \"%ls\"",
1372                             current_path(ctx));
1373                 return WIMLIB_ERR_MKDIR;
1374         }
1375
1376         if (ctx->iosb.Information == FILE_OPENED) {
1377                 /* If we opened an existing directory, try to clear its file
1378                  * attributes.  As far as I know, this only actually makes a
1379                  * difference in the case where a FILE_ATTRIBUTE_READONLY
1380                  * directory has a named data stream which needs to be
1381                  * extracted.  You cannot create a named data stream of such a
1382                  * directory, even though this contradicts Microsoft's
1383                  * documentation for FILE_ATTRIBUTE_READONLY which states it is
1384                  * not honored for directories!  */
1385                 FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL };
1386                 (*func_NtSetInformationFile)(h, &ctx->iosb, &basic_info,
1387                                              sizeof(basic_info), FileBasicInformation);
1388         }
1389
1390         if (!dentry_is_root(dentry)) {
1391                 ret = set_short_name(h, dentry, ctx);
1392                 if (ret)
1393                         goto out;
1394         }
1395
1396         ret = adjust_compression_attribute(h, dentry, ctx);
1397 out:
1398         (*func_NtClose)(h);
1399         return ret;
1400 }
1401
1402 /*
1403  * Create all the directories being extracted, other than the target directory
1404  * itself.
1405  *
1406  * Note: we don't honor directory hard links.  However, we don't allow them to
1407  * exist in WIM images anyway (see inode_fixup.c).
1408  */
1409 static int
1410 create_directories(struct list_head *dentry_list,
1411                    struct win32_apply_ctx *ctx)
1412 {
1413         const struct wim_dentry *dentry;
1414         int ret;
1415
1416         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1417
1418                 if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
1419                         continue;
1420
1421                 /* Note: Here we include files with
1422                  * FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT, but we
1423                  * wait until later to actually set the reparse data.  */
1424
1425                 ret = create_directory(dentry, ctx);
1426
1427                 if (!ret)
1428                         ret = create_any_empty_ads(dentry, ctx);
1429
1430                 ret = check_apply_error(dentry, ctx, ret);
1431                 if (ret)
1432                         return ret;
1433
1434                 ret = report_file_created(&ctx->common);
1435                 if (ret)
1436                         return ret;
1437         }
1438         return 0;
1439 }
1440
1441 /*
1442  * Creates the nondirectory file named by @dentry.
1443  *
1444  * On success, returns an open handle to the file in @h_ret, with GENERIC_READ,
1445  * GENERIC_WRITE, and DELETE access.  Also, the path to the file will be saved
1446  * in ctx->pathbuf.  On failure, returns an error code.
1447  */
1448 static int
1449 create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
1450                           struct win32_apply_ctx *ctx)
1451 {
1452         int ret;
1453         HANDLE h;
1454
1455         build_extraction_path(dentry, ctx);
1456
1457         ret = supersede_file_or_stream(ctx, &h);
1458         if (ret)
1459                 goto out;
1460
1461         ret = adjust_compression_attribute(h, dentry, ctx);
1462         if (ret)
1463                 goto out_close;
1464
1465         ret = create_any_empty_ads(dentry, ctx);
1466         if (ret)
1467                 goto out_close;
1468
1469         *h_ret = h;
1470         return 0;
1471
1472 out_close:
1473         (*func_NtClose)(h);
1474 out:
1475         return ret;
1476 }
1477
1478 /* Creates a hard link at the location named by @dentry to the file represented
1479  * by the open handle @h.  Or, if the target volume does not support hard links,
1480  * create a separate file instead.  */
1481 static int
1482 create_link(HANDLE h, const struct wim_dentry *dentry,
1483             struct win32_apply_ctx *ctx)
1484 {
1485         if (ctx->common.supported_features.hard_links) {
1486
1487                 build_extraction_path(dentry, ctx);
1488
1489                 size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
1490                                  ctx->pathbuf.Length + sizeof(wchar_t);
1491                 u8 buf[bufsize] _aligned_attribute(8);
1492                 FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
1493                 NTSTATUS status;
1494
1495                 info->ReplaceIfExists = TRUE;
1496                 info->RootDirectory = ctx->attr.RootDirectory;
1497                 info->FileNameLength = ctx->pathbuf.Length;
1498                 memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
1499                 info->FileName[info->FileNameLength / 2] = L'\0';
1500
1501                 /* Note: the null terminator isn't actually necessary,
1502                  * but if you don't add the extra character, you get
1503                  * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
1504                  * happens to be 2  */
1505
1506                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1507                                                       info, bufsize,
1508                                                       FileLinkInformation);
1509                 if (NT_SUCCESS(status))
1510                         return 0;
1511                 winnt_error(status, L"Failed to create link \"%ls\"",
1512                             current_path(ctx));
1513                 return WIMLIB_ERR_LINK;
1514         } else {
1515                 HANDLE h2;
1516                 int ret;
1517
1518                 ret = create_nondirectory_inode(&h2, dentry, ctx);
1519                 if (ret)
1520                         return ret;
1521
1522                 (*func_NtClose)(h2);
1523                 return 0;
1524         }
1525 }
1526
1527 /* Given an inode (represented by the open handle @h) for which one link has
1528  * been created (named by @first_dentry), create the other links.
1529  *
1530  * Or, if the target volume does not support hard links, create separate files.
1531  *
1532  * Note: This uses ctx->pathbuf and does not reset it.
1533  */
1534 static int
1535 create_links(HANDLE h, const struct wim_dentry *first_dentry,
1536              struct win32_apply_ctx *ctx)
1537 {
1538         const struct wim_inode *inode;
1539         const struct list_head *next;
1540         const struct wim_dentry *dentry;
1541         int ret;
1542
1543         inode = first_dentry->d_inode;
1544         next = inode->i_extraction_aliases.next;
1545         do {
1546                 dentry = list_entry(next, struct wim_dentry,
1547                                     d_extraction_alias_node);
1548                 if (dentry != first_dentry) {
1549                         ret = create_link(h, dentry, ctx);
1550                         if (ret)
1551                                 return ret;
1552                 }
1553                 next = next->next;
1554         } while (next != &inode->i_extraction_aliases);
1555         return 0;
1556 }
1557
1558 /* Create a nondirectory file, including all links.  */
1559 static int
1560 create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx)
1561 {
1562         struct wim_dentry *first_dentry;
1563         HANDLE h;
1564         int ret;
1565
1566         first_dentry = first_extraction_alias(inode);
1567
1568         /* Create first link.  */
1569         ret = create_nondirectory_inode(&h, first_dentry, ctx);
1570         if (ret)
1571                 return ret;
1572
1573         /* Set short name.  */
1574         ret = set_short_name(h, first_dentry, ctx);
1575
1576         /* Create additional links, OR if hard links are not supported just
1577          * create more files.  */
1578         if (!ret)
1579                 ret = create_links(h, first_dentry, ctx);
1580
1581         /* "WIMBoot" extraction: set external backing by the WIM file if needed.  */
1582         if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
1583                 ret = set_external_backing(h, inode, ctx);
1584
1585         (*func_NtClose)(h);
1586         return ret;
1587 }
1588
1589 /* Create all the nondirectory files being extracted, including all aliases
1590  * (hard links).  */
1591 static int
1592 create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1593 {
1594         struct wim_dentry *dentry;
1595         struct wim_inode *inode;
1596         int ret;
1597
1598         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1599                 inode = dentry->d_inode;
1600                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1601                         continue;
1602                 /* Call create_nondirectory() only once per inode  */
1603                 if (dentry == inode_first_extraction_dentry(inode)) {
1604                         ret = create_nondirectory(inode, ctx);
1605                         ret = check_apply_error(dentry, ctx, ret);
1606                         if (ret)
1607                                 return ret;
1608                 }
1609                 ret = report_file_created(&ctx->common);
1610                 if (ret)
1611                         return ret;
1612         }
1613         return 0;
1614 }
1615
1616 static void
1617 close_handles(struct win32_apply_ctx *ctx)
1618 {
1619         for (unsigned i = 0; i < ctx->num_open_handles; i++)
1620                 (*func_NtClose)(ctx->open_handles[i]);
1621 }
1622
1623 /* Prepare to read the next stream, which has size @stream_size, into an
1624  * in-memory buffer.  */
1625 static bool
1626 prepare_data_buffer(struct win32_apply_ctx *ctx, u64 stream_size)
1627 {
1628         if (stream_size > ctx->data_buffer_size) {
1629                 /* Larger buffer needed.  */
1630                 void *new_buffer;
1631                 if ((size_t)stream_size != stream_size)
1632                         return false;
1633                 new_buffer = REALLOC(ctx->data_buffer, stream_size);
1634                 if (!new_buffer)
1635                         return false;
1636                 ctx->data_buffer = new_buffer;
1637                 ctx->data_buffer_size = stream_size;
1638         }
1639         /* On the first call this changes data_buffer_ptr from NULL, which tells
1640          * extract_chunk() that the data buffer needs to be filled while reading
1641          * the stream data.  */
1642         ctx->data_buffer_ptr = ctx->data_buffer;
1643         return true;
1644 }
1645
1646 static int
1647 begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
1648                               struct wim_dentry *dentry,
1649                               const wchar_t *stream_name,
1650                               struct win32_apply_ctx *ctx)
1651 {
1652         const struct wim_inode *inode = dentry->d_inode;
1653         size_t stream_name_nchars = 0;
1654         FILE_ALLOCATION_INFORMATION alloc_info;
1655         HANDLE h;
1656         NTSTATUS status;
1657
1658         if (unlikely(stream_name))
1659                 stream_name_nchars = wcslen(stream_name);
1660
1661         if (unlikely(stream_name_nchars)) {
1662                 build_extraction_path_with_ads(dentry, ctx,
1663                                                stream_name, stream_name_nchars);
1664         } else {
1665                 build_extraction_path(dentry, ctx);
1666         }
1667
1668
1669         /* Encrypted file?  */
1670         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
1671             && (stream_name_nchars == 0))
1672         {
1673                 if (!ctx->common.supported_features.encrypted_files)
1674                         return 0;
1675
1676                 /* We can't write encrypted file streams directly; we must use
1677                  * WriteEncryptedFileRaw(), which requires providing the data
1678                  * through a callback function.  This can't easily be combined
1679                  * with our own callback-based approach.
1680                  *
1681                  * The current workaround is to simply read the stream into
1682                  * memory and write the encrypted file from that.
1683                  *
1684                  * TODO: This isn't sufficient for extremely large encrypted
1685                  * files.  Perhaps we should create an extra thread to write
1686                  * such files...  */
1687                 if (!prepare_data_buffer(ctx, stream->size))
1688                         return WIMLIB_ERR_NOMEM;
1689                 list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries);
1690                 return 0;
1691         }
1692
1693         /* Reparse point?
1694          *
1695          * Note: FILE_ATTRIBUTE_REPARSE_POINT is tested *after*
1696          * FILE_ATTRIBUTE_ENCRYPTED since the WIM format does not store both EFS
1697          * data and reparse data for the same file, and the EFS data takes
1698          * precedence.  */
1699         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
1700             && (stream_name_nchars == 0))
1701         {
1702                 if (!ctx->common.supported_features.reparse_points)
1703                         return 0;
1704
1705                 /* We can't write the reparse stream directly; we must set it
1706                  * with FSCTL_SET_REPARSE_POINT, which requires that all the
1707                  * data be available.  So, stage the data in a buffer.  */
1708
1709                 if (!prepare_data_buffer(ctx, stream->size))
1710                         return WIMLIB_ERR_NOMEM;
1711                 list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
1712                 return 0;
1713         }
1714
1715         if (ctx->num_open_handles == MAX_OPEN_STREAMS) {
1716                 /* XXX: Fix this.  But because of the checks in
1717                  * extract_stream_list(), this can now only happen on a
1718                  * filesystem that does not support hard links.  */
1719                 ERROR("Can't extract data: too many open files!");
1720                 return WIMLIB_ERR_UNSUPPORTED;
1721         }
1722
1723         /* Open a new handle  */
1724         status = do_create_file(&h,
1725                                 FILE_WRITE_DATA | SYNCHRONIZE,
1726                                 NULL, 0, FILE_OPEN_IF,
1727                                 FILE_SEQUENTIAL_ONLY |
1728                                         FILE_SYNCHRONOUS_IO_NONALERT,
1729                                 ctx);
1730         if (!NT_SUCCESS(status)) {
1731                 winnt_error(status, L"Can't open \"%ls\" for writing",
1732                             current_path(ctx));
1733                 return WIMLIB_ERR_OPEN;
1734         }
1735
1736         ctx->open_handles[ctx->num_open_handles++] = h;
1737
1738         /* Allocate space for the data.  */
1739         alloc_info.AllocationSize.QuadPart = stream->size;
1740         (*func_NtSetInformationFile)(h, &ctx->iosb,
1741                                      &alloc_info, sizeof(alloc_info),
1742                                      FileAllocationInformation);
1743         return 0;
1744 }
1745
1746 /* Set the reparse data @rpbuf of length @rpbuflen on the extracted file
1747  * corresponding to the WIM dentry @dentry.  */
1748 static int
1749 do_set_reparse_data(const struct wim_dentry *dentry,
1750                     const void *rpbuf, u16 rpbuflen,
1751                     struct win32_apply_ctx *ctx)
1752 {
1753         NTSTATUS status;
1754         HANDLE h;
1755
1756         status = create_file(&h, GENERIC_WRITE, NULL,
1757                              0, FILE_OPEN, 0, dentry, ctx);
1758         if (!NT_SUCCESS(status))
1759                 goto fail;
1760
1761         status = (*func_NtFsControlFile)(h, NULL, NULL, NULL,
1762                                          &ctx->iosb, FSCTL_SET_REPARSE_POINT,
1763                                          (void *)rpbuf, rpbuflen,
1764                                          NULL, 0);
1765         (*func_NtClose)(h);
1766
1767         if (NT_SUCCESS(status))
1768                 return 0;
1769
1770         /* On Windows, by default only the Administrator can create symbolic
1771          * links for some reason.  By default we just issue a warning if this
1772          * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
1773          * to get a hard error.  */
1774         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
1775             && (status == STATUS_PRIVILEGE_NOT_HELD ||
1776                 status == STATUS_ACCESS_DENIED)
1777             && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1778                 dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1779         {
1780                 WARNING("Can't create symbolic link \"%ls\"!              \n"
1781                         "          (Need Administrator rights, or at least "
1782                         "the\n"
1783                         "          SeCreateSymbolicLink privilege.)",
1784                         current_path(ctx));
1785                 return 0;
1786         }
1787
1788 fail:
1789         winnt_error(status, L"Can't set reparse data on \"%ls\"",
1790                     current_path(ctx));
1791         return WIMLIB_ERR_SET_REPARSE_DATA;
1792 }
1793
1794 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1795  * pointer to the suffix of the path that begins with the device directly, such
1796  * as e:\Windows\System32.  */
1797 static const wchar_t *
1798 skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
1799 {
1800         static const wchar_t * const dirs[] = {
1801                 L"\\??\\",
1802                 L"\\DosDevices\\",
1803                 L"\\Device\\",
1804         };
1805         size_t first_dir_len = 0;
1806         const wchar_t * const end = path + path_nchars;
1807
1808         for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
1809                 size_t len = wcslen(dirs[i]);
1810                 if (len <= (end - path) && !wcsnicmp(path, dirs[i], len)) {
1811                         first_dir_len = len;
1812                         break;
1813                 }
1814         }
1815         if (first_dir_len == 0)
1816                 return path;
1817         path += first_dir_len;
1818         while (path != end && *path == L'\\')
1819                 path++;
1820         return path;
1821 }
1822
1823 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1824  * pointer to the suffix of the path that is device-relative, such as
1825  * Windows\System32.
1826  *
1827  * The path has an explicit length and is not necessarily null terminated.
1828  *
1829  * If the path just something like \??\e: then the returned pointer will point
1830  * just past the colon.  In this case the length of the result will be 0
1831  * characters.  */
1832 static const wchar_t *
1833 get_device_relative_path(const wchar_t *path, size_t path_nchars)
1834 {
1835         const wchar_t * const orig_path = path;
1836         const wchar_t * const end = path + path_nchars;
1837
1838         path = skip_nt_toplevel_component(path, path_nchars);
1839         if (path == orig_path)
1840                 return orig_path;
1841
1842         path = wmemchr(path, L'\\', (end - path));
1843         if (!path)
1844                 return end;
1845         do {
1846                 path++;
1847         } while (path != end && *path == L'\\');
1848         return path;
1849 }
1850
1851 /*
1852  * Given a reparse point buffer for a symbolic link or junction, adjust its
1853  * contents so that the target of the link is consistent with the new location
1854  * of the files.
1855  */
1856 static void
1857 try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, struct win32_apply_ctx *ctx)
1858 {
1859         struct reparse_data rpdata;
1860         size_t orig_subst_name_nchars;
1861         const wchar_t *relpath;
1862         size_t relpath_nchars;
1863         size_t target_ntpath_nchars;
1864         size_t fixed_subst_name_nchars;
1865         const wchar_t *fixed_print_name;
1866         size_t fixed_print_name_nchars;
1867
1868         if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
1869                 /* Do nothing if the reparse data is invalid.  */
1870                 return;
1871         }
1872
1873         if (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
1874             (rpdata.rpflags & SYMBOLIC_LINK_RELATIVE))
1875         {
1876                 /* Do nothing if it's a relative symbolic link.  */
1877                 return;
1878         }
1879
1880         /* Build the new substitute name from the NT namespace path to the
1881          * target directory, then a path separator, then the "device relative"
1882          * part of the old substitute name.  */
1883
1884         orig_subst_name_nchars = rpdata.substitute_name_nbytes / sizeof(wchar_t);
1885
1886         relpath = get_device_relative_path(rpdata.substitute_name,
1887                                            orig_subst_name_nchars);
1888         relpath_nchars = orig_subst_name_nchars -
1889                          (relpath - rpdata.substitute_name);
1890
1891         target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
1892
1893         fixed_subst_name_nchars = target_ntpath_nchars;
1894         if (relpath_nchars)
1895                 fixed_subst_name_nchars += 1 + relpath_nchars;
1896         wchar_t fixed_subst_name[fixed_subst_name_nchars];
1897
1898         wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer,
1899                 target_ntpath_nchars);
1900         if (relpath_nchars) {
1901                 fixed_subst_name[target_ntpath_nchars] = L'\\';
1902                 wmemcpy(&fixed_subst_name[target_ntpath_nchars + 1],
1903                         relpath, relpath_nchars);
1904         }
1905         /* Doesn't need to be null-terminated.  */
1906
1907         /* Print name should be Win32, but not all NT names can even be
1908          * translated to Win32 names.  But we can at least delete the top-level
1909          * directory, such as \??\, and this will have the expected result in
1910          * the usual case.  */
1911         fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
1912                                                       fixed_subst_name_nchars);
1913         fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
1914                                                              fixed_subst_name);
1915
1916         rpdata.substitute_name = fixed_subst_name;
1917         rpdata.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
1918         rpdata.print_name = (wchar_t *)fixed_print_name;
1919         rpdata.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
1920         make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
1921 }
1922
1923 /* Sets reparse data on the specified file.  This handles "fixing" the targets
1924  * of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX was
1925  * specified.  */
1926 static int
1927 set_reparse_data(const struct wim_dentry *dentry,
1928                  const void *_rpbuf, u16 rpbuflen, struct win32_apply_ctx *ctx)
1929 {
1930         const struct wim_inode *inode = dentry->d_inode;
1931         const void *rpbuf = _rpbuf;
1932
1933         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
1934             && !inode->i_not_rpfixed
1935             && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1936                 inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1937         {
1938                 memcpy(&ctx->rpfixbuf, _rpbuf, rpbuflen);
1939                 try_rpfix((u8 *)&ctx->rpfixbuf, &rpbuflen, ctx);
1940                 rpbuf = &ctx->rpfixbuf;
1941         }
1942         return do_set_reparse_data(dentry, rpbuf, rpbuflen, ctx);
1943
1944 }
1945
1946 /* Import the next block of raw encrypted data  */
1947 static DWORD WINAPI
1948 import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
1949 {
1950         struct win32_apply_ctx *ctx = pvCallbackContext;
1951         ULONG copy_len;
1952
1953         copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
1954         memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
1955         ctx->encrypted_offset += copy_len;
1956         *Length = copy_len;
1957         return ERROR_SUCCESS;
1958 }
1959
1960 /*
1961  * Write the raw encrypted data to the already-created file (or directory)
1962  * corresponding to @dentry.
1963  *
1964  * The raw encrypted data is provided in ctx->data_buffer, and its size is
1965  * ctx->encrypted_size.
1966  *
1967  * This function may close the target directory, in which case the caller needs
1968  * to re-open it if needed.
1969  */
1970 static int
1971 extract_encrypted_file(const struct wim_dentry *dentry,
1972                        struct win32_apply_ctx *ctx)
1973 {
1974         void *rawctx;
1975         DWORD err;
1976         ULONG flags;
1977         bool retried;
1978
1979         /* Temporarily build a Win32 path for OpenEncryptedFileRaw()  */
1980         build_win32_extraction_path(dentry, ctx);
1981
1982         flags = CREATE_FOR_IMPORT | OVERWRITE_HIDDEN;
1983         if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1984                 flags |= CREATE_FOR_DIR;
1985
1986         retried = false;
1987 retry:
1988         err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, flags, &rawctx);
1989         if (err == ERROR_SHARING_VIOLATION && !retried) {
1990                 /* This can be caused by the handle we have open to the target
1991                  * directory.  Try closing it temporarily.  */
1992                 close_target_directory(ctx);
1993                 retried = true;
1994                 goto retry;
1995         }
1996
1997         /* Restore the NT namespace path  */
1998         build_extraction_path(dentry, ctx);
1999
2000         if (err != ERROR_SUCCESS) {
2001                 win32_error(err, L"Can't open \"%ls\" for encrypted import",
2002                             current_path(ctx));
2003                 return WIMLIB_ERR_OPEN;
2004         }
2005
2006         ctx->encrypted_offset = 0;
2007
2008         err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx);
2009
2010         CloseEncryptedFileRaw(rawctx);
2011
2012         if (err != ERROR_SUCCESS) {
2013                 win32_error(err, L"Can't import encrypted file \"%ls\"",
2014                             current_path(ctx));
2015                 return WIMLIB_ERR_WRITE;
2016         }
2017
2018         return 0;
2019 }
2020
2021 /* Called when starting to read a stream for extraction on Windows  */
2022 static int
2023 begin_extract_stream(struct wim_lookup_table_entry *stream, void *_ctx)
2024 {
2025         struct win32_apply_ctx *ctx = _ctx;
2026         const struct stream_owner *owners = stream_owners(stream);
2027         int ret;
2028
2029         ctx->num_open_handles = 0;
2030         ctx->data_buffer_ptr = NULL;
2031         INIT_LIST_HEAD(&ctx->reparse_dentries);
2032         INIT_LIST_HEAD(&ctx->encrypted_dentries);
2033
2034         for (u32 i = 0; i < stream->out_refcnt; i++) {
2035                 const struct wim_inode *inode = owners[i].inode;
2036                 const wchar_t *stream_name = owners[i].stream_name;
2037                 struct wim_dentry *dentry;
2038
2039                 /* A copy of the stream needs to be extracted to @inode.  */
2040
2041                 if (ctx->common.supported_features.hard_links) {
2042                         dentry = inode_first_extraction_dentry(inode);
2043                         ret = begin_extract_stream_instance(stream, dentry,
2044                                                             stream_name, ctx);
2045                         ret = check_apply_error(dentry, ctx, ret);
2046                         if (ret)
2047                                 goto fail;
2048                 } else {
2049                         /* Hard links not supported.  Extract the stream
2050                          * separately to each alias of the inode.  */
2051                         struct list_head *next;
2052
2053                         next = inode->i_extraction_aliases.next;
2054                         do {
2055                                 dentry = list_entry(next, struct wim_dentry,
2056                                                     d_extraction_alias_node);
2057                                 ret = begin_extract_stream_instance(stream,
2058                                                                     dentry,
2059                                                                     stream_name,
2060                                                                     ctx);
2061                                 ret = check_apply_error(dentry, ctx, ret);
2062                                 if (ret)
2063                                         goto fail;
2064                                 next = next->next;
2065                         } while (next != &inode->i_extraction_aliases);
2066                 }
2067         }
2068
2069         return 0;
2070
2071 fail:
2072         close_handles(ctx);
2073         return ret;
2074 }
2075
2076 /* Called when the next chunk of a stream has been read for extraction on
2077  * Windows  */
2078 static int
2079 extract_chunk(const void *chunk, size_t size, void *_ctx)
2080 {
2081         struct win32_apply_ctx *ctx = _ctx;
2082
2083         /* Write the data chunk to each open handle  */
2084         for (unsigned i = 0; i < ctx->num_open_handles; i++) {
2085                 u8 *bufptr = (u8 *)chunk;
2086                 size_t bytes_remaining = size;
2087                 NTSTATUS status;
2088                 while (bytes_remaining) {
2089                         ULONG count = min(0xFFFFFFFF, bytes_remaining);
2090
2091                         status = (*func_NtWriteFile)(ctx->open_handles[i],
2092                                                      NULL, NULL, NULL,
2093                                                      &ctx->iosb, bufptr, count,
2094                                                      NULL, NULL);
2095                         if (!NT_SUCCESS(status)) {
2096                                 winnt_error(status, L"Error writing data to target volume");
2097                                 return WIMLIB_ERR_WRITE;
2098                         }
2099                         bufptr += ctx->iosb.Information;
2100                         bytes_remaining -= ctx->iosb.Information;
2101                 }
2102         }
2103
2104         /* Copy the data chunk into the buffer (if needed)  */
2105         if (ctx->data_buffer_ptr)
2106                 ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
2107                                                chunk, size);
2108         return 0;
2109 }
2110
2111 /* Called when a stream has been fully read for extraction on Windows  */
2112 static int
2113 end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx)
2114 {
2115         struct win32_apply_ctx *ctx = _ctx;
2116         int ret;
2117         const struct wim_dentry *dentry;
2118
2119         close_handles(ctx);
2120
2121         if (status)
2122                 return status;
2123
2124         if (likely(!ctx->data_buffer_ptr))
2125                 return 0;
2126
2127         if (!list_empty(&ctx->reparse_dentries)) {
2128                 if (stream->size > REPARSE_DATA_MAX_SIZE) {
2129                         dentry = list_first_entry(&ctx->reparse_dentries,
2130                                                   struct wim_dentry, tmp_list);
2131                         build_extraction_path(dentry, ctx);
2132                         ERROR("Reparse data of \"%ls\" has size "
2133                               "%"PRIu64" bytes (exceeds %u bytes)",
2134                               current_path(ctx), stream->size,
2135                               REPARSE_DATA_MAX_SIZE);
2136                         ret = WIMLIB_ERR_INVALID_REPARSE_DATA;
2137                         return check_apply_error(dentry, ctx, ret);
2138                 }
2139                 /* In the WIM format, reparse streams are just the reparse data
2140                  * and omit the header.  But we can reconstruct the header.  */
2141                 memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, stream->size);
2142                 ctx->rpbuf.rpdatalen = stream->size;
2143                 ctx->rpbuf.rpreserved = 0;
2144                 list_for_each_entry(dentry, &ctx->reparse_dentries, tmp_list) {
2145                         ctx->rpbuf.rptag = dentry->d_inode->i_reparse_tag;
2146                         ret = set_reparse_data(dentry, &ctx->rpbuf,
2147                                                stream->size + REPARSE_DATA_OFFSET,
2148                                                ctx);
2149                         ret = check_apply_error(dentry, ctx, ret);
2150                         if (ret)
2151                                 return ret;
2152                 }
2153         }
2154
2155         if (!list_empty(&ctx->encrypted_dentries)) {
2156                 ctx->encrypted_size = stream->size;
2157                 list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) {
2158                         ret = extract_encrypted_file(dentry, ctx);
2159                         ret = check_apply_error(dentry, ctx, ret);
2160                         if (ret)
2161                                 return ret;
2162                         /* Re-open the target directory if needed.  */
2163                         ret = open_target_directory(ctx);
2164                         if (ret)
2165                                 return ret;
2166                 }
2167         }
2168
2169         return 0;
2170 }
2171
2172 /* Attributes that can't be set directly  */
2173 #define SPECIAL_ATTRIBUTES                      \
2174         (FILE_ATTRIBUTE_REPARSE_POINT   |       \
2175          FILE_ATTRIBUTE_DIRECTORY       |       \
2176          FILE_ATTRIBUTE_ENCRYPTED       |       \
2177          FILE_ATTRIBUTE_SPARSE_FILE     |       \
2178          FILE_ATTRIBUTE_COMPRESSED)
2179
2180 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
2181  * handle @h.  */
2182 static NTSTATUS
2183 set_security_descriptor(HANDLE h, const void *_desc,
2184                         size_t desc_size, struct win32_apply_ctx *ctx)
2185 {
2186         SECURITY_INFORMATION info;
2187         NTSTATUS status;
2188         SECURITY_DESCRIPTOR_RELATIVE *desc;
2189
2190         /*
2191          * Ideally, we would just pass in the security descriptor buffer as-is.
2192          * But it turns out that Windows can mess up the security descriptor
2193          * even when using the low-level NtSetSecurityObject() function:
2194          *
2195          * - Windows will clear SE_DACL_AUTO_INHERITED if it is set in the
2196          *   passed buffer.  To actually get Windows to set
2197          *   SE_DACL_AUTO_INHERITED, the application must set the non-persistent
2198          *   flag SE_DACL_AUTO_INHERIT_REQ.  As usual, Microsoft didn't bother
2199          *   to properly document either of these flags.  It's unclear how
2200          *   important SE_DACL_AUTO_INHERITED actually is, but to be safe we use
2201          *   the SE_DACL_AUTO_INHERIT_REQ workaround to set it if needed.
2202          *
2203          * - The above also applies to the equivalent SACL flags,
2204          *   SE_SACL_AUTO_INHERITED and SE_SACL_AUTO_INHERIT_REQ.
2205          *
2206          * - If the application says that it's setting
2207          *   DACL_SECURITY_INFORMATION, then Windows sets SE_DACL_PRESENT in the
2208          *   resulting security descriptor, even if the security descriptor the
2209          *   application provided did not have a DACL.  This seems to be
2210          *   unavoidable, since omitting DACL_SECURITY_INFORMATION would cause a
2211          *   default DACL to remain.  Fortunately, this behavior seems harmless,
2212          *   since the resulting DACL will still be "null" --- but it will be
2213          *   "the other representation of null".
2214          *
2215          * - The above also applies to SACL_SECURITY_INFORMATION and
2216          *   SE_SACL_PRESENT.  Again, it's seemingly unavoidable but "harmless"
2217          *   that Windows changes the representation of a "null SACL".
2218          */
2219         if (likely(desc_size <= STACK_MAX)) {
2220                 desc = alloca(desc_size);
2221         } else {
2222                 desc = MALLOC(desc_size);
2223                 if (!desc)
2224                         return STATUS_NO_MEMORY;
2225         }
2226
2227         memcpy(desc, _desc, desc_size);
2228
2229         if (likely(desc_size >= 4)) {
2230
2231                 if (desc->Control & SE_DACL_AUTO_INHERITED)
2232                         desc->Control |= SE_DACL_AUTO_INHERIT_REQ;
2233
2234                 if (desc->Control & SE_SACL_AUTO_INHERITED)
2235                         desc->Control |= SE_SACL_AUTO_INHERIT_REQ;
2236         }
2237
2238         /*
2239          * More API insanity.  We want to set the entire security descriptor
2240          * as-is.  But all available APIs require specifying the specific parts
2241          * of the security descriptor being set.  Especially annoying is that
2242          * mandatory integrity labels are part of the SACL, but they aren't set
2243          * with SACL_SECURITY_INFORMATION.  Instead, applications must also
2244          * specify LABEL_SECURITY_INFORMATION (Windows Vista, Windows 7) or
2245          * BACKUP_SECURITY_INFORMATION (Windows 8).  But at least older versions
2246          * of Windows don't error out if you provide these newer flags...
2247          *
2248          * Also, if the process isn't running as Administrator, then it probably
2249          * doesn't have SE_RESTORE_PRIVILEGE.  In this case, it will always get
2250          * the STATUS_PRIVILEGE_NOT_HELD error by trying to set the SACL, even
2251          * if the security descriptor it provided did not have a SACL.  By
2252          * default, in this case we try to recover and set as much of the
2253          * security descriptor as possible --- potentially excluding the DACL, and
2254          * even the owner, as well as the SACL.
2255          */
2256
2257         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
2258                DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION |
2259                LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION;
2260
2261
2262         /*
2263          * It's also worth noting that SetFileSecurity() is unusable because it
2264          * doesn't request "backup semantics" when it opens the file internally.
2265          * NtSetSecurityObject() seems to be the best function to use in backup
2266          * applications.  (SetSecurityInfo() should also work, but it's harder
2267          * to use and must call NtSetSecurityObject() internally anyway.
2268          * BackupWrite() is theoretically usable as well, but it's inflexible
2269          * and poorly documented.)
2270          */
2271
2272 retry:
2273         status = (*func_NtSetSecurityObject)(h, info, desc);
2274         if (NT_SUCCESS(status))
2275                 goto out_maybe_free_desc;
2276
2277         /* Failed to set the requested parts of the security descriptor.  If the
2278          * error was permissions-related, try to set fewer parts of the security
2279          * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
2280         if ((status == STATUS_PRIVILEGE_NOT_HELD ||
2281              status == STATUS_ACCESS_DENIED) &&
2282             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
2283         {
2284                 if (info & SACL_SECURITY_INFORMATION) {
2285                         info &= ~(SACL_SECURITY_INFORMATION |
2286                                   LABEL_SECURITY_INFORMATION |
2287                                   BACKUP_SECURITY_INFORMATION);
2288                         ctx->partial_security_descriptors++;
2289                         goto retry;
2290                 }
2291                 if (info & DACL_SECURITY_INFORMATION) {
2292                         info &= ~DACL_SECURITY_INFORMATION;
2293                         goto retry;
2294                 }
2295                 if (info & OWNER_SECURITY_INFORMATION) {
2296                         info &= ~OWNER_SECURITY_INFORMATION;
2297                         goto retry;
2298                 }
2299                 /* Nothing left except GROUP, and if we removed it we
2300                  * wouldn't have anything at all.  */
2301         }
2302
2303         /* No part of the security descriptor could be set, or
2304          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security
2305          * descriptor could not be set.  */
2306         if (!(info & SACL_SECURITY_INFORMATION))
2307                 ctx->partial_security_descriptors--;
2308         ctx->no_security_descriptors++;
2309
2310 out_maybe_free_desc:
2311         if (unlikely(desc_size > STACK_MAX))
2312                 FREE(desc);
2313         return status;
2314 }
2315
2316 /* Set metadata on the open file @h from the WIM inode @inode.  */
2317 static int
2318 do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
2319                           struct win32_apply_ctx *ctx)
2320 {
2321         FILE_BASIC_INFORMATION info;
2322         NTSTATUS status;
2323
2324         /* Set security descriptor if present and not in NO_ACLS mode  */
2325         if (inode->i_security_id >= 0 &&
2326             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
2327         {
2328                 const struct wim_security_data *sd;
2329                 const void *desc;
2330                 size_t desc_size;
2331
2332                 sd = wim_get_current_security_data(ctx->common.wim);
2333                 desc = sd->descriptors[inode->i_security_id];
2334                 desc_size = sd->sizes[inode->i_security_id];
2335
2336                 status = set_security_descriptor(h, desc, desc_size, ctx);
2337                 if (!NT_SUCCESS(status) &&
2338                     (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
2339                 {
2340                         winnt_error(status,
2341                                     L"Can't set security descriptor on \"%ls\"",
2342                                     current_path(ctx));
2343                         return WIMLIB_ERR_SET_SECURITY;
2344                 }
2345         }
2346
2347         /* Set attributes and timestamps  */
2348         info.CreationTime.QuadPart = inode->i_creation_time;
2349         info.LastAccessTime.QuadPart = inode->i_last_access_time;
2350         info.LastWriteTime.QuadPart = inode->i_last_write_time;
2351         info.ChangeTime.QuadPart = 0;
2352         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) {
2353                 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2354         } else {
2355                 info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES;
2356                 if (info.FileAttributes == 0)
2357                         info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2358         }
2359
2360         status = (*func_NtSetInformationFile)(h, &ctx->iosb,
2361                                               &info, sizeof(info),
2362                                               FileBasicInformation);
2363         /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set
2364          * attributes on the root directory.  (Apparently because FAT doesn't
2365          * actually have a place to store those attributes!)  */
2366         if (!NT_SUCCESS(status)
2367             && !(status == STATUS_INVALID_PARAMETER &&
2368                  dentry_is_root(inode_first_extraction_dentry(inode))))
2369         {
2370                 winnt_error(status, L"Can't set basic metadata on \"%ls\"",
2371                             current_path(ctx));
2372                 return WIMLIB_ERR_SET_ATTRIBUTES;
2373         }
2374
2375         return 0;
2376 }
2377
2378 static int
2379 apply_metadata_to_file(const struct wim_dentry *dentry,
2380                        struct win32_apply_ctx *ctx)
2381 {
2382         const struct wim_inode *inode = dentry->d_inode;
2383         DWORD perms;
2384         HANDLE h;
2385         NTSTATUS status;
2386         int ret;
2387
2388         perms = FILE_WRITE_ATTRIBUTES | WRITE_DAC |
2389                 WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
2390
2391         build_extraction_path(dentry, ctx);
2392
2393         /* Open a handle with as many relevant permissions as possible.  */
2394         while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL,
2395                                                    0, FILE_OPEN, 0, ctx)))
2396         {
2397                 if (status == STATUS_PRIVILEGE_NOT_HELD ||
2398                     status == STATUS_ACCESS_DENIED)
2399                 {
2400                         if (perms & ACCESS_SYSTEM_SECURITY) {
2401                                 perms &= ~ACCESS_SYSTEM_SECURITY;
2402                                 continue;
2403                         }
2404                         if (perms & WRITE_DAC) {
2405                                 perms &= ~WRITE_DAC;
2406                                 continue;
2407                         }
2408                         if (perms & WRITE_OWNER) {
2409                                 perms &= ~WRITE_OWNER;
2410                                 continue;
2411                         }
2412                 }
2413                 winnt_error(status, L"Can't open \"%ls\" to set metadata",
2414                             current_path(ctx));
2415                 return WIMLIB_ERR_OPEN;
2416         }
2417
2418         ret = do_apply_metadata_to_file(h, inode, ctx);
2419
2420         (*func_NtClose)(h);
2421
2422         return ret;
2423 }
2424
2425 static int
2426 apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
2427 {
2428         const struct wim_dentry *dentry;
2429         int ret;
2430
2431         /* We go in reverse so that metadata is set on all a directory's
2432          * children before the directory itself.  This avoids any potential
2433          * problems with attributes, timestamps, or security descriptors.  */
2434         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node)
2435         {
2436                 ret = apply_metadata_to_file(dentry, ctx);
2437                 ret = check_apply_error(dentry, ctx, ret);
2438                 if (ret)
2439                         return ret;
2440                 ret = report_file_metadata_applied(&ctx->common);
2441                 if (ret)
2442                         return ret;
2443         }
2444         return 0;
2445 }
2446
2447 /* Issue warnings about problems during the extraction for which warnings were
2448  * not already issued (due to the high number of potential warnings if we issued
2449  * them per-file).  */
2450 static void
2451 do_warnings(const struct win32_apply_ctx *ctx)
2452 {
2453         if (ctx->partial_security_descriptors == 0
2454             && ctx->no_security_descriptors == 0
2455             && ctx->num_set_short_name_failures == 0
2456         #if 0
2457             && ctx->num_remove_short_name_failures == 0
2458         #endif
2459             )
2460                 return;
2461
2462         WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
2463                 ctx->common.target);
2464         if (ctx->num_set_short_name_failures) {
2465                 WARNING("- Could not set short names on %lu files or directories",
2466                         ctx->num_set_short_name_failures);
2467         }
2468 #if 0
2469         if (ctx->num_remove_short_name_failures) {
2470                 WARNING("- Could not remove short names on %lu files or directories"
2471                         "          (This is expected on Vista and earlier)",
2472                         ctx->num_remove_short_name_failures);
2473         }
2474 #endif
2475         if (ctx->partial_security_descriptors) {
2476                 WARNING("- Could only partially set the security descriptor\n"
2477                         "            on %lu files or directories.",
2478                         ctx->partial_security_descriptors);
2479         }
2480         if (ctx->no_security_descriptors) {
2481                 WARNING("- Could not set security descriptor at all\n"
2482                         "            on %lu files or directories.",
2483                         ctx->no_security_descriptors);
2484         }
2485         if (ctx->partial_security_descriptors || ctx->no_security_descriptors) {
2486                 WARNING("To fully restore all security descriptors, run the program\n"
2487                         "          with Administrator rights.");
2488         }
2489 }
2490
2491 static uint64_t
2492 count_dentries(const struct list_head *dentry_list)
2493 {
2494         const struct list_head *cur;
2495         uint64_t count = 0;
2496
2497         list_for_each(cur, dentry_list)
2498                 count++;
2499
2500         return count;
2501 }
2502
2503 /* Extract files from a WIM image to a directory on Windows  */
2504 static int
2505 win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
2506 {
2507         int ret;
2508         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
2509         uint64_t dentry_count;
2510
2511         ret = prepare_target(dentry_list, ctx);
2512         if (ret)
2513                 goto out;
2514
2515         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
2516                 ret = start_wimboot_extraction(ctx);
2517                 if (ret)
2518                         goto out;
2519         }
2520
2521         dentry_count = count_dentries(dentry_list);
2522
2523         ret = start_file_structure_phase(&ctx->common, dentry_count);
2524         if (ret)
2525                 goto out;
2526
2527         ret = create_directories(dentry_list, ctx);
2528         if (ret)
2529                 goto out;
2530
2531         ret = create_nondirectories(dentry_list, ctx);
2532         if (ret)
2533                 goto out;
2534
2535         ret = end_file_structure_phase(&ctx->common);
2536         if (ret)
2537                 goto out;
2538
2539         struct read_stream_list_callbacks cbs = {
2540                 .begin_stream      = begin_extract_stream,
2541                 .begin_stream_ctx  = ctx,
2542                 .consume_chunk     = extract_chunk,
2543                 .consume_chunk_ctx = ctx,
2544                 .end_stream        = end_extract_stream,
2545                 .end_stream_ctx    = ctx,
2546         };
2547         ret = extract_stream_list(&ctx->common, &cbs);
2548         if (ret)
2549                 goto out;
2550
2551         ret = start_file_metadata_phase(&ctx->common, dentry_count);
2552         if (ret)
2553                 goto out;
2554
2555         ret = apply_metadata(dentry_list, ctx);
2556         if (ret)
2557                 goto out;
2558
2559         ret = end_file_metadata_phase(&ctx->common);
2560         if (ret)
2561                 goto out;
2562
2563         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
2564                 ret = end_wimboot_extraction(ctx);
2565                 if (ret)
2566                         goto out;
2567         }
2568
2569         do_warnings(ctx);
2570 out:
2571         close_target_directory(ctx);
2572         if (ctx->target_ntpath.Buffer)
2573                 HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
2574         FREE(ctx->pathbuf.Buffer);
2575         FREE(ctx->print_buffer);
2576         if (ctx->wimboot.prepopulate_pats) {
2577                 FREE(ctx->wimboot.prepopulate_pats->strings);
2578                 FREE(ctx->wimboot.prepopulate_pats);
2579         }
2580         FREE(ctx->wimboot.mem_prepopulate_pats);
2581         FREE(ctx->data_buffer);
2582         return ret;
2583 }
2584
2585 const struct apply_operations win32_apply_ops = {
2586         .name                   = "Windows",
2587         .get_supported_features = win32_get_supported_features,
2588         .extract                = win32_extract,
2589         .will_externally_back   = win32_will_externally_back,
2590         .context_size           = sizeof(struct win32_apply_ctx),
2591 };
2592
2593 #endif /* __WIN32__ */