]> wimlib.net Git - wimlib/blob - src/win32_apply.c
A few comment fixes
[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->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->d_parent) && will_extract_dentry(d->d_parent);
430              d = 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 /*
989  * Creates the nondirectory file named by @dentry.
990  *
991  * On success, returns an open handle to the file in @h_ret, with GENERIC_READ,
992  * GENERIC_WRITE, and DELETE access.  Also, the path to the file will be saved
993  * in ctx->pathbuf.  On failure, returns WIMLIB_ERR_OPEN.
994  */
995 static int
996 create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
997                           struct win32_apply_ctx *ctx)
998 {
999         const struct wim_inode *inode;
1000         ULONG attrib;
1001         NTSTATUS status;
1002         bool retried = false;
1003
1004         inode = dentry->d_inode;
1005
1006         /* If the file already exists and has FILE_ATTRIBUTE_SYSTEM and/or
1007          * FILE_ATTRIBUTE_HIDDEN, these must be specified in order to supersede
1008          * the file.
1009          *
1010          * Normally the user shouldn't be trying to overwrite such files anyway,
1011          * but we at least provide FILE_ATTRIBUTE_SYSTEM and
1012          * FILE_ATTRIBUTE_HIDDEN if the WIM inode has those attributes so that
1013          * we catch the case where the user extracts the same files to the same
1014          * location more than one time.
1015          *
1016          * Also specify FILE_ATTRIBUTE_ENCRYPTED if the file needs to be
1017          * encrypted.
1018          *
1019          * In NO_ATTRIBUTES mode just don't specify any attributes at all.
1020          */
1021         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) {
1022                 attrib = 0;
1023         } else {
1024                 attrib = (inode->i_attributes & (FILE_ATTRIBUTE_SYSTEM |
1025                                                  FILE_ATTRIBUTE_HIDDEN |
1026                                                  FILE_ATTRIBUTE_ENCRYPTED));
1027         }
1028         build_extraction_path(dentry, ctx);
1029 retry:
1030         status = do_create_file(h_ret, GENERIC_READ | GENERIC_WRITE | DELETE,
1031                                 NULL, attrib, FILE_SUPERSEDE,
1032                                 FILE_NON_DIRECTORY_FILE, ctx);
1033         if (NT_SUCCESS(status)) {
1034                 int ret;
1035
1036                 ret = adjust_compression_attribute(*h_ret, dentry, ctx);
1037                 if (ret) {
1038                         (*func_NtClose)(*h_ret);
1039                         return ret;
1040                 }
1041
1042                 ret = maybe_clear_encryption_attribute(h_ret, dentry, ctx);
1043                 /* May close the handle!!! */
1044
1045                 if (ret) {
1046                         if (*h_ret)
1047                                 (*func_NtClose)(*h_ret);
1048                         return ret;
1049                 }
1050
1051                 if (!*h_ret) {
1052                         /* Re-open the handle so that we can return it on
1053                          * success.  */
1054                         status = do_create_file(h_ret,
1055                                                 GENERIC_READ |
1056                                                         GENERIC_WRITE | DELETE,
1057                                                 NULL, 0, FILE_OPEN,
1058                                                 FILE_NON_DIRECTORY_FILE, ctx);
1059                         if (!NT_SUCCESS(status))
1060                                 goto fail;
1061                 }
1062
1063                 ret = create_any_empty_ads(dentry, ctx);
1064                 if (ret) {
1065                         (*func_NtClose)(*h_ret);
1066                         return ret;
1067                 }
1068                 return 0;
1069         }
1070
1071         if (status == STATUS_ACCESS_DENIED && !retried) {
1072                 /* We also can't supersede an existing file that has
1073                  * FILE_ATTRIBUTE_READONLY set; doing so causes NtCreateFile()
1074                  * to return STATUS_ACCESS_DENIED .  The only workaround seems
1075                  * to be to explicitly remove FILE_ATTRIBUTE_READONLY on the
1076                  * existing file, then try again.  */
1077
1078                 FILE_BASIC_INFORMATION info;
1079                 HANDLE h;
1080
1081                 status = do_create_file(&h, FILE_WRITE_ATTRIBUTES, NULL, 0,
1082                                         FILE_OPEN, FILE_NON_DIRECTORY_FILE, ctx);
1083                 if (!NT_SUCCESS(status))
1084                         goto fail;
1085
1086                 memset(&info, 0, sizeof(info));
1087                 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
1088
1089                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1090                                                       &info, sizeof(info),
1091                                                       FileBasicInformation);
1092                 (*func_NtClose)(h);
1093                 if (!NT_SUCCESS(status))
1094                         goto fail;
1095                 retried = true;
1096                 goto retry;
1097         }
1098 fail:
1099         set_errno_from_nt_status(status);
1100         ERROR_WITH_ERRNO("Can't create file \"%ls\" (status=0x%08"PRIx32")",
1101                          current_path(ctx), (u32)status);
1102         return WIMLIB_ERR_OPEN;
1103 }
1104
1105 /* Creates a hard link at the location named by @dentry to the file represented
1106  * by the open handle @h.  Or, if the target volume does not support hard links,
1107  * create a separate file instead.  */
1108 static int
1109 create_link(HANDLE h, const struct wim_dentry *dentry,
1110             struct win32_apply_ctx *ctx)
1111 {
1112         if (ctx->common.supported_features.hard_links) {
1113
1114                 build_extraction_path(dentry, ctx);
1115
1116                 size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
1117                                  ctx->pathbuf.Length + sizeof(wchar_t);
1118                 u8 buf[bufsize] _aligned_attribute(8);
1119                 FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
1120                 NTSTATUS status;
1121
1122                 info->ReplaceIfExists = TRUE;
1123                 info->RootDirectory = ctx->attr.RootDirectory;
1124                 info->FileNameLength = ctx->pathbuf.Length;
1125                 memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
1126                 info->FileName[info->FileNameLength / 2] = L'\0';
1127
1128                 /* Note: the null terminator isn't actually necessary,
1129                  * but if you don't add the extra character, you get
1130                  * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
1131                  * happens to be 2  */
1132
1133                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1134                                                       info, bufsize,
1135                                                       FileLinkInformation);
1136                 if (NT_SUCCESS(status))
1137                         return 0;
1138                 ERROR("Failed to create link \"%ls\" (status=0x%08"PRIx32")",
1139                       current_path(ctx), (u32)status);
1140                 return WIMLIB_ERR_LINK;
1141         } else {
1142                 HANDLE h2;
1143                 int ret;
1144
1145                 ret = create_nondirectory_inode(&h2, dentry, ctx);
1146                 if (ret)
1147                         return ret;
1148
1149                 (*func_NtClose)(h2);
1150                 return 0;
1151         }
1152 }
1153
1154 /* Given an inode (represented by the open handle @h) for which one link has
1155  * been created (named by @first_dentry), create the other links.
1156  *
1157  * Or, if the target volume does not support hard links, create separate files.
1158  *
1159  * Note: This uses ctx->pathbuf and does not reset it.
1160  */
1161 static int
1162 create_links(HANDLE h, const struct wim_dentry *first_dentry,
1163              struct win32_apply_ctx *ctx)
1164 {
1165         const struct wim_inode *inode;
1166         const struct list_head *next;
1167         const struct wim_dentry *dentry;
1168         int ret;
1169
1170         inode = first_dentry->d_inode;
1171         next = inode->i_extraction_aliases.next;
1172         do {
1173                 dentry = list_entry(next, struct wim_dentry,
1174                                     d_extraction_alias_node);
1175                 if (dentry != first_dentry) {
1176                         ret = create_link(h, dentry, ctx);
1177                         if (ret)
1178                                 return ret;
1179                 }
1180                 next = next->next;
1181         } while (next != &inode->i_extraction_aliases);
1182         return 0;
1183 }
1184
1185 /* Create a nondirectory file, including all links.  */
1186 static int
1187 create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx)
1188 {
1189         const struct wim_dentry *first_dentry;
1190         HANDLE h;
1191         int ret;
1192
1193         first_dentry = first_extraction_alias(inode);
1194
1195         /* Create first link.  */
1196         ret = create_nondirectory_inode(&h, first_dentry, ctx);
1197         if (ret)
1198                 return ret;
1199
1200         /* Set short name.  */
1201         ret = set_short_name(h, first_dentry, ctx);
1202
1203         /* Create additional links, OR if hard links are not supported just
1204          * create more files.  */
1205         if (!ret)
1206                 ret = create_links(h, first_dentry, ctx);
1207
1208         (*func_NtClose)(h);
1209         return ret;
1210 }
1211
1212 /* Create all the nondirectory files being extracted, including all aliases
1213  * (hard links).  */
1214 static int
1215 create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1216 {
1217         const struct wim_dentry *dentry;
1218         const struct wim_inode *inode;
1219         int ret;
1220
1221         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1222                 inode = dentry->d_inode;
1223                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1224                         continue;
1225                 /* Call create_nondirectory() only once per inode  */
1226                 if (dentry != inode_first_extraction_dentry(inode))
1227                         continue;
1228                 ret = create_nondirectory(inode, ctx);
1229                 if (ret)
1230                         return ret;
1231         }
1232         return 0;
1233 }
1234
1235 static void
1236 close_handles(struct win32_apply_ctx *ctx)
1237 {
1238         for (unsigned i = 0; i < ctx->num_open_handles; i++)
1239                 (*func_NtClose)(ctx->open_handles[i]);
1240 }
1241
1242 /* Prepare to read the next stream, which has size @stream_size, into an
1243  * in-memory buffer.  */
1244 static int
1245 prepare_data_buffer(struct win32_apply_ctx *ctx, u64 stream_size)
1246 {
1247         if (stream_size > ctx->data_buffer_size) {
1248                 /* Larger buffer needed.  */
1249                 void *new_buffer;
1250                 if ((size_t)stream_size != stream_size)
1251                         return WIMLIB_ERR_NOMEM;
1252                 new_buffer = REALLOC(ctx->data_buffer, stream_size);
1253                 if (!new_buffer)
1254                         return WIMLIB_ERR_NOMEM;
1255                 ctx->data_buffer = new_buffer;
1256                 ctx->data_buffer_size = stream_size;
1257         }
1258         /* On the first call this changes data_buffer_ptr from NULL, which tells
1259          * extract_chunk() that the data buffer needs to be filled while reading
1260          * the stream data.  */
1261         ctx->data_buffer_ptr = ctx->data_buffer;
1262         return 0;
1263 }
1264
1265 static int
1266 begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
1267                               struct wim_dentry *dentry,
1268                               const wchar_t *stream_name,
1269                               struct win32_apply_ctx *ctx)
1270 {
1271         const struct wim_inode *inode = dentry->d_inode;
1272         size_t stream_name_nchars = 0;
1273         FILE_ALLOCATION_INFORMATION alloc_info;
1274         HANDLE h;
1275         NTSTATUS status;
1276
1277         if (unlikely(stream_name))
1278                 stream_name_nchars = wcslen(stream_name);
1279
1280         if (unlikely(stream_name_nchars)) {
1281                 build_extraction_path_with_ads(dentry, ctx,
1282                                                stream_name, stream_name_nchars);
1283         } else {
1284                 build_extraction_path(dentry, ctx);
1285         }
1286
1287         /* Reparse point?  */
1288         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
1289             && (stream_name_nchars == 0))
1290         {
1291                 if (!ctx->common.supported_features.reparse_points)
1292                         return 0;
1293
1294                 /* We can't write the reparse stream directly; we must set it
1295                  * with FSCTL_SET_REPARSE_POINT, which requires that all the
1296                  * data be available.  So, stage the data in a buffer.  */
1297
1298                 list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
1299                 return prepare_data_buffer(ctx, stream->size);
1300         }
1301
1302         /* Encrypted file?  */
1303         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
1304             && (stream_name_nchars == 0))
1305         {
1306                 if (!ctx->common.supported_features.encrypted_files)
1307                         return 0;
1308
1309                 /* We can't write encrypted file streams directly; we must use
1310                  * WriteEncryptedFileRaw(), which requires providing the data
1311                  * through a callback function.  This can't easily be combined
1312                  * with our own callback-based approach.
1313                  *
1314                  * The current workaround is to simply read the stream into
1315                  * memory and write the encrypted file from that.
1316                  *
1317                  * TODO: This isn't sufficient for extremely large encrypted
1318                  * files.  Perhaps we should create an extra thread to write
1319                  * such files...  */
1320                 list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries);
1321                 return prepare_data_buffer(ctx, stream->size);
1322         }
1323
1324         /* Extracting unnamed data stream in WIMBoot mode?  */
1325         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)
1326             && (stream_name_nchars == 0)
1327             && (stream->resource_location == RESOURCE_IN_WIM)
1328             && (stream->rspec->wim == ctx->common.wim)
1329             && (stream->size == stream->rspec->uncompressed_size))
1330         {
1331                 int ret = calculate_dentry_full_path(dentry);
1332                 if (ret)
1333                         return ret;
1334                 if (in_prepopulate_list(dentry, ctx)) {
1335                         union wimlib_progress_info info;
1336
1337                         info.wimboot_exclude.path_in_wim = dentry->_full_path;
1338                         info.wimboot_exclude.extraction_path = current_path(ctx);
1339
1340                         ret = call_progress(ctx->common.progfunc,
1341                                             WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
1342                                             &info, ctx->common.progctx);
1343                         FREE(dentry->_full_path);
1344                         dentry->_full_path = NULL;
1345                         if (ret)
1346                                 return ret;
1347                         /* Go on and open the file for normal extraction.  */
1348                 } else {
1349                         FREE(dentry->_full_path);
1350                         dentry->_full_path = NULL;
1351                         return wimboot_set_pointer(&ctx->attr,
1352                                                    current_path(ctx),
1353                                                    stream,
1354                                                    ctx->wimboot.data_source_id,
1355                                                    ctx->wimboot.wim_lookup_table_hash,
1356                                                    ctx->wimboot.wof_running);
1357                 }
1358         }
1359
1360         /* Too many open handles?  */
1361         if (ctx->num_open_handles == MAX_OPEN_HANDLES) {
1362                 ERROR("Can't extract data: too many open files!");
1363                 return WIMLIB_ERR_UNSUPPORTED;
1364         }
1365
1366         /* Open a new handle  */
1367         status = do_create_file(&h,
1368                                 FILE_WRITE_DATA | SYNCHRONIZE,
1369                                 NULL, 0, FILE_OPEN_IF,
1370                                 FILE_SEQUENTIAL_ONLY |
1371                                         FILE_SYNCHRONOUS_IO_NONALERT,
1372                                 ctx);
1373         if (!NT_SUCCESS(status)) {
1374                 set_errno_from_nt_status(status);
1375                 ERROR_WITH_ERRNO("Can't open \"%ls\" for writing "
1376                                  "(status=0x%08"PRIx32")",
1377                                  current_path(ctx), (u32)status);
1378                 return WIMLIB_ERR_OPEN;
1379         }
1380
1381         ctx->open_handles[ctx->num_open_handles++] = h;
1382
1383         /* Allocate space for the data.  */
1384         alloc_info.AllocationSize.QuadPart = stream->size;
1385         (*func_NtSetInformationFile)(h, &ctx->iosb,
1386                                      &alloc_info, sizeof(alloc_info),
1387                                      FileAllocationInformation);
1388         return 0;
1389 }
1390
1391 /* Set the reparse data @rpbuf of length @rpbuflen on the extracted file
1392  * corresponding to the WIM dentry @dentry.  */
1393 static int
1394 do_set_reparse_data(const struct wim_dentry *dentry,
1395                     const void *rpbuf, u16 rpbuflen,
1396                     struct win32_apply_ctx *ctx)
1397 {
1398         NTSTATUS status;
1399         HANDLE h;
1400
1401         status = create_file(&h, GENERIC_WRITE, NULL,
1402                              0, FILE_OPEN, 0, dentry, ctx);
1403         if (!NT_SUCCESS(status))
1404                 goto fail;
1405
1406         status = (*func_NtFsControlFile)(h, NULL, NULL, NULL,
1407                                          &ctx->iosb, FSCTL_SET_REPARSE_POINT,
1408                                          (void *)rpbuf, rpbuflen,
1409                                          NULL, 0);
1410         (*func_NtClose)(h);
1411
1412         if (NT_SUCCESS(status))
1413                 return 0;
1414
1415         /* On Windows, by default only the Administrator can create symbolic
1416          * links for some reason.  By default we just issue a warning if this
1417          * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
1418          * to get a hard error.  */
1419         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
1420             && (status == STATUS_PRIVILEGE_NOT_HELD ||
1421                 status == STATUS_ACCESS_DENIED)
1422             && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1423                 dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1424         {
1425                 WARNING("Can't create symbolic link \"%ls\"!              \n"
1426                         "          (Need Administrator rights, or at least "
1427                         "the\n"
1428                         "          SeCreateSymbolicLink privilege.)",
1429                         current_path(ctx));
1430                 return 0;
1431         }
1432
1433 fail:
1434         set_errno_from_nt_status(status);
1435         ERROR_WITH_ERRNO("Can't set reparse data on \"%ls\" "
1436                          "(status=0x%08"PRIx32")",
1437                          current_path(ctx), (u32)status);
1438         return WIMLIB_ERR_SET_REPARSE_DATA;
1439 }
1440
1441 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1442  * pointer to the suffix of the path that begins with the device directly, such
1443  * as e:\Windows\System32.  */
1444 static const wchar_t *
1445 skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
1446 {
1447         static const wchar_t * const dirs[] = {
1448                 L"\\??\\",
1449                 L"\\DosDevices\\",
1450                 L"\\Device\\",
1451         };
1452         size_t first_dir_len = 0;
1453         const wchar_t * const end = path + path_nchars;
1454
1455         for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
1456                 size_t len = wcslen(dirs[i]);
1457                 if (len <= (end - path) && !wcsnicmp(path, dirs[i], len)) {
1458                         first_dir_len = len;
1459                         break;
1460                 }
1461         }
1462         if (first_dir_len == 0)
1463                 return path;
1464         path += first_dir_len;
1465         while (path != end && *path == L'\\')
1466                 path++;
1467         return path;
1468 }
1469
1470 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1471  * pointer to the suffix of the path that is device-relative, such as
1472  * Windows\System32.  */
1473 static const wchar_t *
1474 get_device_relative_path(const wchar_t *path, size_t path_nchars)
1475 {
1476         const wchar_t * const orig_path = path;
1477         const wchar_t * const end = path + path_nchars;
1478
1479         path = skip_nt_toplevel_component(path, path_nchars);
1480         if (path == orig_path)
1481                 return orig_path;
1482
1483         path = wmemchr(path, L'\\', (end - path));
1484         if (!path)
1485                 return orig_path;
1486         do {
1487                 path++;
1488         } while (path != end && *path == L'\\');
1489         return path;
1490 }
1491
1492 /*
1493  * Given a reparse point buffer for a symbolic link or junction, adjust its
1494  * contents so that the target of the link is consistent with the new location
1495  * of the files.
1496  */
1497 static void
1498 try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, struct win32_apply_ctx *ctx)
1499 {
1500         struct reparse_data rpdata;
1501         size_t orig_subst_name_nchars;
1502         const wchar_t *relpath;
1503         size_t relpath_nchars;
1504         size_t target_ntpath_nchars;
1505         size_t fixed_subst_name_nchars;
1506         const wchar_t *fixed_print_name;
1507         size_t fixed_print_name_nchars;
1508
1509         if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
1510                 /* Do nothing if the reparse data is invalid.  */
1511                 return;
1512         }
1513
1514         if (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
1515             (rpdata.rpflags & SYMBOLIC_LINK_RELATIVE))
1516         {
1517                 /* Do nothing if it's a relative symbolic link.  */
1518                 return;
1519         }
1520
1521         /* Build the new substitute name from the NT namespace path to the
1522          * target directory, then a path separator, then the "device relative"
1523          * part of the old substitute name.  */
1524
1525         orig_subst_name_nchars = rpdata.substitute_name_nbytes / sizeof(wchar_t);
1526
1527         relpath = get_device_relative_path(rpdata.substitute_name,
1528                                            orig_subst_name_nchars);
1529         relpath_nchars = orig_subst_name_nchars -
1530                          (relpath - rpdata.substitute_name);
1531
1532         target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
1533
1534         fixed_subst_name_nchars = target_ntpath_nchars + 1 + relpath_nchars;
1535         wchar_t fixed_subst_name[fixed_subst_name_nchars];
1536
1537         wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer,
1538                 target_ntpath_nchars);
1539         fixed_subst_name[target_ntpath_nchars] = L'\\';
1540         wmemcpy(&fixed_subst_name[target_ntpath_nchars + 1],
1541                 relpath, relpath_nchars);
1542         /* Doesn't need to be null-terminated.  */
1543
1544         /* Print name should be Win32, but not all NT names can even be
1545          * translated to Win32 names.  But we can at least delete the top-level
1546          * directory, such as \??\, and this will have the expected result in
1547          * the usual case.  */
1548         fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
1549                                                       fixed_subst_name_nchars);
1550         fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
1551                                                              fixed_subst_name);
1552
1553         rpdata.substitute_name = fixed_subst_name;
1554         rpdata.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
1555         rpdata.print_name = (wchar_t *)fixed_print_name;
1556         rpdata.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
1557         make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
1558 }
1559
1560 /* Sets reparse data on the specified file.  This handles "fixing" the targets
1561  * of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX was
1562  * specified.  */
1563 static int
1564 set_reparse_data(const struct wim_dentry *dentry,
1565                  const void *_rpbuf, u16 rpbuflen, struct win32_apply_ctx *ctx)
1566 {
1567         const struct wim_inode *inode = dentry->d_inode;
1568         const void *rpbuf = _rpbuf;
1569
1570         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
1571             && !inode->i_not_rpfixed
1572             && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1573                 inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1574         {
1575                 memcpy(&ctx->rpfixbuf, _rpbuf, rpbuflen);
1576                 try_rpfix((u8 *)&ctx->rpfixbuf, &rpbuflen, ctx);
1577                 rpbuf = &ctx->rpfixbuf;
1578         }
1579         return do_set_reparse_data(dentry, rpbuf, rpbuflen, ctx);
1580
1581 }
1582
1583 /* Import the next block of raw encrypted data  */
1584 static DWORD WINAPI
1585 import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
1586 {
1587         struct win32_apply_ctx *ctx = pvCallbackContext;
1588         ULONG copy_len;
1589
1590         copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
1591         memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
1592         ctx->encrypted_offset += copy_len;
1593         *Length = copy_len;
1594         return ERROR_SUCCESS;
1595 }
1596
1597 /* Write the raw encrypted data to the already-created file corresponding to
1598  * @dentry.
1599  *
1600  * The raw encrypted data is provided in ctx->data_buffer, and its size is
1601  * ctx->encrypted_size.  */
1602 static int
1603 extract_encrypted_file(const struct wim_dentry *dentry,
1604                        struct win32_apply_ctx *ctx)
1605 {
1606         void *rawctx;
1607         DWORD err;
1608
1609         /* Temporarily build a Win32 path for OpenEncryptedFileRaw()  */
1610         build_win32_extraction_path(dentry, ctx);
1611
1612         err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer,
1613                                    CREATE_FOR_IMPORT, &rawctx);
1614
1615         /* Restore the NT namespace path  */
1616         build_extraction_path(dentry, ctx);
1617
1618         if (err != ERROR_SUCCESS) {
1619                 set_errno_from_win32_error(err);
1620                 ERROR_WITH_ERRNO("Can't open \"%ls\" for encrypted import "
1621                                  "(err=%"PRIu32")", current_path(ctx), (u32)err);
1622                 return WIMLIB_ERR_OPEN;
1623         }
1624
1625         ctx->encrypted_offset = 0;
1626
1627         err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx);
1628
1629         CloseEncryptedFileRaw(rawctx);
1630
1631         if (err != ERROR_SUCCESS) {
1632                 set_errno_from_win32_error(err);
1633                 ERROR_WITH_ERRNO("Can't import encrypted file \"%ls\" "
1634                                  "(err=%"PRIu32")", current_path(ctx), (u32)err);
1635                 return WIMLIB_ERR_WRITE;
1636         }
1637
1638         return 0;
1639 }
1640
1641 /* Called when starting to read a stream for extraction on Windows  */
1642 static int
1643 begin_extract_stream(struct wim_lookup_table_entry *stream,
1644                      u32 flags, void *_ctx)
1645 {
1646         struct win32_apply_ctx *ctx = _ctx;
1647         const struct stream_owner *owners = stream_owners(stream);
1648         int ret;
1649
1650         ctx->num_open_handles = 0;
1651         ctx->data_buffer_ptr = NULL;
1652         INIT_LIST_HEAD(&ctx->reparse_dentries);
1653         INIT_LIST_HEAD(&ctx->encrypted_dentries);
1654
1655         for (u32 i = 0; i < stream->out_refcnt; i++) {
1656                 const struct wim_inode *inode = owners[i].inode;
1657                 const wchar_t *stream_name = owners[i].stream_name;
1658                 struct wim_dentry *dentry;
1659
1660                 /* A copy of the stream needs to be extracted to @inode.  */
1661
1662                 if (ctx->common.supported_features.hard_links) {
1663                         dentry = inode_first_extraction_dentry(inode);
1664                         ret = begin_extract_stream_instance(stream, dentry,
1665                                                             stream_name, ctx);
1666                         if (ret)
1667                                 goto fail;
1668                 } else {
1669                         /* Hard links not supported.  Extract the stream
1670                          * separately to each alias of the inode.  */
1671                         struct list_head *next;
1672
1673                         next = inode->i_extraction_aliases.next;
1674                         do {
1675                                 dentry = list_entry(next, struct wim_dentry,
1676                                                     d_extraction_alias_node);
1677                                 ret = begin_extract_stream_instance(stream,
1678                                                                     dentry,
1679                                                                     stream_name,
1680                                                                     ctx);
1681                                 if (ret)
1682                                         goto fail;
1683                                 next = next->next;
1684                         } while (next != &inode->i_extraction_aliases);
1685                 }
1686         }
1687
1688         if (unlikely(ctx->num_open_handles == 0 && ctx->data_buffer_ptr == NULL)) {
1689                 /* The data of this stream isn't actually needed!
1690                  * (This can happen in WIMBoot mode.)  */
1691                 return BEGIN_STREAM_STATUS_SKIP_STREAM;
1692         }
1693         return 0;
1694
1695 fail:
1696         close_handles(ctx);
1697         return ret;
1698 }
1699
1700 /* Called when the next chunk of a stream has been read for extraction on
1701  * Windows  */
1702 static int
1703 extract_chunk(const void *chunk, size_t size, void *_ctx)
1704 {
1705         struct win32_apply_ctx *ctx = _ctx;
1706
1707         /* Write the data chunk to each open handle  */
1708         for (unsigned i = 0; i < ctx->num_open_handles; i++) {
1709                 u8 *bufptr = (u8 *)chunk;
1710                 size_t bytes_remaining = size;
1711                 NTSTATUS status;
1712                 while (bytes_remaining) {
1713                         ULONG count = min(0xFFFFFFFF, bytes_remaining);
1714
1715                         status = (*func_NtWriteFile)(ctx->open_handles[i],
1716                                                      NULL, NULL, NULL,
1717                                                      &ctx->iosb, bufptr, count,
1718                                                      NULL, NULL);
1719                         if (!NT_SUCCESS(status)) {
1720                                 set_errno_from_nt_status(status);
1721                                 ERROR_WITH_ERRNO("Error writing data to target "
1722                                                  "volume (status=0x%08"PRIx32")",
1723                                                  (u32)status);
1724                                 return WIMLIB_ERR_WRITE;
1725                         }
1726                         bufptr += ctx->iosb.Information;
1727                         bytes_remaining -= ctx->iosb.Information;
1728                 }
1729         }
1730
1731         /* Copy the data chunk into the buffer (if needed)  */
1732         if (ctx->data_buffer_ptr)
1733                 ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
1734                                                chunk, size);
1735         return 0;
1736 }
1737
1738 /* Called when a stream has been fully read for extraction on Windows  */
1739 static int
1740 end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx)
1741 {
1742         struct win32_apply_ctx *ctx = _ctx;
1743         int ret;
1744         const struct wim_dentry *dentry;
1745
1746         close_handles(ctx);
1747
1748         if (status)
1749                 return status;
1750
1751         if (likely(!ctx->data_buffer_ptr))
1752                 return 0;
1753
1754         if (!list_empty(&ctx->reparse_dentries)) {
1755                 if (stream->size > REPARSE_DATA_MAX_SIZE) {
1756                         dentry = list_first_entry(&ctx->reparse_dentries,
1757                                                   struct wim_dentry, tmp_list);
1758                         build_extraction_path(dentry, ctx);
1759                         ERROR("Reparse data of \"%ls\" has size "
1760                               "%"PRIu64" bytes (exceeds %u bytes)",
1761                               current_path(ctx), stream->size,
1762                               REPARSE_DATA_MAX_SIZE);
1763                         return WIMLIB_ERR_INVALID_REPARSE_DATA;
1764                 }
1765                 /* In the WIM format, reparse streams are just the reparse data
1766                  * and omit the header.  But we can reconstruct the header.  */
1767                 memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, stream->size);
1768                 ctx->rpbuf.rpdatalen = stream->size;
1769                 ctx->rpbuf.rpreserved = 0;
1770                 list_for_each_entry(dentry, &ctx->reparse_dentries, tmp_list) {
1771                         ctx->rpbuf.rptag = dentry->d_inode->i_reparse_tag;
1772                         ret = set_reparse_data(dentry, &ctx->rpbuf,
1773                                                stream->size + REPARSE_DATA_OFFSET,
1774                                                ctx);
1775                         if (ret)
1776                                 return ret;
1777                 }
1778         }
1779
1780         if (!list_empty(&ctx->encrypted_dentries)) {
1781                 ctx->encrypted_size = stream->size;
1782                 list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) {
1783                         ret = extract_encrypted_file(dentry, ctx);
1784                         if (ret)
1785                                 return ret;
1786                 }
1787         }
1788
1789         return 0;
1790 }
1791
1792 /* Attributes that can't be set directly  */
1793 #define SPECIAL_ATTRIBUTES                      \
1794         (FILE_ATTRIBUTE_REPARSE_POINT   |       \
1795          FILE_ATTRIBUTE_DIRECTORY       |       \
1796          FILE_ATTRIBUTE_ENCRYPTED       |       \
1797          FILE_ATTRIBUTE_SPARSE_FILE     |       \
1798          FILE_ATTRIBUTE_COMPRESSED)
1799
1800 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
1801  * handle @h.  */
1802 static NTSTATUS
1803 set_security_descriptor(HANDLE h, const void *desc,
1804                         size_t desc_size, struct win32_apply_ctx *ctx)
1805 {
1806         SECURITY_INFORMATION info;
1807         NTSTATUS status;
1808
1809         /* We really just want to set entire the security descriptor as-is, but
1810          * all available APIs require specifying the specific parts of the
1811          * descriptor being set.  Start out by requesting all parts be set.  If
1812          * permissions problems are encountered, fall back to omitting some
1813          * parts (first the SACL, then the DACL, then the owner), unless the
1814          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS flag has been enabled.  */
1815         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
1816                DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION;
1817
1818         /* Prefer NtSetSecurityObject() to SetFileSecurity().  SetFileSecurity()
1819          * itself necessarily uses NtSetSecurityObject() as the latter is the
1820          * underlying system call for setting security information, but
1821          * SetFileSecurity() opens the handle with NtCreateFile() without
1822          * FILE_OPEN_FILE_BACKUP_INTENT.  Hence, access checks are done and due
1823          * to the Windows security model, even a process running as the
1824          * Administrator can have access denied.  (Of course, this not mentioned
1825          * in the MS "documentation".)  */
1826 retry:
1827         status = (*func_NtSetSecurityObject)(h, info, (PSECURITY_DESCRIPTOR)desc);
1828         if (NT_SUCCESS(status))
1829                 return status;
1830         /* Failed to set the requested parts of the security descriptor.  If the
1831          * error was permissions-related, try to set fewer parts of the security
1832          * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
1833         if ((status == STATUS_PRIVILEGE_NOT_HELD ||
1834              status == STATUS_ACCESS_DENIED) &&
1835             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
1836         {
1837                 if (info & SACL_SECURITY_INFORMATION) {
1838                         info &= ~SACL_SECURITY_INFORMATION;
1839                         ctx->partial_security_descriptors++;
1840                         goto retry;
1841                 }
1842                 if (info & DACL_SECURITY_INFORMATION) {
1843                         info &= ~DACL_SECURITY_INFORMATION;
1844                         goto retry;
1845                 }
1846                 if (info & OWNER_SECURITY_INFORMATION) {
1847                         info &= ~OWNER_SECURITY_INFORMATION;
1848                         goto retry;
1849                 }
1850                 /* Nothing left except GROUP, and if we removed it we
1851                  * wouldn't have anything at all.  */
1852         }
1853
1854         /* No part of the security descriptor could be set, or
1855          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security
1856          * descriptor could not be set.  */
1857         if (!(info & SACL_SECURITY_INFORMATION))
1858                 ctx->partial_security_descriptors--;
1859         ctx->no_security_descriptors++;
1860         return status;
1861 }
1862
1863 /* Set metadata on the open file @h from the WIM inode @inode.  */
1864 static int
1865 do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
1866                           struct win32_apply_ctx *ctx)
1867 {
1868         FILE_BASIC_INFORMATION info;
1869         NTSTATUS status;
1870
1871         /* Set security descriptor if present and not in NO_ACLS mode  */
1872         if (inode->i_security_id >= 0 &&
1873             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
1874         {
1875                 const struct wim_security_data *sd;
1876                 const void *desc;
1877                 size_t desc_size;
1878
1879                 sd = wim_get_current_security_data(ctx->common.wim);
1880                 desc = sd->descriptors[inode->i_security_id];
1881                 desc_size = sd->sizes[inode->i_security_id];
1882
1883                 status = set_security_descriptor(h, desc, desc_size, ctx);
1884                 if (!NT_SUCCESS(status) &&
1885                     (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
1886                 {
1887                         set_errno_from_nt_status(status);
1888                         ERROR_WITH_ERRNO("Can't set security descriptor "
1889                                          "on \"%ls\" (status=0x%08"PRIx32")",
1890                                          current_path(ctx), (u32)status);
1891                         return WIMLIB_ERR_SET_SECURITY;
1892                 }
1893         }
1894
1895         /* Set attributes and timestamps  */
1896         info.CreationTime.QuadPart = inode->i_creation_time;
1897         info.LastAccessTime.QuadPart = inode->i_last_access_time;
1898         info.LastWriteTime.QuadPart = inode->i_last_write_time;
1899         info.ChangeTime.QuadPart = 0;
1900         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
1901                 info.FileAttributes = 0;
1902         else
1903                 info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES;
1904
1905         status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1906                                               &info, sizeof(info),
1907                                               FileBasicInformation);
1908         /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set
1909          * attributes on the root directory.  (Apparently because FAT doesn't
1910          * actually have a place to store those attributes!)  */
1911         if (!NT_SUCCESS(status)
1912             && !(status == STATUS_INVALID_PARAMETER &&
1913                  dentry_is_root(inode_first_extraction_dentry(inode))))
1914         {
1915                 set_errno_from_nt_status(status);
1916                 ERROR_WITH_ERRNO("Can't set basic metadata on \"%ls\" "
1917                                  "(status=0x%08"PRIx32")",
1918                                  current_path(ctx), (u32)status);
1919                 return WIMLIB_ERR_SET_ATTRIBUTES;
1920         }
1921
1922         return 0;
1923 }
1924
1925 static int
1926 apply_metadata_to_file(const struct wim_dentry *dentry,
1927                        struct win32_apply_ctx *ctx)
1928 {
1929         const struct wim_inode *inode = dentry->d_inode;
1930         DWORD perms;
1931         HANDLE h;
1932         NTSTATUS status;
1933         int ret;
1934
1935         perms = FILE_WRITE_ATTRIBUTES | WRITE_DAC |
1936                 WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
1937
1938         build_extraction_path(dentry, ctx);
1939
1940         /* Open a handle with as many relevant permissions as possible.  */
1941         while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL,
1942                                                    0, FILE_OPEN, 0, ctx)))
1943         {
1944                 if (status == STATUS_PRIVILEGE_NOT_HELD ||
1945                     status == STATUS_ACCESS_DENIED)
1946                 {
1947                         if (perms & ACCESS_SYSTEM_SECURITY) {
1948                                 perms &= ~ACCESS_SYSTEM_SECURITY;
1949                                 continue;
1950                         }
1951                         if (perms & WRITE_DAC) {
1952                                 perms &= ~WRITE_DAC;
1953                                 continue;
1954                         }
1955                         if (perms & WRITE_OWNER) {
1956                                 perms &= ~WRITE_OWNER;
1957                                 continue;
1958                         }
1959                 }
1960                 set_errno_from_nt_status(status);
1961                 ERROR_WITH_ERRNO("Can't open \"%ls\" to set metadata "
1962                                  "(status=0x%08"PRIx32")",
1963                                  current_path(ctx), (u32)status);
1964                 return WIMLIB_ERR_OPEN;
1965         }
1966
1967         ret = do_apply_metadata_to_file(h, inode, ctx);
1968
1969         (*func_NtClose)(h);
1970
1971         return ret;
1972 }
1973
1974 static int
1975 apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1976 {
1977         const struct wim_dentry *dentry;
1978         int ret;
1979
1980         /* We go in reverse so that metadata is set on all a directory's
1981          * children before the directory itself.  This avoids any potential
1982          * problems with attributes, timestamps, or security descriptors.  */
1983         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node)
1984         {
1985                 ret = apply_metadata_to_file(dentry, ctx);
1986                 if (ret)
1987                         return ret;
1988         }
1989         return 0;
1990 }
1991
1992 /* Issue warnings about problems during the extraction for which warnings were
1993  * not already issued (due to the high number of potential warnings if we issued
1994  * them per-file).  */
1995 static void
1996 do_warnings(const struct win32_apply_ctx *ctx)
1997 {
1998         if (ctx->partial_security_descriptors == 0 &&
1999             ctx->no_security_descriptors == 0)
2000                 return;
2001
2002         WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
2003                 ctx->common.target);
2004         if (ctx->partial_security_descriptors != 0) {
2005                 WARNING("- Could only partially set the security descriptor\n"
2006                         "            on %lu files or directories.",
2007                         ctx->partial_security_descriptors);
2008         }
2009         if (ctx->no_security_descriptors != 0) {
2010                 WARNING("- Could not set security descriptor at all\n"
2011                         "            on %lu files or directories.",
2012                         ctx->no_security_descriptors);
2013         }
2014         WARNING("To fully restore all security descriptors, run the program\n"
2015                 "          with Administrator rights.");
2016 }
2017
2018 /* Extract files from a WIM image to a directory on Windows  */
2019 static int
2020 win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
2021 {
2022         int ret;
2023         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
2024
2025         ret = prepare_target(dentry_list, ctx);
2026         if (ret)
2027                 goto out;
2028
2029         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
2030                 ret = start_wimboot_extraction(ctx);
2031                 if (ret)
2032                         goto out;
2033         }
2034
2035         ret = create_directories(dentry_list, ctx);
2036         if (ret)
2037                 goto out;
2038
2039         ret = create_nondirectories(dentry_list, ctx);
2040         if (ret)
2041                 goto out;
2042
2043         struct read_stream_list_callbacks cbs = {
2044                 .begin_stream      = begin_extract_stream,
2045                 .begin_stream_ctx  = ctx,
2046                 .consume_chunk     = extract_chunk,
2047                 .consume_chunk_ctx = ctx,
2048                 .end_stream        = end_extract_stream,
2049                 .end_stream_ctx    = ctx,
2050         };
2051         ret = extract_stream_list(&ctx->common, &cbs);
2052         if (ret)
2053                 goto out;
2054
2055         ret = apply_metadata(dentry_list, ctx);
2056         if (ret)
2057                 goto out;
2058
2059         do_warnings(ctx);
2060 out:
2061         if (ctx->h_target)
2062                 (*func_NtClose)(ctx->h_target);
2063         if (ctx->target_ntpath.Buffer)
2064                 HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
2065         FREE(ctx->pathbuf.Buffer);
2066         FREE(ctx->print_buffer);
2067         if (ctx->wimboot.prepopulate_pats) {
2068                 FREE(ctx->wimboot.prepopulate_pats->strings);
2069                 FREE(ctx->wimboot.prepopulate_pats);
2070         }
2071         FREE(ctx->wimboot.mem_prepopulate_pats);
2072         FREE(ctx->data_buffer);
2073         return ret;
2074 }
2075
2076 const struct apply_operations win32_apply_ops = {
2077         .name                   = "Windows",
2078         .get_supported_features = win32_get_supported_features,
2079         .extract                = win32_extract,
2080         .context_size           = sizeof(struct win32_apply_ctx),
2081 };