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