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