]> wimlib.net Git - wimlib/blob - src/win32_apply.c
win32_apply.c: Fallback to RtlDosPathNameToNtPathName_U()
[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 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
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/capture.h" /* for mangle_pat() and match_pattern_list()  */
32 #include "wimlib/dentry.h"
33 #include "wimlib/error.h"
34 #include "wimlib/lookup_table.h"
35 #include "wimlib/metadata.h"
36 #include "wimlib/reparse.h"
37 #include "wimlib/textfile.h"
38 #include "wimlib/xml.h"
39 #include "wimlib/wimboot.h"
40
41 /* TODO: Add workaround for when a stream needs to be extracted to more places
42  * than this  */
43 #define MAX_OPEN_HANDLES 32768
44
45 struct win32_apply_ctx {
46
47         /* Extract flags, the pointer to the WIMStruct, etc.  */
48         struct apply_ctx common;
49
50         /* WIMBoot information, only filled in if WIMLIB_EXTRACT_FLAG_WIMBOOT
51          * was provided  */
52         struct {
53                 u64 data_source_id;
54                 struct string_set *prepopulate_pats;
55                 void *mem_prepopulate_pats;
56                 u8 wim_lookup_table_hash[SHA1_HASH_SIZE];
57                 bool wof_running;
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 junction  */
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_HANDLES];
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
131 /* Get the drive letter from a Windows path, or return the null character if the
132  * path is relative.  */
133 static wchar_t
134 get_drive_letter(const wchar_t *path)
135 {
136         /* Skip \\?\ prefix  */
137         if (!wcsncmp(path, L"\\\\?\\", 4))
138                 path += 4;
139
140         /* Return drive letter if valid  */
141         if (((path[0] >= L'a' && path[0] <= L'z') ||
142              (path[0] >= L'A' && path[0] <= L'Z')) && path[1] == L':')
143                 return path[0];
144
145         return L'\0';
146 }
147
148 static void
149 get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
150               bool *short_names_supported_ret)
151 {
152         wchar_t filesystem_name[MAX_PATH + 1];
153         wchar_t drive[4];
154         wchar_t *volume = NULL;
155
156         *vol_flags_ret = 0;
157         *short_names_supported_ret = false;
158
159         drive[0] = get_drive_letter(target);
160         if (drive[0]) {
161                 drive[1] = L':';
162                 drive[2] = L'\\';
163                 drive[3] = L'\0';
164                 volume = drive;
165         }
166
167         if (!GetVolumeInformation(volume, NULL, 0, NULL, NULL,
168                                   vol_flags_ret, filesystem_name,
169                                   ARRAY_LEN(filesystem_name)))
170         {
171                 DWORD err = GetLastError();
172                 set_errno_from_win32_error(err);
173                 WARNING_WITH_ERRNO("Failed to get volume information for "
174                                    "\"%ls\" (err=%"PRIu32")",
175                                    target, (u32)err);
176                 return;
177         }
178
179         if (wcsstr(filesystem_name, L"NTFS")) {
180                 /* FILE_SUPPORTS_HARD_LINKS is only supported on Windows 7 and
181                  * later.  Force it on anyway if filesystem is NTFS.  */
182                 *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS;
183
184                 /* There's no volume flag for short names, but according to the
185                  * MS documentation they are only user-settable on NTFS.  */
186                 *short_names_supported_ret = true;
187         }
188 }
189
190 static int
191 win32_get_supported_features(const wchar_t *target,
192                              struct wim_features *supported_features)
193 {
194         DWORD vol_flags;
195         bool short_names_supported;
196
197         /* Query the features of the target volume.  */
198
199         get_vol_flags(target, &vol_flags, &short_names_supported);
200
201         supported_features->archive_files = 1;
202         supported_features->hidden_files = 1;
203         supported_features->system_files = 1;
204
205         if (vol_flags & FILE_FILE_COMPRESSION)
206                 supported_features->compressed_files = 1;
207
208         if (vol_flags & FILE_SUPPORTS_ENCRYPTION) {
209                 supported_features->encrypted_files = 1;
210                 supported_features->encrypted_directories = 1;
211         }
212
213         supported_features->not_context_indexed_files = 1;
214
215         /* Don't do anything with FILE_SUPPORTS_SPARSE_FILES.  */
216
217         if (vol_flags & FILE_NAMED_STREAMS)
218                 supported_features->named_data_streams = 1;
219
220         if (vol_flags & FILE_SUPPORTS_HARD_LINKS)
221                 supported_features->hard_links = 1;
222
223         if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS)
224                 supported_features->reparse_points = 1;
225
226         if (vol_flags & FILE_PERSISTENT_ACLS)
227                 supported_features->security_descriptors = 1;
228
229         if (short_names_supported)
230                 supported_features->short_names = 1;
231
232         supported_features->timestamps = 1;
233
234         /* Note: Windows does not support case sensitive filenames!  At least
235          * not without changing the registry and rebooting...  */
236
237         return 0;
238 }
239
240 /* Load the patterns from [PrepopulateList] of WimBootCompresse.ini in the WIM
241  * image being extracted.  */
242 static int
243 load_prepopulate_pats(struct win32_apply_ctx *ctx)
244 {
245         const wchar_t *path = L"\\Windows\\System32\\WimBootCompress.ini";
246         struct wim_dentry *dentry;
247         struct wim_lookup_table_entry *lte;
248         int ret;
249         void *buf;
250         struct string_set *s;
251         void *mem;
252         struct text_file_section sec;
253
254         dentry = get_dentry(ctx->common.wim, path, WIMLIB_CASE_INSENSITIVE);
255         if (!dentry ||
256             (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
257                                               FILE_ATTRIBUTE_REPARSE_POINT |
258                                               FILE_ATTRIBUTE_ENCRYPTED)) ||
259             !(lte = inode_unnamed_lte(dentry->d_inode, ctx->common.wim->lookup_table)))
260         {
261                 WARNING("%ls does not exist in WIM image!", path);
262                 return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
263         }
264
265         ret = read_full_stream_into_alloc_buf(lte, &buf);
266         if (ret)
267                 return ret;
268
269         s = CALLOC(1, sizeof(struct string_set));
270         if (!s) {
271                 FREE(buf);
272                 return WIMLIB_ERR_NOMEM;
273         }
274
275         sec.name = T("PrepopulateList");
276         sec.strings = s;
277
278         ret = do_load_text_file(path, buf, lte->size, &mem, &sec, 1,
279                                 LOAD_TEXT_FILE_REMOVE_QUOTES |
280                                         LOAD_TEXT_FILE_NO_WARNINGS,
281                                 mangle_pat);
282         BUILD_BUG_ON(OS_PREFERRED_PATH_SEPARATOR != WIM_PATH_SEPARATOR);
283         FREE(buf);
284         if (ret) {
285                 FREE(s);
286                 return ret;
287         }
288         ctx->wimboot.prepopulate_pats = s;
289         ctx->wimboot.mem_prepopulate_pats = mem;
290         return 0;
291 }
292
293 /* Returns %true if the path to @dentry matches a pattern in [PrepopulateList]
294  * of WimBootCompress.ini.  Otherwise returns %false.
295  *
296  * @dentry must have had its full path calculated.  */
297 static bool
298 in_prepopulate_list(struct wim_dentry *dentry,
299                     const struct win32_apply_ctx *ctx)
300 {
301         const struct string_set *pats = ctx->wimboot.prepopulate_pats;
302
303         if (!pats || !pats->num_strings)
304                 return false;
305
306         return match_pattern_list(dentry->_full_path,
307                                   wcslen(dentry->_full_path), pats);
308 }
309
310 /* Calculates the SHA-1 message digest of the WIM's lookup table.  */
311 static int
312 hash_lookup_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE])
313 {
314         return wim_reshdr_to_hash(&wim->hdr.lookup_table_reshdr, wim, hash);
315 }
316
317 /* Prepare for doing a "WIMBoot" extraction by loading patterns from
318  * [PrepopulateList] of WimBootCompress.ini and allocating a WOF data source ID
319  * on the target volume.  */
320 static int
321 start_wimboot_extraction(struct win32_apply_ctx *ctx)
322 {
323         int ret;
324         WIMStruct *wim = ctx->common.wim;
325
326         ret = load_prepopulate_pats(ctx);
327         if (ret == WIMLIB_ERR_NOMEM)
328                 return ret;
329
330         if (!wim_info_get_wimboot(wim->wim_info,
331                                   wim->current_image))
332                 WARNING("Image is not marked as WIMBoot compatible!");
333
334         ret = hash_lookup_table(ctx->common.wim,
335                                 ctx->wimboot.wim_lookup_table_hash);
336         if (ret)
337                 return ret;
338
339         return wimboot_alloc_data_source_id(wim->filename,
340                                             wim->hdr.guid,
341                                             wim->current_image,
342                                             ctx->common.target,
343                                             &ctx->wimboot.data_source_id,
344                                             &ctx->wimboot.wof_running);
345 }
346
347 /* Returns the number of wide characters needed to represent the path to the
348  * specified @dentry, relative to the target directory, when extracted.
349  *
350  * Does not include null terminator (not needed for NtCreateFile).  */
351 static size_t
352 dentry_extraction_path_length(const struct wim_dentry *dentry)
353 {
354         size_t len = 0;
355         const struct wim_dentry *d;
356
357         d = dentry;
358         do {
359                 len += d->d_extraction_name_nchars + 1;
360                 d = d->parent;
361         } while (!dentry_is_root(d) && will_extract_dentry(d));
362
363         return --len;  /* No leading slash  */
364 }
365
366 /* Returns the length of the longest string that might need to be appended to
367  * the path to an alias of an inode to open or create a named data stream.
368  *
369  * If the inode has no named data streams, this will be 0.  Otherwise, this will
370  * be 1 plus the length of the longest-named data stream, since the data stream
371  * name must be separated form the path by the ':' character.  */
372 static size_t
373 inode_longest_named_data_stream_spec(const struct wim_inode *inode)
374 {
375         size_t max = 0;
376         for (u16 i = 0; i < inode->i_num_ads; i++) {
377                 size_t len = inode->i_ads_entries[i].stream_name_nbytes;
378                 if (len > max)
379                         max = len;
380         }
381         if (max)
382                 max = 1 + (max / sizeof(wchar_t));
383         return max;
384 }
385
386 /* Find the length, in wide characters, of the longest path needed for
387  * extraction of any file in @dentry_list relative to the target directory.
388  *
389  * Accounts for named data streams, but does not include null terminator (not
390  * needed for NtCreateFile).  */
391 static size_t
392 compute_path_max(struct list_head *dentry_list)
393 {
394         size_t max = 0;
395         const struct wim_dentry *dentry;
396
397         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
398                 size_t len;
399
400                 len = dentry_extraction_path_length(dentry);
401
402                 /* Account for named data streams  */
403                 len += inode_longest_named_data_stream_spec(dentry->d_inode);
404
405                 if (len > max)
406                         max = len;
407         }
408
409         return max;
410 }
411
412 /* Build the path at which to extract the @dentry, relative to the target
413  * directory.
414  *
415  * The path is saved in ctx->pathbuf.  */
416 static void
417 build_extraction_path(const struct wim_dentry *dentry,
418                       struct win32_apply_ctx *ctx)
419 {
420         size_t len;
421         wchar_t *p;
422         const struct wim_dentry *d;
423
424         len = dentry_extraction_path_length(dentry);
425
426         ctx->pathbuf.Length = len * sizeof(wchar_t);
427         p = ctx->pathbuf.Buffer + len;
428         for (d = dentry;
429              !dentry_is_root(d->parent) && will_extract_dentry(d->parent);
430              d = d->parent)
431         {
432                 p -= d->d_extraction_name_nchars;
433                 wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
434                 *--p = '\\';
435         }
436         /* No leading slash  */
437         p -= d->d_extraction_name_nchars;
438         wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
439 }
440
441 /* Build the path at which to extract the @dentry, relative to the target
442  * directory, adding the suffix for a named data stream.
443  *
444  * The path is saved in ctx->pathbuf.  */
445 static void
446 build_extraction_path_with_ads(const struct wim_dentry *dentry,
447                                struct win32_apply_ctx *ctx,
448                                const wchar_t *stream_name,
449                                size_t stream_name_nchars)
450 {
451         wchar_t *p;
452
453         build_extraction_path(dentry, ctx);
454
455         /* Add :NAME for named data stream  */
456         p = ctx->pathbuf.Buffer + (ctx->pathbuf.Length / sizeof(wchar_t));
457         *p++ = L':';
458         wmemcpy(p, stream_name, stream_name_nchars);
459         ctx->pathbuf.Length += (1 + stream_name_nchars) * sizeof(wchar_t);
460 }
461
462 /* Build the Win32 namespace path to the specified @dentry when extracted.
463  *
464  * The path is saved in ctx->pathbuf and will be null terminated.
465  *
466  * XXX: We could get rid of this if it wasn't needed for the file encryption
467  * APIs.  */
468 static void
469 build_win32_extraction_path(const struct wim_dentry *dentry,
470                             struct win32_apply_ctx *ctx)
471 {
472         build_extraction_path(dentry, ctx);
473
474         /* Prepend target_ntpath to our relative path, then change \??\ into \\?\  */
475
476         memmove(ctx->pathbuf.Buffer +
477                         (ctx->target_ntpath.Length / sizeof(wchar_t)) + 1,
478                 ctx->pathbuf.Buffer, ctx->pathbuf.Length);
479         memcpy(ctx->pathbuf.Buffer, ctx->target_ntpath.Buffer,
480                 ctx->target_ntpath.Length);
481         ctx->pathbuf.Buffer[ctx->target_ntpath.Length / sizeof(wchar_t)] = L'\\';
482         ctx->pathbuf.Length += ctx->target_ntpath.Length + sizeof(wchar_t);
483         ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)] = L'\0';
484
485         wimlib_assert(ctx->pathbuf.Length >= 4 * sizeof(wchar_t) &&
486                       !wmemcmp(ctx->pathbuf.Buffer, L"\\??\\", 4));
487
488         ctx->pathbuf.Buffer[1] = L'\\';
489
490 }
491
492 /* Returns a "printable" representation of the last relative NT path that was
493  * constructed with build_extraction_path() or build_extraction_path_with_ads().
494  *
495  * This will be overwritten by the next call to this function.  */
496 static const wchar_t *
497 current_path(struct win32_apply_ctx *ctx)
498 {
499         wchar_t *p = ctx->print_buffer;
500
501         p = wmempcpy(p, ctx->common.target, ctx->common.target_nchars);
502         *p++ = L'\\';
503         p = wmempcpy(p, ctx->pathbuf.Buffer, ctx->pathbuf.Length / sizeof(wchar_t));
504         *p = L'\0';
505         return ctx->print_buffer;
506 }
507
508 /*
509  * Ensures the target directory exists and opens a handle to it, in preparation
510  * of using paths relative to it.
511  */
512 static int
513 prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
514 {
515         NTSTATUS status;
516         size_t path_max;
517
518         /* Open handle to the target directory (possibly creating it).  */
519
520         if (func_RtlDosPathNameToNtPathName_U_WithStatus) {
521                 status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(ctx->common.target,
522                                                                          &ctx->target_ntpath,
523                                                                          NULL, NULL);
524         } else {
525                 if ((*func_RtlDosPathNameToNtPathName_U)(ctx->common.target,
526                                                          &ctx->target_ntpath,
527                                                          NULL, NULL))
528                         status = STATUS_SUCCESS;
529                 else
530                         status = STATUS_NO_MEMORY;
531         }
532         if (!NT_SUCCESS(status)) {
533                 if (status == STATUS_NO_MEMORY) {
534                         return WIMLIB_ERR_NOMEM;
535                 } else {
536                         ERROR("\"%ls\": invalid path name "
537                               "(status=0x%08"PRIx32")",
538                               ctx->common.target, (u32)status);
539                         return WIMLIB_ERR_INVALID_PARAM;
540                 }
541         }
542
543         ctx->attr.Length = sizeof(ctx->attr);
544         ctx->attr.ObjectName = &ctx->target_ntpath;
545
546         status = (*func_NtCreateFile)(&ctx->h_target,
547                                       FILE_TRAVERSE,
548                                       &ctx->attr,
549                                       &ctx->iosb,
550                                       NULL,
551                                       0,
552                                       FILE_SHARE_VALID_FLAGS,
553                                       FILE_OPEN_IF,
554                                       FILE_DIRECTORY_FILE |
555                                               FILE_OPEN_REPARSE_POINT |
556                                               FILE_OPEN_FOR_BACKUP_INTENT,
557                                       NULL,
558                                       0);
559
560         if (!NT_SUCCESS(status)) {
561                 set_errno_from_nt_status(status);
562                 ERROR_WITH_ERRNO("Can't open or create directory \"%ls\" "
563                                  "(status=0x%08"PRIx32")",
564                                  ctx->common.target, (u32)status);
565                 return WIMLIB_ERR_OPENDIR;
566         }
567
568         path_max = compute_path_max(dentry_list);
569
570         /* Add some extra for building Win32 paths for the file encryption APIs
571          * ...  */
572         path_max += 2 + (ctx->target_ntpath.Length / sizeof(wchar_t));
573
574         ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t);
575         ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength);
576         if (!ctx->pathbuf.Buffer)
577                 return WIMLIB_ERR_NOMEM;
578
579         ctx->attr.RootDirectory = ctx->h_target;
580         ctx->attr.ObjectName = &ctx->pathbuf;
581
582         ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) *
583                                    sizeof(wchar_t));
584         if (!ctx->print_buffer)
585                 return WIMLIB_ERR_NOMEM;
586
587         return 0;
588 }
589
590 /* When creating an inode that will have a short (DOS) name, we create it using
591  * the long name associated with the short name.  This ensures that the short
592  * name gets associated with the correct long name.  */
593 static const struct wim_dentry *
594 first_extraction_alias(const struct wim_inode *inode)
595 {
596         const struct list_head *next = inode->i_extraction_aliases.next;
597         const struct wim_dentry *dentry;
598
599         do {
600                 dentry = list_entry(next, struct wim_dentry,
601                                     d_extraction_alias_node);
602                 if (dentry_has_short_name(dentry))
603                         break;
604                 next = next->next;
605         } while (next != &inode->i_extraction_aliases);
606         return dentry;
607 }
608
609 /*
610  * Set or clear FILE_ATTRIBUTE_COMPRESSED if the inherited value is different
611  * from the desired value.
612  *
613  * Note that you can NOT override the inherited value of
614  * FILE_ATTRIBUTE_COMPRESSED directly with NtCreateFile().
615  */
616 static int
617 adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
618                              struct win32_apply_ctx *ctx)
619 {
620         const bool compressed = (dentry->d_inode->i_attributes &
621                                  FILE_ATTRIBUTE_COMPRESSED);
622
623         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
624                 return 0;
625
626         if (!ctx->common.supported_features.compressed_files)
627                 return 0;
628
629         FILE_BASIC_INFORMATION info;
630         NTSTATUS status;
631         USHORT compression_state;
632
633         /* Get current attributes  */
634         status = (*func_NtQueryInformationFile)(h, &ctx->iosb,
635                                                 &info, sizeof(info),
636                                                 FileBasicInformation);
637         if (NT_SUCCESS(status) &&
638             compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED))
639         {
640                 /* Nothing needs to be done.  */
641                 return 0;
642         }
643
644         /* Set the new compression state  */
645
646         if (compressed)
647                 compression_state = COMPRESSION_FORMAT_DEFAULT;
648         else
649                 compression_state = COMPRESSION_FORMAT_NONE;
650
651         status = (*func_NtFsControlFile)(h,
652                                          NULL,
653                                          NULL,
654                                          NULL,
655                                          &ctx->iosb,
656                                          FSCTL_SET_COMPRESSION,
657                                          &compression_state,
658                                          sizeof(USHORT),
659                                          NULL,
660                                          0);
661         if (NT_SUCCESS(status))
662                 return 0;
663
664         set_errno_from_nt_status(status);
665         ERROR_WITH_ERRNO("Can't %s compression attribute on \"%ls\" "
666                          "(status=0x%08"PRIx32")",
667                          (compressed ? "set" : "clear"),
668                          current_path(ctx), status);
669         return WIMLIB_ERR_SET_ATTRIBUTES;
670 }
671
672 /*
673  * Clear FILE_ATTRIBUTE_ENCRYPTED if the file or directory is not supposed to be
674  * encrypted.
675  *
676  * You can provide FILE_ATTRIBUTE_ENCRYPTED to NtCreateFile() to set it on the
677  * created file.  However, the file or directory will otherwise default to the
678  * encryption state of the parent directory.  This function works around this
679  * limitation by using DecryptFile() to remove FILE_ATTRIBUTE_ENCRYPTED on files
680  * (and directories) that are not supposed to have it set.
681  *
682  * Regardless of whether it succeeds or fails, this function may close the
683  * handle to the file.  If it does, it sets it to NULL.
684  */
685 static int
686 maybe_clear_encryption_attribute(HANDLE *h_ret, const struct wim_dentry *dentry,
687                                  struct win32_apply_ctx *ctx)
688 {
689         if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
690                 return 0;
691
692         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
693                 return 0;
694
695         if (!ctx->common.supported_features.encrypted_files)
696                 return 0;
697
698         FILE_BASIC_INFORMATION info;
699         NTSTATUS status;
700         BOOL bret;
701
702         /* Get current attributes  */
703         status = (*func_NtQueryInformationFile)(*h_ret, &ctx->iosb,
704                                                 &info, sizeof(info),
705                                                 FileBasicInformation);
706         if (NT_SUCCESS(status) &&
707             !(info.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED))
708         {
709                 /* Nothing needs to be done.  */
710                 return 0;
711         }
712
713         /* Set the new encryption state  */
714
715         /* Due to Windows' crappy file encryption APIs, we need to close the
716          * handle to the file so we don't get ERROR_SHARING_VIOLATION.  We also
717          * hack together a Win32 path, although we will use the \\?\ prefix so
718          * it will actually be a NT path in disguise...  */
719         (*func_NtClose)(*h_ret);
720         *h_ret = NULL;
721
722         build_win32_extraction_path(dentry, ctx);
723
724         bret = DecryptFile(ctx->pathbuf.Buffer, 0);
725
726         /* Restore the NT namespace path  */
727         build_extraction_path(dentry, ctx);
728
729         if (!bret) {
730                 DWORD err = GetLastError();
731                 set_errno_from_win32_error(err);
732                 ERROR_WITH_ERRNO("Can't decrypt file \"%ls\" (err=%"PRIu32")",
733                                   current_path(ctx), (u32)err);
734                 return WIMLIB_ERR_SET_ATTRIBUTES;
735         }
736         return 0;
737 }
738
739 /* Set the short name on the open file @h which has been created at the location
740  * indicated by @dentry.
741  *
742  * Note that this may add, change, or remove the short name.
743  *
744  * @h must be opened with DELETE access.
745  *
746  * Returns 0 or WIMLIB_ERR_SET_SHORT_NAME.  The latter only happens in
747  * STRICT_SHORT_NAMES mode.
748  */
749 static int
750 set_short_name(HANDLE h, const struct wim_dentry *dentry,
751                struct win32_apply_ctx *ctx)
752 {
753         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
754                          dentry->short_name_nbytes;
755         u8 buf[bufsize] _aligned_attribute(8);
756         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
757         NTSTATUS status;
758
759         info->FileNameLength = dentry->short_name_nbytes;
760         memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
761
762         status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
763                                               FileShortNameInformation);
764         if (NT_SUCCESS(status))
765                 return 0;
766
767         /* By default, failure to set short names is not an error (since short
768          * names aren't too important anymore...).  */
769         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES))
770                 return 0;
771
772         if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
773                 if (dentry->short_name_nbytes == 0)
774                         return 0;
775                 ERROR("Can't extract short name when short "
776                       "names are not enabled on the volume!");
777         } else {
778                 ERROR("Can't set short name on \"%ls\" (status=0x%08"PRIx32")",
779                       current_path(ctx), (u32)status);
780         }
781         return WIMLIB_ERR_SET_SHORT_NAME;
782 }
783
784 /*
785  * A wrapper around NtCreateFile() to make it slightly more usable...
786  * This uses the path currently constructed in ctx->pathbuf.
787  *
788  * Also, we always specify FILE_OPEN_FOR_BACKUP_INTENT and
789  * FILE_OPEN_REPARSE_POINT.
790  */
791 static NTSTATUS
792 do_create_file(PHANDLE FileHandle,
793                ACCESS_MASK DesiredAccess,
794                PLARGE_INTEGER AllocationSize,
795                ULONG FileAttributes,
796                ULONG CreateDisposition,
797                ULONG CreateOptions,
798                struct win32_apply_ctx *ctx)
799 {
800         return (*func_NtCreateFile)(FileHandle,
801                                     DesiredAccess,
802                                     &ctx->attr,
803                                     &ctx->iosb,
804                                     AllocationSize,
805                                     FileAttributes,
806                                     FILE_SHARE_VALID_FLAGS,
807                                     CreateDisposition,
808                                     CreateOptions |
809                                         FILE_OPEN_FOR_BACKUP_INTENT |
810                                         FILE_OPEN_REPARSE_POINT,
811                                     NULL,
812                                     0);
813 }
814
815 /* Like do_create_file(), but builds the extraction path of the @dentry first.
816  */
817 static NTSTATUS
818 create_file(PHANDLE FileHandle,
819             ACCESS_MASK DesiredAccess,
820             PLARGE_INTEGER AllocationSize,
821             ULONG FileAttributes,
822             ULONG CreateDisposition,
823             ULONG CreateOptions,
824             const struct wim_dentry *dentry,
825             struct win32_apply_ctx *ctx)
826 {
827         build_extraction_path(dentry, ctx);
828         return do_create_file(FileHandle,
829                               DesiredAccess,
830                               AllocationSize,
831                               FileAttributes,
832                               CreateDisposition,
833                               CreateOptions,
834                               ctx);
835 }
836
837 /* Create empty named data streams.
838  *
839  * Since these won't have 'struct wim_lookup_table_entry's, they won't show up
840  * in the call to extract_stream_list().  Hence the need for the special case.
841  */
842 static int
843 create_any_empty_ads(const struct wim_dentry *dentry,
844                      struct win32_apply_ctx *ctx)
845 {
846         const struct wim_inode *inode = dentry->d_inode;
847         LARGE_INTEGER allocation_size;
848         bool path_modified = false;
849         int ret = 0;
850
851         if (!ctx->common.supported_features.named_data_streams)
852                 return 0;
853
854         for (u16 i = 0; i < inode->i_num_ads; i++) {
855                 const struct wim_ads_entry *entry;
856                 NTSTATUS status;
857                 HANDLE h;
858
859                 entry = &inode->i_ads_entries[i];
860
861                 /* Not named?  */
862                 if (!entry->stream_name_nbytes)
863                         continue;
864
865                 /* Not empty?  */
866                 if (entry->lte)
867                         continue;
868
869                 /* Probably setting the allocation size to 0 has no effect, but
870                  * we might as well try.  */
871                 allocation_size.QuadPart = 0;
872
873                 build_extraction_path_with_ads(dentry, ctx,
874                                                entry->stream_name,
875                                                entry->stream_name_nbytes /
876                                                         sizeof(wchar_t));
877                 path_modified = true;
878                 status = do_create_file(&h, FILE_WRITE_DATA, &allocation_size,
879                                         0, FILE_SUPERSEDE, 0, ctx);
880                 if (!NT_SUCCESS(status)) {
881                         set_errno_from_nt_status(status);
882                         ERROR_WITH_ERRNO("Can't create \"%ls\" "
883                                          "(status=0x%08"PRIx32")",
884                                          current_path(ctx), (u32)status);
885                         ret = WIMLIB_ERR_OPEN;
886                         break;
887                 }
888                 (*func_NtClose)(h);
889         }
890         /* Restore the path to the dentry itself  */
891         if (path_modified)
892                 build_extraction_path(dentry, ctx);
893         return ret;
894 }
895
896 /*
897  * Creates the directory named by @dentry, or uses an existing directory at that
898  * location.  If necessary, sets the short name and/or fixes compression and
899  * encryption attributes.
900  *
901  * Returns 0, WIMLIB_ERR_MKDIR, or WIMLIB_ERR_SET_SHORT_NAME.
902  */
903 static int
904 create_directory(const struct wim_dentry *dentry,
905                  struct win32_apply_ctx *ctx)
906 {
907         HANDLE h;
908         NTSTATUS status;
909         int ret;
910         ULONG attrib;
911
912         /* Special attributes:
913          *
914          * Use FILE_ATTRIBUTE_ENCRYPTED if the directory needs to have it set.
915          * This doesn't work for FILE_ATTRIBUTE_COMPRESSED (unfortunately).
916          *
917          * Don't specify FILE_ATTRIBUTE_DIRECTORY; it gets set anyway as a
918          * result of the FILE_DIRECTORY_FILE option.  */
919         attrib = (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED);
920
921         /* DELETE is needed for set_short_name().
922          * GENERIC_READ and GENERIC_WRITE are needed for
923          * adjust_compression_attribute().  */
924         status = create_file(&h, GENERIC_READ | GENERIC_WRITE | DELETE, NULL,
925                              attrib, FILE_OPEN_IF, FILE_DIRECTORY_FILE,
926                              dentry, ctx);
927         if (!NT_SUCCESS(status)) {
928                 set_errno_from_nt_status(status);
929                 ERROR_WITH_ERRNO("Can't create directory \"%ls\" "
930                                  "(status=0x%08"PRIx32")",
931                                  current_path(ctx), (u32)status);
932                 return WIMLIB_ERR_MKDIR;
933         }
934
935         ret = set_short_name(h, dentry, ctx);
936
937         if (!ret)
938                 ret = adjust_compression_attribute(h, dentry, ctx);
939
940         if (!ret)
941                 ret = maybe_clear_encryption_attribute(&h, dentry, ctx);
942                 /* May close the handle!!! */
943
944         if (h)
945                 (*func_NtClose)(h);
946         return ret;
947 }
948
949 /*
950  * Create all the directories being extracted, other than the target directory
951  * itself.
952  *
953  * Note: we don't honor directory hard links.  However, we don't allow them to
954  * exist in WIM images anyway (see inode_fixup.c).
955  */
956 static int
957 create_directories(struct list_head *dentry_list,
958                    struct win32_apply_ctx *ctx)
959 {
960         const struct wim_dentry *dentry;
961         int ret;
962
963         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
964
965                 if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
966                         continue;
967
968                 /* Note: Here we include files with
969                  * FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT, but we
970                  * wait until later to actually set the reparse data.  */
971
972                 /* If the root dentry is being extracted, it was already done so
973                  * it prepare_target().  */
974                 if (dentry_is_root(dentry))
975                         continue;
976
977                 ret = create_directory(dentry, ctx);
978                 if (ret)
979                         return ret;
980
981                 ret = create_any_empty_ads(dentry, ctx);
982                 if (ret)
983                         return ret;
984         }
985         return 0;
986 }
987
988 /* Gets the number of bytes to allocate for the specified inode.  */
989 static void
990 inode_get_allocation_size(const struct wim_inode *inode,
991                           LARGE_INTEGER *allocation_size_ret)
992 {
993         const struct wim_lookup_table_entry *unnamed_stream;
994
995         /* We just count the unnamed data stream.  */
996
997         unnamed_stream = inode_unnamed_lte_resolved(inode);
998         if (unnamed_stream)
999                 allocation_size_ret->QuadPart = unnamed_stream->size;
1000         else
1001                 allocation_size_ret->QuadPart = 0;
1002 }
1003
1004 /*
1005  * Creates the nondirectory file named by @dentry.
1006  *
1007  * On success, returns an open handle to the file in @h_ret, with GENERIC_READ,
1008  * GENERIC_WRITE, and DELETE access.  Also, the path to the file will be saved
1009  * in ctx->pathbuf.  On failure, returns WIMLIB_ERR_OPEN.
1010  */
1011 static int
1012 create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
1013                           struct win32_apply_ctx *ctx)
1014 {
1015         const struct wim_inode *inode;
1016         LARGE_INTEGER allocation_size;
1017         ULONG attrib;
1018         NTSTATUS status;
1019         bool retried = false;
1020
1021         inode = dentry->d_inode;
1022
1023         /* To increase performance, we will pre-allocate space for the file
1024          * data.  */
1025         inode_get_allocation_size(inode, &allocation_size);
1026
1027         /* If the file already exists and has FILE_ATTRIBUTE_SYSTEM and/or
1028          * FILE_ATTRIBUTE_HIDDEN, these must be specified in order to supersede
1029          * the file.
1030          *
1031          * Normally the user shouldn't be trying to overwrite such files anyway,
1032          * but we at least provide FILE_ATTRIBUTE_SYSTEM and
1033          * FILE_ATTRIBUTE_HIDDEN if the WIM inode has those attributes so that
1034          * we catch the case where the user extracts the same files to the same
1035          * location more than one time.
1036          *
1037          * Also specify FILE_ATTRIBUTE_ENCRYPTED if the file needs to be
1038          * encrypted.
1039          *
1040          * In NO_ATTRIBUTES mode just don't specify any attributes at all.
1041          */
1042         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) {
1043                 attrib = 0;
1044         } else {
1045                 attrib = (inode->i_attributes & (FILE_ATTRIBUTE_SYSTEM |
1046                                                  FILE_ATTRIBUTE_HIDDEN |
1047                                                  FILE_ATTRIBUTE_ENCRYPTED));
1048         }
1049         build_extraction_path(dentry, ctx);
1050 retry:
1051         status = do_create_file(h_ret, GENERIC_READ | GENERIC_WRITE | DELETE,
1052                                 &allocation_size, attrib, FILE_SUPERSEDE,
1053                                 FILE_NON_DIRECTORY_FILE, ctx);
1054         if (NT_SUCCESS(status)) {
1055                 int ret;
1056
1057                 ret = adjust_compression_attribute(*h_ret, dentry, ctx);
1058                 if (ret) {
1059                         (*func_NtClose)(*h_ret);
1060                         return ret;
1061                 }
1062
1063                 ret = maybe_clear_encryption_attribute(h_ret, dentry, ctx);
1064                 /* May close the handle!!! */
1065
1066                 if (ret) {
1067                         if (*h_ret)
1068                                 (*func_NtClose)(*h_ret);
1069                         return ret;
1070                 }
1071
1072                 if (!*h_ret) {
1073                         /* Re-open the handle so that we can return it on
1074                          * success.  */
1075                         status = do_create_file(h_ret,
1076                                                 GENERIC_READ |
1077                                                         GENERIC_WRITE | DELETE,
1078                                                 NULL, 0, FILE_OPEN,
1079                                                 FILE_NON_DIRECTORY_FILE, ctx);
1080                         if (!NT_SUCCESS(status))
1081                                 goto fail;
1082                 }
1083
1084                 ret = create_any_empty_ads(dentry, ctx);
1085                 if (ret) {
1086                         (*func_NtClose)(*h_ret);
1087                         return ret;
1088                 }
1089                 return 0;
1090         }
1091
1092         if (status == STATUS_ACCESS_DENIED && !retried) {
1093                 /* We also can't supersede an existing file that has
1094                  * FILE_ATTRIBUTE_READONLY set; doing so causes NtCreateFile()
1095                  * to return STATUS_ACCESS_DENIED .  The only workaround seems
1096                  * to be to explicitly remove FILE_ATTRIBUTE_READONLY on the
1097                  * existing file, then try again.  */
1098
1099                 FILE_BASIC_INFORMATION info;
1100                 HANDLE h;
1101
1102                 status = do_create_file(&h, FILE_WRITE_ATTRIBUTES, NULL, 0,
1103                                         FILE_OPEN, FILE_NON_DIRECTORY_FILE, ctx);
1104                 if (!NT_SUCCESS(status))
1105                         goto fail;
1106
1107                 memset(&info, 0, sizeof(info));
1108                 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
1109
1110                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1111                                                       &info, sizeof(info),
1112                                                       FileBasicInformation);
1113                 (*func_NtClose)(h);
1114                 if (!NT_SUCCESS(status))
1115                         goto fail;
1116                 retried = true;
1117                 goto retry;
1118         }
1119 fail:
1120         set_errno_from_nt_status(status);
1121         ERROR_WITH_ERRNO("Can't create file \"%ls\" (status=0x%08"PRIx32")",
1122                          current_path(ctx), (u32)status);
1123         return WIMLIB_ERR_OPEN;
1124 }
1125
1126 /* Creates a hard link at the location named by @dentry to the file represented
1127  * by the open handle @h.  Or, if the target volume does not support hard links,
1128  * create a separate file instead.  */
1129 static int
1130 create_link(HANDLE h, const struct wim_dentry *dentry,
1131             struct win32_apply_ctx *ctx)
1132 {
1133         if (ctx->common.supported_features.hard_links) {
1134
1135                 build_extraction_path(dentry, ctx);
1136
1137                 size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
1138                                  ctx->pathbuf.Length + sizeof(wchar_t);
1139                 u8 buf[bufsize] _aligned_attribute(8);
1140                 FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
1141                 NTSTATUS status;
1142
1143                 info->ReplaceIfExists = TRUE;
1144                 info->RootDirectory = ctx->attr.RootDirectory;
1145                 info->FileNameLength = ctx->pathbuf.Length;
1146                 memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
1147                 info->FileName[info->FileNameLength / 2] = L'\0';
1148
1149                 /* Note: the null terminator isn't actually necessary,
1150                  * but if you don't add the extra character, you get
1151                  * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
1152                  * happens to be 2  */
1153
1154                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1155                                                       info, bufsize,
1156                                                       FileLinkInformation);
1157                 if (NT_SUCCESS(status))
1158                         return 0;
1159                 ERROR("Failed to create link \"%ls\" (status=0x%08"PRIx32")",
1160                       current_path(ctx), (u32)status);
1161                 return WIMLIB_ERR_LINK;
1162         } else {
1163                 HANDLE h2;
1164                 int ret;
1165
1166                 ret = create_nondirectory_inode(&h2, dentry, ctx);
1167                 if (ret)
1168                         return ret;
1169
1170                 (*func_NtClose)(h2);
1171                 return 0;
1172         }
1173 }
1174
1175 /* Given an inode (represented by the open handle @h) for which one link has
1176  * been created (named by @first_dentry), create the other links.
1177  *
1178  * Or, if the target volume does not support hard links, create separate files.
1179  *
1180  * Note: This uses ctx->pathbuf and does not reset it.
1181  */
1182 static int
1183 create_links(HANDLE h, const struct wim_dentry *first_dentry,
1184              struct win32_apply_ctx *ctx)
1185 {
1186         const struct wim_inode *inode;
1187         const struct list_head *next;
1188         const struct wim_dentry *dentry;
1189         int ret;
1190
1191         inode = first_dentry->d_inode;
1192         next = inode->i_extraction_aliases.next;
1193         do {
1194                 dentry = list_entry(next, struct wim_dentry,
1195                                     d_extraction_alias_node);
1196                 if (dentry != first_dentry) {
1197                         ret = create_link(h, dentry, ctx);
1198                         if (ret)
1199                                 return ret;
1200                 }
1201                 next = next->next;
1202         } while (next != &inode->i_extraction_aliases);
1203         return 0;
1204 }
1205
1206 /* Create a nondirectory file, including all links.  */
1207 static int
1208 create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx)
1209 {
1210         const struct wim_dentry *first_dentry;
1211         HANDLE h;
1212         int ret;
1213
1214         first_dentry = first_extraction_alias(inode);
1215
1216         /* Create first link.  */
1217         ret = create_nondirectory_inode(&h, first_dentry, ctx);
1218         if (ret)
1219                 return ret;
1220
1221         /* Set short name.  */
1222         ret = set_short_name(h, first_dentry, ctx);
1223
1224         /* Create additional links, OR if hard links are not supported just
1225          * create more files.  */
1226         if (!ret)
1227                 ret = create_links(h, first_dentry, ctx);
1228
1229         (*func_NtClose)(h);
1230         return ret;
1231 }
1232
1233 /* Create all the nondirectory files being extracted, including all aliases
1234  * (hard links).  */
1235 static int
1236 create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1237 {
1238         const struct wim_dentry *dentry;
1239         const struct wim_inode *inode;
1240         int ret;
1241
1242         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1243                 inode = dentry->d_inode;
1244                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1245                         continue;
1246                 /* Call create_nondirectory() only once per inode  */
1247                 if (dentry != inode_first_extraction_dentry(inode))
1248                         continue;
1249                 ret = create_nondirectory(inode, ctx);
1250                 if (ret)
1251                         return ret;
1252         }
1253         return 0;
1254 }
1255
1256 static void
1257 close_handles(struct win32_apply_ctx *ctx)
1258 {
1259         for (unsigned i = 0; i < ctx->num_open_handles; i++)
1260                 (*func_NtClose)(ctx->open_handles[i]);
1261 }
1262
1263 /* Prepare to read the next stream, which has size @stream_size, into an
1264  * in-memory buffer.  */
1265 static int
1266 prepare_data_buffer(struct win32_apply_ctx *ctx, u64 stream_size)
1267 {
1268         if (stream_size > ctx->data_buffer_size) {
1269                 /* Larger buffer needed.  */
1270                 void *new_buffer;
1271                 if ((size_t)stream_size != stream_size)
1272                         return WIMLIB_ERR_NOMEM;
1273                 new_buffer = REALLOC(ctx->data_buffer, stream_size);
1274                 if (!new_buffer)
1275                         return WIMLIB_ERR_NOMEM;
1276                 ctx->data_buffer = new_buffer;
1277                 ctx->data_buffer_size = stream_size;
1278         }
1279         /* On the first call this changes data_buffer_ptr from NULL, which tells
1280          * extract_chunk() that the data buffer needs to be filled while reading
1281          * the stream data.  */
1282         ctx->data_buffer_ptr = ctx->data_buffer;
1283         return 0;
1284 }
1285
1286 static int
1287 begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
1288                               struct wim_dentry *dentry,
1289                               const wchar_t *stream_name,
1290                               struct win32_apply_ctx *ctx)
1291 {
1292         const struct wim_inode *inode = dentry->d_inode;
1293         size_t stream_name_nchars = 0;
1294         LARGE_INTEGER allocation_size;
1295         HANDLE h;
1296         NTSTATUS status;
1297
1298         if (unlikely(stream_name))
1299                 stream_name_nchars = wcslen(stream_name);
1300
1301         if (unlikely(stream_name_nchars)) {
1302                 build_extraction_path_with_ads(dentry, ctx,
1303                                                stream_name, stream_name_nchars);
1304         } else {
1305                 build_extraction_path(dentry, ctx);
1306         }
1307
1308         /* Reparse point?  */
1309         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
1310             && (stream_name_nchars == 0))
1311         {
1312                 if (!ctx->common.supported_features.reparse_points)
1313                         return 0;
1314
1315                 /* We can't write the reparse stream directly; we must set it
1316                  * with FSCTL_SET_REPARSE_POINT, which requires that all the
1317                  * data be available.  So, stage the data in a buffer.  */
1318
1319                 list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
1320                 return prepare_data_buffer(ctx, stream->size);
1321         }
1322
1323         /* Encrypted file?  */
1324         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
1325             && (stream_name_nchars == 0))
1326         {
1327                 if (!ctx->common.supported_features.encrypted_files)
1328                         return 0;
1329
1330                 /* We can't write encrypted file streams directly; we must use
1331                  * WriteEncryptedFileRaw(), which requires providing the data
1332                  * through a callback function.  This can't easily be combined
1333                  * with our own callback-based approach.
1334                  *
1335                  * The current workaround is to simply read the stream into
1336                  * memory and write the encrypted file from that.
1337                  *
1338                  * TODO: This isn't sufficient for extremely large encrypted
1339                  * files.  Perhaps we should create an extra thread to write
1340                  * such files...  */
1341                 list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries);
1342                 return prepare_data_buffer(ctx, stream->size);
1343         }
1344
1345         /* Extracting unnamed data stream in WIMBoot mode?  */
1346         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)
1347             && (stream_name_nchars == 0)
1348             && (stream->resource_location == RESOURCE_IN_WIM)
1349             && (stream->rspec->wim == ctx->common.wim)
1350             && (stream->size == stream->rspec->uncompressed_size))
1351         {
1352                 int ret = calculate_dentry_full_path(dentry);
1353                 if (ret)
1354                         return ret;
1355                 if (in_prepopulate_list(dentry, ctx)) {
1356                         union wimlib_progress_info info;
1357
1358                         info.wimboot_exclude.path_in_wim = dentry->_full_path;
1359                         info.wimboot_exclude.extraction_path = current_path(ctx);
1360
1361                         ret = call_progress(ctx->common.progfunc,
1362                                             WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
1363                                             &info, ctx->common.progctx);
1364                         FREE(dentry->_full_path);
1365                         dentry->_full_path = NULL;
1366                         return ret;
1367                 } else {
1368                         FREE(dentry->_full_path);
1369                         dentry->_full_path = NULL;
1370                         return wimboot_set_pointer(&ctx->attr,
1371                                                    current_path(ctx),
1372                                                    stream,
1373                                                    ctx->wimboot.data_source_id,
1374                                                    ctx->wimboot.wim_lookup_table_hash,
1375                                                    ctx->wimboot.wof_running);
1376                 }
1377         }
1378
1379         /* Too many open handles?  */
1380         if (ctx->num_open_handles == MAX_OPEN_HANDLES) {
1381                 ERROR("Too many open handles!");
1382                 return WIMLIB_ERR_UNSUPPORTED;
1383         }
1384
1385         /* Open a new handle  */
1386         allocation_size.QuadPart = stream->size;
1387         status = do_create_file(&h,
1388                                 FILE_WRITE_DATA | SYNCHRONIZE,
1389                                 &allocation_size, 0, FILE_OPEN_IF,
1390                                 FILE_SEQUENTIAL_ONLY |
1391                                         FILE_SYNCHRONOUS_IO_NONALERT,
1392                                 ctx);
1393         if (!NT_SUCCESS(status)) {
1394                 set_errno_from_nt_status(status);
1395                 ERROR_WITH_ERRNO("Can't open \"%ls\" for writing "
1396                                  "(status=0x%08"PRIx32")",
1397                                  current_path(ctx), (u32)status);
1398                 return WIMLIB_ERR_OPEN;
1399         }
1400
1401         ctx->open_handles[ctx->num_open_handles++] = h;
1402         return 0;
1403 }
1404
1405 /* Set the reparse data @rpbuf of length @rpbuflen on the extracted file
1406  * corresponding to the WIM dentry @dentry.  */
1407 static int
1408 do_set_reparse_data(const struct wim_dentry *dentry,
1409                     const void *rpbuf, u16 rpbuflen,
1410                     struct win32_apply_ctx *ctx)
1411 {
1412         NTSTATUS status;
1413         HANDLE h;
1414
1415         status = create_file(&h, GENERIC_WRITE, NULL,
1416                              0, FILE_OPEN, 0, dentry, ctx);
1417         if (!NT_SUCCESS(status))
1418                 goto fail;
1419
1420         status = (*func_NtFsControlFile)(h, NULL, NULL, NULL,
1421                                          &ctx->iosb, FSCTL_SET_REPARSE_POINT,
1422                                          (void *)rpbuf, rpbuflen,
1423                                          NULL, 0);
1424         (*func_NtClose)(h);
1425
1426         if (NT_SUCCESS(status))
1427                 return 0;
1428
1429         /* On Windows, by default only the Administrator can create symbolic
1430          * links for some reason.  By default we just issue a warning if this
1431          * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
1432          * to get a hard error.  */
1433         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
1434             && (status == STATUS_PRIVILEGE_NOT_HELD ||
1435                 status == STATUS_ACCESS_DENIED)
1436             && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1437                 dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1438         {
1439                 WARNING("Can't create symbolic link \"%ls\"!              \n"
1440                         "          (Need Administrator rights, or at least "
1441                         "the\n"
1442                         "          SeCreateSymbolicLink privilege.)",
1443                         current_path(ctx));
1444                 return 0;
1445         }
1446
1447 fail:
1448         set_errno_from_nt_status(status);
1449         ERROR_WITH_ERRNO("Can't set reparse data on \"%ls\" "
1450                          "(status=0x%08"PRIx32")",
1451                          current_path(ctx), (u32)status);
1452         return WIMLIB_ERR_SET_REPARSE_DATA;
1453 }
1454
1455 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1456  * pointer to the suffix of the path that begins with the device directly, such
1457  * as e:\Windows\System32.  */
1458 static const wchar_t *
1459 skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
1460 {
1461         static const wchar_t * const dirs[] = {
1462                 L"\\??\\",
1463                 L"\\DosDevices\\",
1464                 L"\\Device\\",
1465         };
1466         size_t first_dir_len = 0;
1467         const wchar_t * const end = path + path_nchars;
1468
1469         for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
1470                 size_t len = wcslen(dirs[i]);
1471                 if (len <= (end - path) && !wcsnicmp(path, dirs[i], len)) {
1472                         first_dir_len = len;
1473                         break;
1474                 }
1475         }
1476         if (first_dir_len == 0)
1477                 return path;
1478         path += first_dir_len;
1479         while (path != end && *path == L'\\')
1480                 path++;
1481         return path;
1482 }
1483
1484 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1485  * pointer to the suffix of the path that is device-relative, such as
1486  * Windows\System32.  */
1487 static const wchar_t *
1488 get_device_relative_path(const wchar_t *path, size_t path_nchars)
1489 {
1490         const wchar_t * const orig_path = path;
1491         const wchar_t * const end = path + path_nchars;
1492
1493         path = skip_nt_toplevel_component(path, path_nchars);
1494         if (path == orig_path)
1495                 return orig_path;
1496
1497         path = wmemchr(path, L'\\', (end - path));
1498         if (!path)
1499                 return orig_path;
1500         do {
1501                 path++;
1502         } while (path != end && *path == L'\\');
1503         return path;
1504 }
1505
1506 /*
1507  * Given a reparse point buffer for a symbolic link or junction, adjust its
1508  * contents so that the target of the link is consistent with the new location
1509  * of the files.
1510  */
1511 static void
1512 try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, struct win32_apply_ctx *ctx)
1513 {
1514         struct reparse_data rpdata;
1515         size_t orig_subst_name_nchars;
1516         const wchar_t *relpath;
1517         size_t relpath_nchars;
1518         size_t target_ntpath_nchars;
1519         size_t fixed_subst_name_nchars;
1520         const wchar_t *fixed_print_name;
1521         size_t fixed_print_name_nchars;
1522
1523         if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
1524                 /* Do nothing if the reparse data is invalid.  */
1525                 return;
1526         }
1527
1528         if (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
1529             (rpdata.rpflags & SYMBOLIC_LINK_RELATIVE))
1530         {
1531                 /* Do nothing if it's a relative symbolic link.  */
1532                 return;
1533         }
1534
1535         /* Build the new substitute name from the NT namespace path to the
1536          * target directory, then a path separator, then the "device relative"
1537          * part of the old substitute name.  */
1538
1539         orig_subst_name_nchars = rpdata.substitute_name_nbytes / sizeof(wchar_t);
1540
1541         relpath = get_device_relative_path(rpdata.substitute_name,
1542                                            orig_subst_name_nchars);
1543         relpath_nchars = orig_subst_name_nchars -
1544                          (relpath - rpdata.substitute_name);
1545
1546         target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
1547
1548         fixed_subst_name_nchars = target_ntpath_nchars + 1 + relpath_nchars;
1549         wchar_t fixed_subst_name[fixed_subst_name_nchars];
1550
1551         wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer,
1552                 target_ntpath_nchars);
1553         fixed_subst_name[target_ntpath_nchars] = L'\\';
1554         wmemcpy(&fixed_subst_name[target_ntpath_nchars + 1],
1555                 relpath, relpath_nchars);
1556         /* Doesn't need to be null-terminated.  */
1557
1558         /* Print name should be Win32, but not all NT names can even be
1559          * translated to Win32 names.  But we can at least delete the top-level
1560          * directory, such as \??\, and this will have the expected result in
1561          * the usual case.  */
1562         fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
1563                                                       fixed_subst_name_nchars);
1564         fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
1565                                                              fixed_subst_name);
1566
1567         rpdata.substitute_name = fixed_subst_name;
1568         rpdata.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
1569         rpdata.print_name = (wchar_t *)fixed_print_name;
1570         rpdata.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
1571         make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
1572 }
1573
1574 /* Sets reparse data on the specified file.  This handles "fixing" the targets
1575  * of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX was
1576  * specified.  */
1577 static int
1578 set_reparse_data(const struct wim_dentry *dentry,
1579                  const void *_rpbuf, u16 rpbuflen, struct win32_apply_ctx *ctx)
1580 {
1581         const struct wim_inode *inode = dentry->d_inode;
1582         const void *rpbuf = _rpbuf;
1583
1584         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
1585             && !inode->i_not_rpfixed
1586             && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1587                 inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1588         {
1589                 memcpy(&ctx->rpfixbuf, _rpbuf, rpbuflen);
1590                 try_rpfix((u8 *)&ctx->rpfixbuf, &rpbuflen, ctx);
1591                 rpbuf = &ctx->rpfixbuf;
1592         }
1593         return do_set_reparse_data(dentry, rpbuf, rpbuflen, ctx);
1594
1595 }
1596
1597 /* Import the next block of raw encrypted data  */
1598 static DWORD WINAPI
1599 import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
1600 {
1601         struct win32_apply_ctx *ctx = pvCallbackContext;
1602         ULONG copy_len;
1603
1604         copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
1605         memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
1606         ctx->encrypted_offset += copy_len;
1607         *Length = copy_len;
1608         return ERROR_SUCCESS;
1609 }
1610
1611 /* Write the raw encrypted data to the already-created file corresponding to
1612  * @dentry.
1613  *
1614  * The raw encrypted data is provided in ctx->data_buffer, and its size is
1615  * ctx->encrypted_size.  */
1616 static int
1617 extract_encrypted_file(const struct wim_dentry *dentry,
1618                        struct win32_apply_ctx *ctx)
1619 {
1620         void *rawctx;
1621         DWORD err;
1622
1623         /* Temporarily build a Win32 path for OpenEncryptedFileRaw()  */
1624         build_win32_extraction_path(dentry, ctx);
1625
1626         err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer,
1627                                    CREATE_FOR_IMPORT, &rawctx);
1628
1629         /* Restore the NT namespace path  */
1630         build_extraction_path(dentry, ctx);
1631
1632         if (err != ERROR_SUCCESS) {
1633                 set_errno_from_win32_error(err);
1634                 ERROR_WITH_ERRNO("Can't open \"%ls\" for encrypted import "
1635                                  "(err=%"PRIu32")", current_path(ctx), (u32)err);
1636                 return WIMLIB_ERR_OPEN;
1637         }
1638
1639         ctx->encrypted_offset = 0;
1640
1641         err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx);
1642
1643         CloseEncryptedFileRaw(rawctx);
1644
1645         if (err != ERROR_SUCCESS) {
1646                 set_errno_from_win32_error(err);
1647                 ERROR_WITH_ERRNO("Can't import encrypted file \"%ls\" "
1648                                  "(err=%"PRIu32")", current_path(ctx), (u32)err);
1649                 return WIMLIB_ERR_WRITE;
1650         }
1651
1652         return 0;
1653 }
1654
1655 /* Called when starting to read a stream for extraction on Windows  */
1656 static int
1657 begin_extract_stream(struct wim_lookup_table_entry *stream,
1658                      u32 flags, void *_ctx)
1659 {
1660         struct win32_apply_ctx *ctx = _ctx;
1661         const struct stream_owner *owners = stream_owners(stream);
1662         int ret;
1663
1664         ctx->num_open_handles = 0;
1665         ctx->data_buffer_ptr = NULL;
1666         INIT_LIST_HEAD(&ctx->reparse_dentries);
1667         INIT_LIST_HEAD(&ctx->encrypted_dentries);
1668
1669         for (u32 i = 0; i < stream->out_refcnt; i++) {
1670                 const struct wim_inode *inode = owners[i].inode;
1671                 const wchar_t *stream_name = owners[i].stream_name;
1672                 struct wim_dentry *dentry;
1673
1674                 /* A copy of the stream needs to be extracted to @inode.  */
1675
1676                 if (ctx->common.supported_features.hard_links) {
1677                         dentry = inode_first_extraction_dentry(inode);
1678                         ret = begin_extract_stream_instance(stream, dentry,
1679                                                             stream_name, ctx);
1680                         if (ret)
1681                                 goto fail;
1682                 } else {
1683                         /* Hard links not supported.  Extract the stream
1684                          * separately to each alias of the inode.  */
1685                         struct list_head *next;
1686
1687                         next = inode->i_extraction_aliases.next;
1688                         do {
1689                                 dentry = list_entry(next, struct wim_dentry,
1690                                                     d_extraction_alias_node);
1691                                 ret = begin_extract_stream_instance(stream,
1692                                                                     dentry,
1693                                                                     stream_name,
1694                                                                     ctx);
1695                                 if (ret)
1696                                         goto fail;
1697                                 next = next->next;
1698                         } while (next != &inode->i_extraction_aliases);
1699                 }
1700         }
1701
1702         if (unlikely(ctx->num_open_handles == 0 && ctx->data_buffer_ptr == NULL)) {
1703                 /* The data of this stream isn't actually needed!
1704                  * (This can happen in WIMBoot mode.)  */
1705                 return BEGIN_STREAM_STATUS_SKIP_STREAM;
1706         }
1707         return 0;
1708
1709 fail:
1710         close_handles(ctx);
1711         return ret;
1712 }
1713
1714 /* Called when the next chunk of a stream has been read for extraction on
1715  * Windows  */
1716 static int
1717 extract_chunk(const void *chunk, size_t size, void *_ctx)
1718 {
1719         struct win32_apply_ctx *ctx = _ctx;
1720
1721         /* Write the data chunk to each open handle  */
1722         for (unsigned i = 0; i < ctx->num_open_handles; i++) {
1723                 u8 *bufptr = (u8 *)chunk;
1724                 size_t bytes_remaining = size;
1725                 NTSTATUS status;
1726                 while (bytes_remaining) {
1727                         ULONG count = min(0xFFFFFFFF, bytes_remaining);
1728
1729                         status = (*func_NtWriteFile)(ctx->open_handles[i],
1730                                                      NULL, NULL, NULL,
1731                                                      &ctx->iosb, bufptr, count,
1732                                                      NULL, NULL);
1733                         if (!NT_SUCCESS(status)) {
1734                                 set_errno_from_nt_status(status);
1735                                 ERROR_WITH_ERRNO("Error writing data to target "
1736                                                  "volume (status=0x%08"PRIx32")",
1737                                                  (u32)status);
1738                                 return WIMLIB_ERR_WRITE;
1739                         }
1740                         bufptr += ctx->iosb.Information;
1741                         bytes_remaining -= ctx->iosb.Information;
1742                 }
1743         }
1744
1745         /* Copy the data chunk into the buffer (if needed)  */
1746         if (ctx->data_buffer_ptr)
1747                 ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
1748                                                chunk, size);
1749         return 0;
1750 }
1751
1752 /* Called when a stream has been fully read for extraction on Windows  */
1753 static int
1754 end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx)
1755 {
1756         struct win32_apply_ctx *ctx = _ctx;
1757         int ret;
1758         const struct wim_dentry *dentry;
1759
1760         close_handles(ctx);
1761
1762         if (status)
1763                 return status;
1764
1765         if (likely(!ctx->data_buffer_ptr))
1766                 return 0;
1767
1768         if (!list_empty(&ctx->reparse_dentries)) {
1769                 if (stream->size > REPARSE_DATA_MAX_SIZE) {
1770                         dentry = list_first_entry(&ctx->reparse_dentries,
1771                                                   struct wim_dentry, tmp_list);
1772                         build_extraction_path(dentry, ctx);
1773                         ERROR("Reparse data of \"%ls\" has size "
1774                               "%"PRIu64" bytes (exceeds %u bytes)",
1775                               current_path(ctx), stream->size,
1776                               REPARSE_DATA_MAX_SIZE);
1777                         return WIMLIB_ERR_INVALID_REPARSE_DATA;
1778                 }
1779                 /* In the WIM format, reparse streams are just the reparse data
1780                  * and omit the header.  But we can reconstruct the header.  */
1781                 memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, stream->size);
1782                 ctx->rpbuf.rpdatalen = stream->size;
1783                 ctx->rpbuf.rpreserved = 0;
1784                 list_for_each_entry(dentry, &ctx->reparse_dentries, tmp_list) {
1785                         ctx->rpbuf.rptag = dentry->d_inode->i_reparse_tag;
1786                         ret = set_reparse_data(dentry, &ctx->rpbuf,
1787                                                stream->size + REPARSE_DATA_OFFSET,
1788                                                ctx);
1789                         if (ret)
1790                                 return ret;
1791                 }
1792         }
1793
1794         if (!list_empty(&ctx->encrypted_dentries)) {
1795                 ctx->encrypted_size = stream->size;
1796                 list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) {
1797                         ret = extract_encrypted_file(dentry, ctx);
1798                         if (ret)
1799                                 return ret;
1800                 }
1801         }
1802
1803         return 0;
1804 }
1805
1806 /* Attributes that can't be set directly  */
1807 #define SPECIAL_ATTRIBUTES                      \
1808         (FILE_ATTRIBUTE_REPARSE_POINT   |       \
1809          FILE_ATTRIBUTE_DIRECTORY       |       \
1810          FILE_ATTRIBUTE_ENCRYPTED       |       \
1811          FILE_ATTRIBUTE_SPARSE_FILE     |       \
1812          FILE_ATTRIBUTE_COMPRESSED)
1813
1814 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
1815  * handle @h.  */
1816 static NTSTATUS
1817 set_security_descriptor(HANDLE h, const void *desc,
1818                         size_t desc_size, struct win32_apply_ctx *ctx)
1819 {
1820         SECURITY_INFORMATION info;
1821         NTSTATUS status;
1822
1823         /* We really just want to set entire the security descriptor as-is, but
1824          * all available APIs require specifying the specific parts of the
1825          * descriptor being set.  Start out by requesting all parts be set.  If
1826          * permissions problems are encountered, fall back to omitting some
1827          * parts (first the SACL, then the DACL, then the owner), unless the
1828          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS flag has been enabled.  */
1829         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
1830                DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION;
1831
1832         /* Prefer NtSetSecurityObject() to SetFileSecurity().  SetFileSecurity()
1833          * itself necessarily uses NtSetSecurityObject() as the latter is the
1834          * underlying system call for setting security information, but
1835          * SetFileSecurity() opens the handle with NtCreateFile() without
1836          * FILE_OPEN_FILE_BACKUP_INTENT.  Hence, access checks are done and due
1837          * to the Windows security model, even a process running as the
1838          * Administrator can have access denied.  (Of course, this not mentioned
1839          * in the MS "documentation".)  */
1840 retry:
1841         status = (*func_NtSetSecurityObject)(h, info, (PSECURITY_DESCRIPTOR)desc);
1842         if (NT_SUCCESS(status))
1843                 return status;
1844         /* Failed to set the requested parts of the security descriptor.  If the
1845          * error was permissions-related, try to set fewer parts of the security
1846          * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
1847         if ((status == STATUS_PRIVILEGE_NOT_HELD ||
1848              status == STATUS_ACCESS_DENIED) &&
1849             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
1850         {
1851                 if (info & SACL_SECURITY_INFORMATION) {
1852                         info &= ~SACL_SECURITY_INFORMATION;
1853                         ctx->partial_security_descriptors++;
1854                         goto retry;
1855                 }
1856                 if (info & DACL_SECURITY_INFORMATION) {
1857                         info &= ~DACL_SECURITY_INFORMATION;
1858                         goto retry;
1859                 }
1860                 if (info & OWNER_SECURITY_INFORMATION) {
1861                         info &= ~OWNER_SECURITY_INFORMATION;
1862                         goto retry;
1863                 }
1864                 /* Nothing left except GROUP, and if we removed it we
1865                  * wouldn't have anything at all.  */
1866         }
1867
1868         /* No part of the security descriptor could be set, or
1869          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security
1870          * descriptor could not be set.  */
1871         if (!(info & SACL_SECURITY_INFORMATION))
1872                 ctx->partial_security_descriptors--;
1873         ctx->no_security_descriptors++;
1874         return status;
1875 }
1876
1877 /* Set metadata on the open file @h from the WIM inode @inode.  */
1878 static int
1879 do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
1880                           struct win32_apply_ctx *ctx)
1881 {
1882         FILE_BASIC_INFORMATION info;
1883         NTSTATUS status;
1884
1885         /* Set security descriptor if present and not in NO_ACLS mode  */
1886         if (inode->i_security_id >= 0 &&
1887             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
1888         {
1889                 const struct wim_security_data *sd;
1890                 const void *desc;
1891                 size_t desc_size;
1892
1893                 sd = wim_get_current_security_data(ctx->common.wim);
1894                 desc = sd->descriptors[inode->i_security_id];
1895                 desc_size = sd->sizes[inode->i_security_id];
1896
1897                 status = set_security_descriptor(h, desc, desc_size, ctx);
1898                 if (!NT_SUCCESS(status) &&
1899                     (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
1900                 {
1901                         set_errno_from_nt_status(status);
1902                         ERROR_WITH_ERRNO("Can't set security descriptor "
1903                                          "on \"%ls\" (status=0x%08"PRIx32")",
1904                                          current_path(ctx), (u32)status);
1905                         return WIMLIB_ERR_SET_SECURITY;
1906                 }
1907         }
1908
1909         /* Set attributes and timestamps  */
1910         info.CreationTime.QuadPart = inode->i_creation_time;
1911         info.LastAccessTime.QuadPart = inode->i_last_access_time;
1912         info.LastWriteTime.QuadPart = inode->i_last_write_time;
1913         info.ChangeTime.QuadPart = 0;
1914         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
1915                 info.FileAttributes = 0;
1916         else
1917                 info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES;
1918
1919         status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1920                                               &info, sizeof(info),
1921                                               FileBasicInformation);
1922         /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set
1923          * attributes on the root directory.  (Apparently because FAT doesn't
1924          * actually have a place to store those attributes!)  */
1925         if (!NT_SUCCESS(status)
1926             && !(status == STATUS_INVALID_PARAMETER &&
1927                  dentry_is_root(inode_first_extraction_dentry(inode))))
1928         {
1929                 set_errno_from_nt_status(status);
1930                 ERROR_WITH_ERRNO("Can't set basic metadata on \"%ls\" "
1931                                  "(status=0x%08"PRIx32")",
1932                                  current_path(ctx), (u32)status);
1933                 return WIMLIB_ERR_SET_ATTRIBUTES;
1934         }
1935
1936         return 0;
1937 }
1938
1939 static int
1940 apply_metadata_to_file(const struct wim_dentry *dentry,
1941                        struct win32_apply_ctx *ctx)
1942 {
1943         const struct wim_inode *inode = dentry->d_inode;
1944         DWORD perms;
1945         HANDLE h;
1946         NTSTATUS status;
1947         int ret;
1948
1949         perms = FILE_WRITE_ATTRIBUTES | WRITE_DAC |
1950                 WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
1951
1952         build_extraction_path(dentry, ctx);
1953
1954         /* Open a handle with as many relevant permissions as possible.  */
1955         while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL,
1956                                                    0, FILE_OPEN, 0, ctx)))
1957         {
1958                 if (status == STATUS_PRIVILEGE_NOT_HELD ||
1959                     status == STATUS_ACCESS_DENIED)
1960                 {
1961                         if (perms & ACCESS_SYSTEM_SECURITY) {
1962                                 perms &= ~ACCESS_SYSTEM_SECURITY;
1963                                 continue;
1964                         }
1965                         if (perms & WRITE_DAC) {
1966                                 perms &= ~WRITE_DAC;
1967                                 continue;
1968                         }
1969                         if (perms & WRITE_OWNER) {
1970                                 perms &= ~WRITE_OWNER;
1971                                 continue;
1972                         }
1973                 }
1974                 set_errno_from_nt_status(status);
1975                 ERROR_WITH_ERRNO("Can't open \"%ls\" to set metadata "
1976                                  "(status=0x%08"PRIx32")",
1977                                  current_path(ctx), (u32)status);
1978                 return WIMLIB_ERR_OPEN;
1979         }
1980
1981         ret = do_apply_metadata_to_file(h, inode, ctx);
1982
1983         (*func_NtClose)(h);
1984
1985         return ret;
1986 }
1987
1988 static int
1989 apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1990 {
1991         const struct wim_dentry *dentry;
1992         int ret;
1993
1994         /* We go in reverse so that metadata is set on all a directory's
1995          * children before the directory itself.  This avoids any potential
1996          * problems with attributes, timestamps, or security descriptors.  */
1997         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node)
1998         {
1999                 ret = apply_metadata_to_file(dentry, ctx);
2000                 if (ret)
2001                         return ret;
2002         }
2003         return 0;
2004 }
2005
2006 /* Issue warnings about problems during the extraction for which warnings were
2007  * not already issued (due to the high number of potential warnings if we issued
2008  * them per-file).  */
2009 static void
2010 do_warnings(const struct win32_apply_ctx *ctx)
2011 {
2012         if (ctx->partial_security_descriptors == 0 &&
2013             ctx->no_security_descriptors == 0)
2014                 return;
2015
2016         WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
2017                 ctx->common.target);
2018         if (ctx->partial_security_descriptors != 0) {
2019                 WARNING("- Could only partially set the security descriptor\n"
2020                         "            on %lu files or directories.",
2021                         ctx->partial_security_descriptors);
2022         }
2023         if (ctx->no_security_descriptors != 0) {
2024                 WARNING("- Could not set security descriptor at all\n"
2025                         "            on %lu files or directories.",
2026                         ctx->no_security_descriptors);
2027         }
2028         WARNING("To fully restore all security descriptors, run the program\n"
2029                 "          with Administrator rights.");
2030 }
2031
2032 /* Extract files from a WIM image to a directory on Windows  */
2033 static int
2034 win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
2035 {
2036         int ret;
2037         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
2038
2039         ret = prepare_target(dentry_list, ctx);
2040         if (ret)
2041                 goto out;
2042
2043         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
2044                 ret = start_wimboot_extraction(ctx);
2045                 if (ret)
2046                         goto out;
2047         }
2048
2049         ret = create_directories(dentry_list, ctx);
2050         if (ret)
2051                 goto out;
2052
2053         ret = create_nondirectories(dentry_list, ctx);
2054         if (ret)
2055                 goto out;
2056
2057         struct read_stream_list_callbacks cbs = {
2058                 .begin_stream      = begin_extract_stream,
2059                 .begin_stream_ctx  = ctx,
2060                 .consume_chunk     = extract_chunk,
2061                 .consume_chunk_ctx = ctx,
2062                 .end_stream        = end_extract_stream,
2063                 .end_stream_ctx    = ctx,
2064         };
2065         ret = extract_stream_list(&ctx->common, &cbs);
2066         if (ret)
2067                 goto out;
2068
2069         ret = apply_metadata(dentry_list, ctx);
2070         if (ret)
2071                 goto out;
2072
2073         do_warnings(ctx);
2074 out:
2075         if (ctx->h_target)
2076                 (*func_NtClose)(ctx->h_target);
2077         if (ctx->target_ntpath.Buffer)
2078                 HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
2079         FREE(ctx->pathbuf.Buffer);
2080         FREE(ctx->print_buffer);
2081         if (ctx->wimboot.prepopulate_pats) {
2082                 FREE(ctx->wimboot.prepopulate_pats->strings);
2083                 FREE(ctx->wimboot.prepopulate_pats);
2084         }
2085         FREE(ctx->wimboot.mem_prepopulate_pats);
2086         FREE(ctx->data_buffer);
2087         return ret;
2088 }
2089
2090 const struct apply_operations win32_apply_ops = {
2091         .name                   = "Windows",
2092         .get_supported_features = win32_get_supported_features,
2093         .extract                = win32_extract,
2094         .context_size           = sizeof(struct win32_apply_ctx),
2095 };