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