]> wimlib.net Git - wimlib/blob - src/win32_apply.c
2926883cd343991d8dd7a6f3702beebf29b198c5
[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         status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(ctx->common.target,
521                                                                  &ctx->target_ntpath,
522                                                                  NULL, NULL);
523         if (!NT_SUCCESS(status)) {
524                 if (status == STATUS_NO_MEMORY) {
525                         return WIMLIB_ERR_NOMEM;
526                 } else {
527                         ERROR("\"%ls\": invalid path name "
528                               "(status=0x%08"PRIx32")",
529                               ctx->common.target, (u32)status);
530                         return WIMLIB_ERR_INVALID_PARAM;
531                 }
532         }
533
534         ctx->attr.Length = sizeof(ctx->attr);
535         ctx->attr.ObjectName = &ctx->target_ntpath;
536
537         status = (*func_NtCreateFile)(&ctx->h_target,
538                                       FILE_TRAVERSE,
539                                       &ctx->attr,
540                                       &ctx->iosb,
541                                       NULL,
542                                       0,
543                                       FILE_SHARE_VALID_FLAGS,
544                                       FILE_OPEN_IF,
545                                       FILE_DIRECTORY_FILE |
546                                               FILE_OPEN_REPARSE_POINT |
547                                               FILE_OPEN_FOR_BACKUP_INTENT,
548                                       NULL,
549                                       0);
550
551         if (!NT_SUCCESS(status)) {
552                 set_errno_from_nt_status(status);
553                 ERROR_WITH_ERRNO("Can't open or create directory \"%ls\" "
554                                  "(status=0x%08"PRIx32")",
555                                  ctx->common.target, (u32)status);
556                 return WIMLIB_ERR_OPENDIR;
557         }
558
559         path_max = compute_path_max(dentry_list);
560
561         /* Add some extra for building Win32 paths for the file encryption APIs
562          * ...  */
563         path_max += 2 + (ctx->target_ntpath.Length / sizeof(wchar_t));
564
565         ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t);
566         ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength);
567         if (!ctx->pathbuf.Buffer)
568                 return WIMLIB_ERR_NOMEM;
569
570         ctx->attr.RootDirectory = ctx->h_target;
571         ctx->attr.ObjectName = &ctx->pathbuf;
572
573         ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) *
574                                    sizeof(wchar_t));
575         if (!ctx->print_buffer)
576                 return WIMLIB_ERR_NOMEM;
577
578         return 0;
579 }
580
581 /* When creating an inode that will have a short (DOS) name, we create it using
582  * the long name associated with the short name.  This ensures that the short
583  * name gets associated with the correct long name.  */
584 static const struct wim_dentry *
585 first_extraction_alias(const struct wim_inode *inode)
586 {
587         const struct list_head *next = inode->i_extraction_aliases.next;
588         const struct wim_dentry *dentry;
589
590         do {
591                 dentry = list_entry(next, struct wim_dentry,
592                                     d_extraction_alias_node);
593                 if (dentry_has_short_name(dentry))
594                         break;
595                 next = next->next;
596         } while (next != &inode->i_extraction_aliases);
597         return dentry;
598 }
599
600 /*
601  * Set or clear FILE_ATTRIBUTE_COMPRESSED if the inherited value is different
602  * from the desired value.
603  *
604  * Note that you can NOT override the inherited value of
605  * FILE_ATTRIBUTE_COMPRESSED directly with NtCreateFile().
606  */
607 static int
608 adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
609                              struct win32_apply_ctx *ctx)
610 {
611         const bool compressed = (dentry->d_inode->i_attributes &
612                                  FILE_ATTRIBUTE_COMPRESSED);
613
614         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
615                 return 0;
616
617         if (!ctx->common.supported_features.compressed_files)
618                 return 0;
619
620         FILE_BASIC_INFORMATION info;
621         NTSTATUS status;
622         USHORT compression_state;
623
624         /* Get current attributes  */
625         status = (*func_NtQueryInformationFile)(h, &ctx->iosb,
626                                                 &info, sizeof(info),
627                                                 FileBasicInformation);
628         if (NT_SUCCESS(status) &&
629             compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED))
630         {
631                 /* Nothing needs to be done.  */
632                 return 0;
633         }
634
635         /* Set the new compression state  */
636
637         if (compressed)
638                 compression_state = COMPRESSION_FORMAT_DEFAULT;
639         else
640                 compression_state = COMPRESSION_FORMAT_NONE;
641
642         status = (*func_NtFsControlFile)(h,
643                                          NULL,
644                                          NULL,
645                                          NULL,
646                                          &ctx->iosb,
647                                          FSCTL_SET_COMPRESSION,
648                                          &compression_state,
649                                          sizeof(USHORT),
650                                          NULL,
651                                          0);
652         if (NT_SUCCESS(status))
653                 return 0;
654
655         set_errno_from_nt_status(status);
656         ERROR_WITH_ERRNO("Can't %s compression attribute on \"%ls\" "
657                          "(status=0x%08"PRIx32")",
658                          (compressed ? "set" : "clear"),
659                          current_path(ctx), status);
660         return WIMLIB_ERR_SET_ATTRIBUTES;
661 }
662
663 /*
664  * Clear FILE_ATTRIBUTE_ENCRYPTED if the file or directory is not supposed to be
665  * encrypted.
666  *
667  * You can provide FILE_ATTRIBUTE_ENCRYPTED to NtCreateFile() to set it on the
668  * created file.  However, the file or directory will otherwise default to the
669  * encryption state of the parent directory.  This function works around this
670  * limitation by using DecryptFile() to remove FILE_ATTRIBUTE_ENCRYPTED on files
671  * (and directories) that are not supposed to have it set.
672  *
673  * Regardless of whether it succeeds or fails, this function may close the
674  * handle to the file.  If it does, it sets it to NULL.
675  */
676 static int
677 maybe_clear_encryption_attribute(HANDLE *h_ret, const struct wim_dentry *dentry,
678                                  struct win32_apply_ctx *ctx)
679 {
680         if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
681                 return 0;
682
683         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
684                 return 0;
685
686         if (!ctx->common.supported_features.encrypted_files)
687                 return 0;
688
689         FILE_BASIC_INFORMATION info;
690         NTSTATUS status;
691         BOOL bret;
692
693         /* Get current attributes  */
694         status = (*func_NtQueryInformationFile)(*h_ret, &ctx->iosb,
695                                                 &info, sizeof(info),
696                                                 FileBasicInformation);
697         if (NT_SUCCESS(status) &&
698             !(info.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED))
699         {
700                 /* Nothing needs to be done.  */
701                 return 0;
702         }
703
704         /* Set the new encryption state  */
705
706         /* Due to Windows' crappy file encryption APIs, we need to close the
707          * handle to the file so we don't get ERROR_SHARING_VIOLATION.  We also
708          * hack together a Win32 path, although we will use the \\?\ prefix so
709          * it will actually be a NT path in disguise...  */
710         (*func_NtClose)(*h_ret);
711         *h_ret = NULL;
712
713         build_win32_extraction_path(dentry, ctx);
714
715         bret = DecryptFile(ctx->pathbuf.Buffer, 0);
716
717         /* Restore the NT namespace path  */
718         build_extraction_path(dentry, ctx);
719
720         if (!bret) {
721                 DWORD err = GetLastError();
722                 set_errno_from_win32_error(err);
723                 ERROR_WITH_ERRNO("Can't decrypt file \"%ls\" (err=%"PRIu32")",
724                                   current_path(ctx), (u32)err);
725                 return WIMLIB_ERR_SET_ATTRIBUTES;
726         }
727         return 0;
728 }
729
730 /* Set the short name on the open file @h which has been created at the location
731  * indicated by @dentry.
732  *
733  * Note that this may add, change, or remove the short name.
734  *
735  * @h must be opened with DELETE access.
736  *
737  * Returns 0 or WIMLIB_ERR_SET_SHORT_NAME.  The latter only happens in
738  * STRICT_SHORT_NAMES mode.
739  */
740 static int
741 set_short_name(HANDLE h, const struct wim_dentry *dentry,
742                struct win32_apply_ctx *ctx)
743 {
744         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
745                          dentry->short_name_nbytes;
746         u8 buf[bufsize] _aligned_attribute(8);
747         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
748         NTSTATUS status;
749
750         info->FileNameLength = dentry->short_name_nbytes;
751         memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
752
753         status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
754                                               FileShortNameInformation);
755         if (NT_SUCCESS(status))
756                 return 0;
757
758         /* By default, failure to set short names is not an error (since short
759          * names aren't too important anymore...).  */
760         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES))
761                 return 0;
762
763         if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
764                 if (dentry->short_name_nbytes == 0)
765                         return 0;
766                 ERROR("Can't extract short name when short "
767                       "names are not enabled on the volume!");
768         } else {
769                 ERROR("Can't set short name on \"%ls\" (status=0x%08"PRIx32")",
770                       current_path(ctx), (u32)status);
771         }
772         return WIMLIB_ERR_SET_SHORT_NAME;
773 }
774
775 /*
776  * A wrapper around NtCreateFile() to make it slightly more usable...
777  * This uses the path currently constructed in ctx->pathbuf.
778  *
779  * Also, we always specify FILE_OPEN_FOR_BACKUP_INTENT and
780  * FILE_OPEN_REPARSE_POINT.
781  */
782 static NTSTATUS
783 do_create_file(PHANDLE FileHandle,
784                ACCESS_MASK DesiredAccess,
785                PLARGE_INTEGER AllocationSize,
786                ULONG FileAttributes,
787                ULONG CreateDisposition,
788                ULONG CreateOptions,
789                struct win32_apply_ctx *ctx)
790 {
791         return (*func_NtCreateFile)(FileHandle,
792                                     DesiredAccess,
793                                     &ctx->attr,
794                                     &ctx->iosb,
795                                     AllocationSize,
796                                     FileAttributes,
797                                     FILE_SHARE_VALID_FLAGS,
798                                     CreateDisposition,
799                                     CreateOptions |
800                                         FILE_OPEN_FOR_BACKUP_INTENT |
801                                         FILE_OPEN_REPARSE_POINT,
802                                     NULL,
803                                     0);
804 }
805
806 /* Like do_create_file(), but builds the extraction path of the @dentry first.
807  */
808 static NTSTATUS
809 create_file(PHANDLE FileHandle,
810             ACCESS_MASK DesiredAccess,
811             PLARGE_INTEGER AllocationSize,
812             ULONG FileAttributes,
813             ULONG CreateDisposition,
814             ULONG CreateOptions,
815             const struct wim_dentry *dentry,
816             struct win32_apply_ctx *ctx)
817 {
818         build_extraction_path(dentry, ctx);
819         return do_create_file(FileHandle,
820                               DesiredAccess,
821                               AllocationSize,
822                               FileAttributes,
823                               CreateDisposition,
824                               CreateOptions,
825                               ctx);
826 }
827
828 /* Create empty named data streams.
829  *
830  * Since these won't have 'struct wim_lookup_table_entry's, they won't show up
831  * in the call to extract_stream_list().  Hence the need for the special case.
832  */
833 static int
834 create_any_empty_ads(const struct wim_dentry *dentry,
835                      struct win32_apply_ctx *ctx)
836 {
837         const struct wim_inode *inode = dentry->d_inode;
838         LARGE_INTEGER allocation_size;
839         bool path_modified = false;
840         int ret = 0;
841
842         if (!ctx->common.supported_features.named_data_streams)
843                 return 0;
844
845         for (u16 i = 0; i < inode->i_num_ads; i++) {
846                 const struct wim_ads_entry *entry;
847                 NTSTATUS status;
848                 HANDLE h;
849
850                 entry = &inode->i_ads_entries[i];
851
852                 /* Not named?  */
853                 if (!entry->stream_name_nbytes)
854                         continue;
855
856                 /* Not empty?  */
857                 if (entry->lte)
858                         continue;
859
860                 /* Probably setting the allocation size to 0 has no effect, but
861                  * we might as well try.  */
862                 allocation_size.QuadPart = 0;
863
864                 build_extraction_path_with_ads(dentry, ctx,
865                                                entry->stream_name,
866                                                entry->stream_name_nbytes /
867                                                         sizeof(wchar_t));
868                 path_modified = true;
869                 status = do_create_file(&h, FILE_WRITE_DATA, &allocation_size,
870                                         0, FILE_SUPERSEDE, 0, ctx);
871                 if (!NT_SUCCESS(status)) {
872                         set_errno_from_nt_status(status);
873                         ERROR_WITH_ERRNO("Can't create \"%ls\" "
874                                          "(status=0x%08"PRIx32")",
875                                          current_path(ctx), (u32)status);
876                         ret = WIMLIB_ERR_OPEN;
877                         break;
878                 }
879                 (*func_NtClose)(h);
880         }
881         /* Restore the path to the dentry itself  */
882         if (path_modified)
883                 build_extraction_path(dentry, ctx);
884         return ret;
885 }
886
887 /*
888  * Creates the directory named by @dentry, or uses an existing directory at that
889  * location.  If necessary, sets the short name and/or fixes compression and
890  * encryption attributes.
891  *
892  * Returns 0, WIMLIB_ERR_MKDIR, or WIMLIB_ERR_SET_SHORT_NAME.
893  */
894 static int
895 create_directory(const struct wim_dentry *dentry,
896                  struct win32_apply_ctx *ctx)
897 {
898         HANDLE h;
899         NTSTATUS status;
900         int ret;
901         ULONG attrib;
902
903         /* Special attributes:
904          *
905          * Use FILE_ATTRIBUTE_ENCRYPTED if the directory needs to have it set.
906          * This doesn't work for FILE_ATTRIBUTE_COMPRESSED (unfortunately).
907          *
908          * Don't specify FILE_ATTRIBUTE_DIRECTORY; it gets set anyway as a
909          * result of the FILE_DIRECTORY_FILE option.  */
910         attrib = (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED);
911
912         /* DELETE is needed for set_short_name().
913          * GENERIC_READ and GENERIC_WRITE are needed for
914          * adjust_compression_attribute().  */
915         status = create_file(&h, GENERIC_READ | GENERIC_WRITE | DELETE, NULL,
916                              attrib, FILE_OPEN_IF, FILE_DIRECTORY_FILE,
917                              dentry, ctx);
918         if (!NT_SUCCESS(status)) {
919                 set_errno_from_nt_status(status);
920                 ERROR_WITH_ERRNO("Can't create directory \"%ls\" "
921                                  "(status=0x%08"PRIx32")",
922                                  current_path(ctx), (u32)status);
923                 return WIMLIB_ERR_MKDIR;
924         }
925
926         ret = set_short_name(h, dentry, ctx);
927
928         if (!ret)
929                 ret = adjust_compression_attribute(h, dentry, ctx);
930
931         if (!ret)
932                 ret = maybe_clear_encryption_attribute(&h, dentry, ctx);
933                 /* May close the handle!!! */
934
935         if (h)
936                 (*func_NtClose)(h);
937         return ret;
938 }
939
940 /*
941  * Create all the directories being extracted, other than the target directory
942  * itself.
943  *
944  * Note: we don't honor directory hard links.  However, we don't allow them to
945  * exist in WIM images anyway (see inode_fixup.c).
946  */
947 static int
948 create_directories(struct list_head *dentry_list,
949                    struct win32_apply_ctx *ctx)
950 {
951         const struct wim_dentry *dentry;
952         int ret;
953
954         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
955
956                 if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
957                         continue;
958
959                 /* Note: Here we include files with
960                  * FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT, but we
961                  * wait until later to actually set the reparse data.  */
962
963                 /* If the root dentry is being extracted, it was already done so
964                  * it prepare_target().  */
965                 if (dentry_is_root(dentry))
966                         continue;
967
968                 ret = create_directory(dentry, ctx);
969                 if (ret)
970                         return ret;
971
972                 ret = create_any_empty_ads(dentry, ctx);
973                 if (ret)
974                         return ret;
975         }
976         return 0;
977 }
978
979 /* Gets the number of bytes to allocate for the specified inode.  */
980 static void
981 inode_get_allocation_size(const struct wim_inode *inode,
982                           LARGE_INTEGER *allocation_size_ret)
983 {
984         const struct wim_lookup_table_entry *unnamed_stream;
985
986         /* We just count the unnamed data stream.  */
987
988         unnamed_stream = inode_unnamed_lte_resolved(inode);
989         if (unnamed_stream)
990                 allocation_size_ret->QuadPart = unnamed_stream->size;
991         else
992                 allocation_size_ret->QuadPart = 0;
993 }
994
995 /*
996  * Creates the nondirectory file named by @dentry.
997  *
998  * On success, returns an open handle to the file in @h_ret, with GENERIC_READ,
999  * GENERIC_WRITE, and DELETE access.  Also, the path to the file will be saved
1000  * in ctx->pathbuf.  On failure, returns WIMLIB_ERR_OPEN.
1001  */
1002 static int
1003 create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
1004                           struct win32_apply_ctx *ctx)
1005 {
1006         const struct wim_inode *inode;
1007         LARGE_INTEGER allocation_size;
1008         ULONG attrib;
1009         NTSTATUS status;
1010         bool retried = false;
1011
1012         inode = dentry->d_inode;
1013
1014         /* To increase performance, we will pre-allocate space for the file
1015          * data.  */
1016         inode_get_allocation_size(inode, &allocation_size);
1017
1018         /* If the file already exists and has FILE_ATTRIBUTE_SYSTEM and/or
1019          * FILE_ATTRIBUTE_HIDDEN, these must be specified in order to supersede
1020          * the file.
1021          *
1022          * Normally the user shouldn't be trying to overwrite such files anyway,
1023          * but we at least provide FILE_ATTRIBUTE_SYSTEM and
1024          * FILE_ATTRIBUTE_HIDDEN if the WIM inode has those attributes so that
1025          * we catch the case where the user extracts the same files to the same
1026          * location more than one time.
1027          *
1028          * Also specify FILE_ATTRIBUTE_ENCRYPTED if the file needs to be
1029          * encrypted.
1030          *
1031          * In NO_ATTRIBUTES mode just don't specify any attributes at all.
1032          */
1033         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) {
1034                 attrib = 0;
1035         } else {
1036                 attrib = (inode->i_attributes & (FILE_ATTRIBUTE_SYSTEM |
1037                                                  FILE_ATTRIBUTE_HIDDEN |
1038                                                  FILE_ATTRIBUTE_ENCRYPTED));
1039         }
1040         build_extraction_path(dentry, ctx);
1041 retry:
1042         status = do_create_file(h_ret, GENERIC_READ | GENERIC_WRITE | DELETE,
1043                                 &allocation_size, attrib, FILE_SUPERSEDE,
1044                                 FILE_NON_DIRECTORY_FILE, ctx);
1045         if (NT_SUCCESS(status)) {
1046                 int ret;
1047
1048                 ret = adjust_compression_attribute(*h_ret, dentry, ctx);
1049                 if (ret) {
1050                         (*func_NtClose)(*h_ret);
1051                         return ret;
1052                 }
1053
1054                 ret = maybe_clear_encryption_attribute(h_ret, dentry, ctx);
1055                 /* May close the handle!!! */
1056
1057                 if (ret) {
1058                         if (*h_ret)
1059                                 (*func_NtClose)(*h_ret);
1060                         return ret;
1061                 }
1062
1063                 if (!*h_ret) {
1064                         /* Re-open the handle so that we can return it on
1065                          * success.  */
1066                         status = do_create_file(h_ret,
1067                                                 GENERIC_READ |
1068                                                         GENERIC_WRITE | DELETE,
1069                                                 NULL, 0, FILE_OPEN,
1070                                                 FILE_NON_DIRECTORY_FILE, ctx);
1071                         if (!NT_SUCCESS(status))
1072                                 goto fail;
1073                 }
1074
1075                 ret = create_any_empty_ads(dentry, ctx);
1076                 if (ret) {
1077                         (*func_NtClose)(*h_ret);
1078                         return ret;
1079                 }
1080                 return 0;
1081         }
1082
1083         if (status == STATUS_ACCESS_DENIED && !retried) {
1084                 /* We also can't supersede an existing file that has
1085                  * FILE_ATTRIBUTE_READONLY set; doing so causes NtCreateFile()
1086                  * to return STATUS_ACCESS_DENIED .  The only workaround seems
1087                  * to be to explicitly remove FILE_ATTRIBUTE_READONLY on the
1088                  * existing file, then try again.  */
1089
1090                 FILE_BASIC_INFORMATION info;
1091                 HANDLE h;
1092
1093                 status = do_create_file(&h, FILE_WRITE_ATTRIBUTES, NULL, 0,
1094                                         FILE_OPEN, FILE_NON_DIRECTORY_FILE, ctx);
1095                 if (!NT_SUCCESS(status))
1096                         goto fail;
1097
1098                 memset(&info, 0, sizeof(info));
1099                 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
1100
1101                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1102                                                       &info, sizeof(info),
1103                                                       FileBasicInformation);
1104                 (*func_NtClose)(h);
1105                 if (!NT_SUCCESS(status))
1106                         goto fail;
1107                 retried = true;
1108                 goto retry;
1109         }
1110 fail:
1111         set_errno_from_nt_status(status);
1112         ERROR_WITH_ERRNO("Can't create file \"%ls\" (status=0x%08"PRIx32")",
1113                          current_path(ctx), (u32)status);
1114         return WIMLIB_ERR_OPEN;
1115 }
1116
1117 /* Creates a hard link at the location named by @dentry to the file represented
1118  * by the open handle @h.  Or, if the target volume does not support hard links,
1119  * create a separate file instead.  */
1120 static int
1121 create_link(HANDLE h, const struct wim_dentry *dentry,
1122             struct win32_apply_ctx *ctx)
1123 {
1124         if (ctx->common.supported_features.hard_links) {
1125
1126                 build_extraction_path(dentry, ctx);
1127
1128                 size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
1129                                  ctx->pathbuf.Length + sizeof(wchar_t);
1130                 u8 buf[bufsize] _aligned_attribute(8);
1131                 FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
1132                 NTSTATUS status;
1133
1134                 info->ReplaceIfExists = TRUE;
1135                 info->RootDirectory = ctx->attr.RootDirectory;
1136                 info->FileNameLength = ctx->pathbuf.Length;
1137                 memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
1138                 info->FileName[info->FileNameLength / 2] = L'\0';
1139
1140                 /* Note: the null terminator isn't actually necessary,
1141                  * but if you don't add the extra character, you get
1142                  * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
1143                  * happens to be 2  */
1144
1145                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1146                                                       info, bufsize,
1147                                                       FileLinkInformation);
1148                 if (NT_SUCCESS(status))
1149                         return 0;
1150                 ERROR("Failed to create link \"%ls\" (status=0x%08"PRIx32")",
1151                       current_path(ctx), (u32)status);
1152                 return WIMLIB_ERR_LINK;
1153         } else {
1154                 HANDLE h2;
1155                 int ret;
1156
1157                 ret = create_nondirectory_inode(&h2, dentry, ctx);
1158                 if (ret)
1159                         return ret;
1160
1161                 (*func_NtClose)(h2);
1162                 return 0;
1163         }
1164 }
1165
1166 /* Given an inode (represented by the open handle @h) for which one link has
1167  * been created (named by @first_dentry), create the other links.
1168  *
1169  * Or, if the target volume does not support hard links, create separate files.
1170  *
1171  * Note: This uses ctx->pathbuf and does not reset it.
1172  */
1173 static int
1174 create_links(HANDLE h, const struct wim_dentry *first_dentry,
1175              struct win32_apply_ctx *ctx)
1176 {
1177         const struct wim_inode *inode;
1178         const struct list_head *next;
1179         const struct wim_dentry *dentry;
1180         int ret;
1181
1182         inode = first_dentry->d_inode;
1183         next = inode->i_extraction_aliases.next;
1184         do {
1185                 dentry = list_entry(next, struct wim_dentry,
1186                                     d_extraction_alias_node);
1187                 if (dentry != first_dentry) {
1188                         ret = create_link(h, dentry, ctx);
1189                         if (ret)
1190                                 return ret;
1191                 }
1192                 next = next->next;
1193         } while (next != &inode->i_extraction_aliases);
1194         return 0;
1195 }
1196
1197 /* Create a nondirectory file, including all links.  */
1198 static int
1199 create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx)
1200 {
1201         const struct wim_dentry *first_dentry;
1202         HANDLE h;
1203         int ret;
1204
1205         first_dentry = first_extraction_alias(inode);
1206
1207         /* Create first link.  */
1208         ret = create_nondirectory_inode(&h, first_dentry, ctx);
1209         if (ret)
1210                 return ret;
1211
1212         /* Set short name.  */
1213         ret = set_short_name(h, first_dentry, ctx);
1214
1215         /* Create additional links, OR if hard links are not supported just
1216          * create more files.  */
1217         if (!ret)
1218                 ret = create_links(h, first_dentry, ctx);
1219
1220         (*func_NtClose)(h);
1221         return ret;
1222 }
1223
1224 /* Create all the nondirectory files being extracted, including all aliases
1225  * (hard links).  */
1226 static int
1227 create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1228 {
1229         const struct wim_dentry *dentry;
1230         const struct wim_inode *inode;
1231         int ret;
1232
1233         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1234                 inode = dentry->d_inode;
1235                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1236                         continue;
1237                 /* Call create_nondirectory() only once per inode  */
1238                 if (dentry != inode_first_extraction_dentry(inode))
1239                         continue;
1240                 ret = create_nondirectory(inode, ctx);
1241                 if (ret)
1242                         return ret;
1243         }
1244         return 0;
1245 }
1246
1247 static void
1248 close_handles(struct win32_apply_ctx *ctx)
1249 {
1250         for (unsigned i = 0; i < ctx->num_open_handles; i++)
1251                 (*func_NtClose)(ctx->open_handles[i]);
1252 }
1253
1254 /* Prepare to read the next stream, which has size @stream_size, into an
1255  * in-memory buffer.  */
1256 static int
1257 prepare_data_buffer(struct win32_apply_ctx *ctx, u64 stream_size)
1258 {
1259         if (stream_size > ctx->data_buffer_size) {
1260                 /* Larger buffer needed.  */
1261                 void *new_buffer;
1262                 if ((size_t)stream_size != stream_size)
1263                         return WIMLIB_ERR_NOMEM;
1264                 new_buffer = REALLOC(ctx->data_buffer, stream_size);
1265                 if (!new_buffer)
1266                         return WIMLIB_ERR_NOMEM;
1267                 ctx->data_buffer = new_buffer;
1268                 ctx->data_buffer_size = stream_size;
1269         }
1270         /* On the first call this changes data_buffer_ptr from NULL, which tells
1271          * extract_chunk() that the data buffer needs to be filled while reading
1272          * the stream data.  */
1273         ctx->data_buffer_ptr = ctx->data_buffer;
1274         return 0;
1275 }
1276
1277 static int
1278 begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
1279                               struct wim_dentry *dentry,
1280                               const wchar_t *stream_name,
1281                               struct win32_apply_ctx *ctx)
1282 {
1283         const struct wim_inode *inode = dentry->d_inode;
1284         size_t stream_name_nchars = 0;
1285         LARGE_INTEGER allocation_size;
1286         HANDLE h;
1287         NTSTATUS status;
1288
1289         if (unlikely(stream_name))
1290                 stream_name_nchars = wcslen(stream_name);
1291
1292         if (unlikely(stream_name_nchars)) {
1293                 build_extraction_path_with_ads(dentry, ctx,
1294                                                stream_name, stream_name_nchars);
1295         } else {
1296                 build_extraction_path(dentry, ctx);
1297         }
1298
1299         /* Reparse point?  */
1300         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
1301             && (stream_name_nchars == 0))
1302         {
1303                 if (!ctx->common.supported_features.reparse_points)
1304                         return 0;
1305
1306                 /* We can't write the reparse stream directly; we must set it
1307                  * with FSCTL_SET_REPARSE_POINT, which requires that all the
1308                  * data be available.  So, stage the data in a buffer.  */
1309
1310                 list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
1311                 return prepare_data_buffer(ctx, stream->size);
1312         }
1313
1314         /* Encrypted file?  */
1315         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
1316             && (stream_name_nchars == 0))
1317         {
1318                 if (!ctx->common.supported_features.encrypted_files)
1319                         return 0;
1320
1321                 /* We can't write encrypted file streams directly; we must use
1322                  * WriteEncryptedFileRaw(), which requires providing the data
1323                  * through a callback function.  This can't easily be combined
1324                  * with our own callback-based approach.
1325                  *
1326                  * The current workaround is to simply read the stream into
1327                  * memory and write the encrypted file from that.
1328                  *
1329                  * TODO: This isn't sufficient for extremely large encrypted
1330                  * files.  Perhaps we should create an extra thread to write
1331                  * such files...  */
1332                 list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries);
1333                 return prepare_data_buffer(ctx, stream->size);
1334         }
1335
1336         /* Extracting unnamed data stream in WIMBoot mode?  */
1337         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)
1338             && (stream_name_nchars == 0)
1339             && (stream->resource_location == RESOURCE_IN_WIM)
1340             && (stream->rspec->wim == ctx->common.wim)
1341             && (stream->size == stream->rspec->uncompressed_size))
1342         {
1343                 int ret = calculate_dentry_full_path(dentry);
1344                 if (ret)
1345                         return ret;
1346                 if (in_prepopulate_list(dentry, ctx)) {
1347                         union wimlib_progress_info info;
1348
1349                         info.wimboot_exclude.path_in_wim = dentry->_full_path;
1350                         info.wimboot_exclude.extraction_path = current_path(ctx);
1351
1352                         ret = call_progress(ctx->common.progfunc,
1353                                             WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
1354                                             &info, ctx->common.progctx);
1355                         FREE(dentry->_full_path);
1356                         dentry->_full_path = NULL;
1357                         return ret;
1358                 } else {
1359                         FREE(dentry->_full_path);
1360                         dentry->_full_path = NULL;
1361                         return wimboot_set_pointer(&ctx->attr,
1362                                                    current_path(ctx),
1363                                                    stream,
1364                                                    ctx->wimboot.data_source_id,
1365                                                    ctx->wimboot.wim_lookup_table_hash,
1366                                                    ctx->wimboot.wof_running);
1367                 }
1368         }
1369
1370         /* Too many open handles?  */
1371         if (ctx->num_open_handles == MAX_OPEN_HANDLES) {
1372                 ERROR("Too many open handles!");
1373                 return WIMLIB_ERR_UNSUPPORTED;
1374         }
1375
1376         /* Open a new handle  */
1377         allocation_size.QuadPart = stream->size;
1378         status = do_create_file(&h,
1379                                 FILE_WRITE_DATA | SYNCHRONIZE,
1380                                 &allocation_size, 0, FILE_OPEN_IF,
1381                                 FILE_SEQUENTIAL_ONLY |
1382                                         FILE_SYNCHRONOUS_IO_NONALERT,
1383                                 ctx);
1384         if (!NT_SUCCESS(status)) {
1385                 set_errno_from_nt_status(status);
1386                 ERROR_WITH_ERRNO("Can't open \"%ls\" for writing "
1387                                  "(status=0x%08"PRIx32")",
1388                                  current_path(ctx), (u32)status);
1389                 return WIMLIB_ERR_OPEN;
1390         }
1391
1392         ctx->open_handles[ctx->num_open_handles++] = h;
1393         return 0;
1394 }
1395
1396 /* Set the reparse data @rpbuf of length @rpbuflen on the extracted file
1397  * corresponding to the WIM dentry @dentry.  */
1398 static int
1399 do_set_reparse_data(const struct wim_dentry *dentry,
1400                     const void *rpbuf, u16 rpbuflen,
1401                     struct win32_apply_ctx *ctx)
1402 {
1403         NTSTATUS status;
1404         HANDLE h;
1405
1406         status = create_file(&h, GENERIC_WRITE, NULL,
1407                              0, FILE_OPEN, 0, dentry, ctx);
1408         if (!NT_SUCCESS(status))
1409                 goto fail;
1410
1411         status = (*func_NtFsControlFile)(h, NULL, NULL, NULL,
1412                                          &ctx->iosb, FSCTL_SET_REPARSE_POINT,
1413                                          (void *)rpbuf, rpbuflen,
1414                                          NULL, 0);
1415         (*func_NtClose)(h);
1416
1417         if (NT_SUCCESS(status))
1418                 return 0;
1419
1420         /* On Windows, by default only the Administrator can create symbolic
1421          * links for some reason.  By default we just issue a warning if this
1422          * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
1423          * to get a hard error.  */
1424         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
1425             && (status == STATUS_PRIVILEGE_NOT_HELD ||
1426                 status == STATUS_ACCESS_DENIED)
1427             && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1428                 dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1429         {
1430                 WARNING("Can't create symbolic link \"%ls\"!              \n"
1431                         "          (Need Administrator rights, or at least "
1432                         "the\n"
1433                         "          SeCreateSymbolicLink privilege.)",
1434                         current_path(ctx));
1435                 return 0;
1436         }
1437
1438 fail:
1439         set_errno_from_nt_status(status);
1440         ERROR_WITH_ERRNO("Can't set reparse data on \"%ls\" "
1441                          "(status=0x%08"PRIx32")",
1442                          current_path(ctx), (u32)status);
1443         return WIMLIB_ERR_SET_REPARSE_DATA;
1444 }
1445
1446 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1447  * pointer to the suffix of the path that begins with the device directly, such
1448  * as e:\Windows\System32.  */
1449 static const wchar_t *
1450 skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
1451 {
1452         static const wchar_t * const dirs[] = {
1453                 L"\\??\\",
1454                 L"\\DosDevices\\",
1455                 L"\\Device\\",
1456         };
1457         size_t first_dir_len = 0;
1458         const wchar_t * const end = path + path_nchars;
1459
1460         for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
1461                 size_t len = wcslen(dirs[i]);
1462                 if (len <= (end - path) && !wcsnicmp(path, dirs[i], len)) {
1463                         first_dir_len = len;
1464                         break;
1465                 }
1466         }
1467         if (first_dir_len == 0)
1468                 return path;
1469         path += first_dir_len;
1470         while (path != end && *path == L'\\')
1471                 path++;
1472         return path;
1473 }
1474
1475 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1476  * pointer to the suffix of the path that is device-relative, such as
1477  * Windows\System32.  */
1478 static const wchar_t *
1479 get_device_relative_path(const wchar_t *path, size_t path_nchars)
1480 {
1481         const wchar_t * const orig_path = path;
1482         const wchar_t * const end = path + path_nchars;
1483
1484         path = skip_nt_toplevel_component(path, path_nchars);
1485         if (path == orig_path)
1486                 return orig_path;
1487
1488         path = wmemchr(path, L'\\', (end - path));
1489         if (!path)
1490                 return orig_path;
1491         do {
1492                 path++;
1493         } while (path != end && *path == L'\\');
1494         return path;
1495 }
1496
1497 /*
1498  * Given a reparse point buffer for a symbolic link or junction, adjust its
1499  * contents so that the target of the link is consistent with the new location
1500  * of the files.
1501  */
1502 static void
1503 try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, struct win32_apply_ctx *ctx)
1504 {
1505         struct reparse_data rpdata;
1506         size_t orig_subst_name_nchars;
1507         const wchar_t *relpath;
1508         size_t relpath_nchars;
1509         size_t target_ntpath_nchars;
1510         size_t fixed_subst_name_nchars;
1511         const wchar_t *fixed_print_name;
1512         size_t fixed_print_name_nchars;
1513
1514         if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
1515                 /* Do nothing if the reparse data is invalid.  */
1516                 return;
1517         }
1518
1519         if (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
1520             (rpdata.rpflags & SYMBOLIC_LINK_RELATIVE))
1521         {
1522                 /* Do nothing if it's a relative symbolic link.  */
1523                 return;
1524         }
1525
1526         /* Build the new substitute name from the NT namespace path to the
1527          * target directory, then a path separator, then the "device relative"
1528          * part of the old substitute name.  */
1529
1530         orig_subst_name_nchars = rpdata.substitute_name_nbytes / sizeof(wchar_t);
1531
1532         relpath = get_device_relative_path(rpdata.substitute_name,
1533                                            orig_subst_name_nchars);
1534         relpath_nchars = orig_subst_name_nchars -
1535                          (relpath - rpdata.substitute_name);
1536
1537         target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
1538
1539         fixed_subst_name_nchars = target_ntpath_nchars + 1 + relpath_nchars;
1540         wchar_t fixed_subst_name[fixed_subst_name_nchars];
1541
1542         wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer,
1543                 target_ntpath_nchars);
1544         fixed_subst_name[target_ntpath_nchars] = L'\\';
1545         wmemcpy(&fixed_subst_name[target_ntpath_nchars + 1],
1546                 relpath, relpath_nchars);
1547         /* Doesn't need to be null-terminated.  */
1548
1549         /* Print name should be Win32, but not all NT names can even be
1550          * translated to Win32 names.  But we can at least delete the top-level
1551          * directory, such as \??\, and this will have the expected result in
1552          * the usual case.  */
1553         fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
1554                                                       fixed_subst_name_nchars);
1555         fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
1556                                                              fixed_subst_name);
1557
1558         rpdata.substitute_name = fixed_subst_name;
1559         rpdata.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
1560         rpdata.print_name = (wchar_t *)fixed_print_name;
1561         rpdata.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
1562         make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
1563 }
1564
1565 /* Sets reparse data on the specified file.  This handles "fixing" the targets
1566  * of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX was
1567  * specified.  */
1568 static int
1569 set_reparse_data(const struct wim_dentry *dentry,
1570                  const void *_rpbuf, u16 rpbuflen, struct win32_apply_ctx *ctx)
1571 {
1572         const struct wim_inode *inode = dentry->d_inode;
1573         const void *rpbuf = _rpbuf;
1574
1575         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
1576             && !inode->i_not_rpfixed
1577             && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1578                 inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1579         {
1580                 memcpy(&ctx->rpfixbuf, _rpbuf, rpbuflen);
1581                 try_rpfix((u8 *)&ctx->rpfixbuf, &rpbuflen, ctx);
1582                 rpbuf = &ctx->rpfixbuf;
1583         }
1584         return do_set_reparse_data(dentry, rpbuf, rpbuflen, ctx);
1585
1586 }
1587
1588 /* Import the next block of raw encrypted data  */
1589 static DWORD WINAPI
1590 import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
1591 {
1592         struct win32_apply_ctx *ctx = pvCallbackContext;
1593         ULONG copy_len;
1594
1595         copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
1596         memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
1597         ctx->encrypted_offset += copy_len;
1598         *Length = copy_len;
1599         return ERROR_SUCCESS;
1600 }
1601
1602 /* Write the raw encrypted data to the already-created file corresponding to
1603  * @dentry.
1604  *
1605  * The raw encrypted data is provided in ctx->data_buffer, and its size is
1606  * ctx->encrypted_size.  */
1607 static int
1608 extract_encrypted_file(const struct wim_dentry *dentry,
1609                        struct win32_apply_ctx *ctx)
1610 {
1611         void *rawctx;
1612         DWORD err;
1613
1614         /* Temporarily build a Win32 path for OpenEncryptedFileRaw()  */
1615         build_win32_extraction_path(dentry, ctx);
1616
1617         err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer,
1618                                    CREATE_FOR_IMPORT, &rawctx);
1619
1620         /* Restore the NT namespace path  */
1621         build_extraction_path(dentry, ctx);
1622
1623         if (err != ERROR_SUCCESS) {
1624                 set_errno_from_win32_error(err);
1625                 ERROR_WITH_ERRNO("Can't open \"%ls\" for encrypted import "
1626                                  "(err=%"PRIu32")", current_path(ctx), (u32)err);
1627                 return WIMLIB_ERR_OPEN;
1628         }
1629
1630         ctx->encrypted_offset = 0;
1631
1632         err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx);
1633
1634         CloseEncryptedFileRaw(rawctx);
1635
1636         if (err != ERROR_SUCCESS) {
1637                 set_errno_from_win32_error(err);
1638                 ERROR_WITH_ERRNO("Can't import encrypted file \"%ls\" "
1639                                  "(err=%"PRIu32")", current_path(ctx), (u32)err);
1640                 return WIMLIB_ERR_WRITE;
1641         }
1642
1643         return 0;
1644 }
1645
1646 /* Called when starting to read a stream for extraction on Windows  */
1647 static int
1648 begin_extract_stream(struct wim_lookup_table_entry *stream,
1649                      u32 flags, void *_ctx)
1650 {
1651         struct win32_apply_ctx *ctx = _ctx;
1652         const struct stream_owner *owners = stream_owners(stream);
1653         int ret;
1654
1655         ctx->num_open_handles = 0;
1656         ctx->data_buffer_ptr = NULL;
1657         INIT_LIST_HEAD(&ctx->reparse_dentries);
1658         INIT_LIST_HEAD(&ctx->encrypted_dentries);
1659
1660         for (u32 i = 0; i < stream->out_refcnt; i++) {
1661                 const struct wim_inode *inode = owners[i].inode;
1662                 const wchar_t *stream_name = owners[i].stream_name;
1663                 struct wim_dentry *dentry;
1664
1665                 /* A copy of the stream needs to be extracted to @inode.  */
1666
1667                 if (ctx->common.supported_features.hard_links) {
1668                         dentry = inode_first_extraction_dentry(inode);
1669                         ret = begin_extract_stream_instance(stream, dentry,
1670                                                             stream_name, ctx);
1671                         if (ret)
1672                                 goto fail;
1673                 } else {
1674                         /* Hard links not supported.  Extract the stream
1675                          * separately to each alias of the inode.  */
1676                         struct list_head *next;
1677
1678                         next = inode->i_extraction_aliases.next;
1679                         do {
1680                                 dentry = list_entry(next, struct wim_dentry,
1681                                                     d_extraction_alias_node);
1682                                 ret = begin_extract_stream_instance(stream,
1683                                                                     dentry,
1684                                                                     stream_name,
1685                                                                     ctx);
1686                                 if (ret)
1687                                         goto fail;
1688                                 next = next->next;
1689                         } while (next != &inode->i_extraction_aliases);
1690                 }
1691         }
1692
1693         if (unlikely(ctx->num_open_handles == 0 && ctx->data_buffer_ptr == NULL)) {
1694                 /* The data of this stream isn't actually needed!
1695                  * (This can happen in WIMBoot mode.)  */
1696                 return BEGIN_STREAM_STATUS_SKIP_STREAM;
1697         }
1698         return 0;
1699
1700 fail:
1701         close_handles(ctx);
1702         return ret;
1703 }
1704
1705 /* Called when the next chunk of a stream has been read for extraction on
1706  * Windows  */
1707 static int
1708 extract_chunk(const void *chunk, size_t size, void *_ctx)
1709 {
1710         struct win32_apply_ctx *ctx = _ctx;
1711
1712         /* Write the data chunk to each open handle  */
1713         for (unsigned i = 0; i < ctx->num_open_handles; i++) {
1714                 u8 *bufptr = (u8 *)chunk;
1715                 size_t bytes_remaining = size;
1716                 NTSTATUS status;
1717                 while (bytes_remaining) {
1718                         ULONG count = min(0xFFFFFFFF, bytes_remaining);
1719
1720                         status = (*func_NtWriteFile)(ctx->open_handles[i],
1721                                                      NULL, NULL, NULL,
1722                                                      &ctx->iosb, bufptr, count,
1723                                                      NULL, NULL);
1724                         if (!NT_SUCCESS(status)) {
1725                                 set_errno_from_nt_status(status);
1726                                 ERROR_WITH_ERRNO("Error writing data to target "
1727                                                  "volume (status=0x%08"PRIx32")",
1728                                                  (u32)status);
1729                                 return WIMLIB_ERR_WRITE;
1730                         }
1731                         bufptr += ctx->iosb.Information;
1732                         bytes_remaining -= ctx->iosb.Information;
1733                 }
1734         }
1735
1736         /* Copy the data chunk into the buffer (if needed)  */
1737         if (ctx->data_buffer_ptr)
1738                 ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
1739                                                chunk, size);
1740         return 0;
1741 }
1742
1743 /* Called when a stream has been fully read for extraction on Windows  */
1744 static int
1745 end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx)
1746 {
1747         struct win32_apply_ctx *ctx = _ctx;
1748         int ret;
1749         const struct wim_dentry *dentry;
1750
1751         close_handles(ctx);
1752
1753         if (status)
1754                 return status;
1755
1756         if (likely(!ctx->data_buffer_ptr))
1757                 return 0;
1758
1759         if (!list_empty(&ctx->reparse_dentries)) {
1760                 if (stream->size > REPARSE_DATA_MAX_SIZE) {
1761                         dentry = list_first_entry(&ctx->reparse_dentries,
1762                                                   struct wim_dentry, tmp_list);
1763                         build_extraction_path(dentry, ctx);
1764                         ERROR("Invalid reparse point", current_path(ctx));
1765                         return WIMLIB_ERR_INVALID_REPARSE_DATA;
1766                 }
1767                 /* In the WIM format, reparse streams are just the reparse data
1768                  * and omit the header.  But we can reconstruct the header.  */
1769                 memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, stream->size);
1770                 ctx->rpbuf.rpdatalen = stream->size;
1771                 ctx->rpbuf.rpreserved = 0;
1772                 list_for_each_entry(dentry, &ctx->reparse_dentries, tmp_list) {
1773                         ctx->rpbuf.rptag = dentry->d_inode->i_reparse_tag;
1774                         ret = set_reparse_data(dentry, &ctx->rpbuf,
1775                                                stream->size + REPARSE_DATA_OFFSET,
1776                                                ctx);
1777                         if (ret)
1778                                 return ret;
1779                 }
1780         }
1781
1782         if (!list_empty(&ctx->encrypted_dentries)) {
1783                 ctx->encrypted_size = stream->size;
1784                 list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) {
1785                         ret = extract_encrypted_file(dentry, ctx);
1786                         if (ret)
1787                                 return ret;
1788                 }
1789         }
1790
1791         return 0;
1792 }
1793
1794 /* Attributes that can't be set directly  */
1795 #define SPECIAL_ATTRIBUTES                      \
1796         (FILE_ATTRIBUTE_REPARSE_POINT   |       \
1797          FILE_ATTRIBUTE_DIRECTORY       |       \
1798          FILE_ATTRIBUTE_ENCRYPTED       |       \
1799          FILE_ATTRIBUTE_SPARSE_FILE     |       \
1800          FILE_ATTRIBUTE_COMPRESSED)
1801
1802 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
1803  * handle @h.  */
1804 static NTSTATUS
1805 set_security_descriptor(HANDLE h, const void *desc,
1806                         size_t desc_size, struct win32_apply_ctx *ctx)
1807 {
1808         SECURITY_INFORMATION info;
1809         NTSTATUS status;
1810
1811         /* We really just want to set entire the security descriptor as-is, but
1812          * all available APIs require specifying the specific parts of the
1813          * descriptor being set.  Start out by requesting all parts be set.  If
1814          * permissions problems are encountered, fall back to omitting some
1815          * parts (first the SACL, then the DACL, then the owner), unless the
1816          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS flag has been enabled.  */
1817         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
1818                DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION;
1819
1820         /* Prefer NtSetSecurityObject() to SetFileSecurity().  SetFileSecurity()
1821          * itself necessarily uses NtSetSecurityObject() as the latter is the
1822          * underlying system call for setting security information, but
1823          * SetFileSecurity() opens the handle with NtCreateFile() without
1824          * FILE_OPEN_FILE_BACKUP_INTENT.  Hence, access checks are done and due
1825          * to the Windows security model, even a process running as the
1826          * Administrator can have access denied.  (Of course, this not mentioned
1827          * in the MS "documentation".)  */
1828 retry:
1829         status = (*func_NtSetSecurityObject)(h, info, (PSECURITY_DESCRIPTOR)desc);
1830         if (NT_SUCCESS(status))
1831                 return status;
1832         /* Failed to set the requested parts of the security descriptor.  If the
1833          * error was permissions-related, try to set fewer parts of the security
1834          * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
1835         if ((status == STATUS_PRIVILEGE_NOT_HELD ||
1836              status == STATUS_ACCESS_DENIED) &&
1837             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
1838         {
1839                 if (info & SACL_SECURITY_INFORMATION) {
1840                         info &= ~SACL_SECURITY_INFORMATION;
1841                         ctx->partial_security_descriptors++;
1842                         goto retry;
1843                 }
1844                 if (info & DACL_SECURITY_INFORMATION) {
1845                         info &= ~DACL_SECURITY_INFORMATION;
1846                         goto retry;
1847                 }
1848                 if (info & OWNER_SECURITY_INFORMATION) {
1849                         info &= ~OWNER_SECURITY_INFORMATION;
1850                         goto retry;
1851                 }
1852                 /* Nothing left except GROUP, and if we removed it we
1853                  * wouldn't have anything at all.  */
1854         }
1855
1856         /* No part of the security descriptor could be set, or
1857          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security
1858          * descriptor could not be set.  */
1859         if (!(info & SACL_SECURITY_INFORMATION))
1860                 ctx->partial_security_descriptors--;
1861         ctx->no_security_descriptors++;
1862         return status;
1863 }
1864
1865 /* Set metadata on the open file @h from the WIM inode @inode.  */
1866 static int
1867 do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
1868                           struct win32_apply_ctx *ctx)
1869 {
1870         FILE_BASIC_INFORMATION info;
1871         NTSTATUS status;
1872
1873         /* Set security descriptor if present and not in NO_ACLS mode  */
1874         if (inode->i_security_id >= 0 &&
1875             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
1876         {
1877                 const struct wim_security_data *sd;
1878                 const void *desc;
1879                 size_t desc_size;
1880
1881                 sd = wim_get_current_security_data(ctx->common.wim);
1882                 desc = sd->descriptors[inode->i_security_id];
1883                 desc_size = sd->sizes[inode->i_security_id];
1884
1885                 status = set_security_descriptor(h, desc, desc_size, ctx);
1886                 if (!NT_SUCCESS(status) &&
1887                     (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
1888                 {
1889                         set_errno_from_nt_status(status);
1890                         ERROR_WITH_ERRNO("Can't set security descriptor "
1891                                          "on \"%ls\" (status=0x%08"PRIx32")",
1892                                          current_path(ctx), (u32)status);
1893                         return WIMLIB_ERR_SET_SECURITY;
1894                 }
1895         }
1896
1897         /* Set attributes and timestamps  */
1898         info.CreationTime.QuadPart = inode->i_creation_time;
1899         info.LastAccessTime.QuadPart = inode->i_last_access_time;
1900         info.LastWriteTime.QuadPart = inode->i_last_write_time;
1901         info.ChangeTime.QuadPart = 0;
1902         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
1903                 info.FileAttributes = 0;
1904         else
1905                 info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES;
1906
1907         status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1908                                               &info, sizeof(info),
1909                                               FileBasicInformation);
1910         /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set
1911          * attributes on the root directory.  (Apparently because FAT doesn't
1912          * actually have a place to store those attributes!)  */
1913         if (!NT_SUCCESS(status)
1914             && !(status == STATUS_INVALID_PARAMETER &&
1915                  dentry_is_root(inode_first_extraction_dentry(inode))))
1916         {
1917                 set_errno_from_nt_status(status);
1918                 ERROR_WITH_ERRNO("Can't set basic metadata on \"%ls\" "
1919                                  "(status=0x%08"PRIx32")",
1920                                  current_path(ctx), (u32)status);
1921                 return WIMLIB_ERR_SET_ATTRIBUTES;
1922         }
1923
1924         return 0;
1925 }
1926
1927 static int
1928 apply_metadata_to_file(const struct wim_dentry *dentry,
1929                        struct win32_apply_ctx *ctx)
1930 {
1931         const struct wim_inode *inode = dentry->d_inode;
1932         DWORD perms;
1933         HANDLE h;
1934         NTSTATUS status;
1935         int ret;
1936
1937         perms = FILE_WRITE_ATTRIBUTES | WRITE_DAC |
1938                 WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
1939
1940         build_extraction_path(dentry, ctx);
1941
1942         /* Open a handle with as many relevant permissions as possible.  */
1943         while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL,
1944                                                    0, FILE_OPEN, 0, ctx)))
1945         {
1946                 if (status == STATUS_PRIVILEGE_NOT_HELD ||
1947                     status == STATUS_ACCESS_DENIED)
1948                 {
1949                         if (perms & ACCESS_SYSTEM_SECURITY) {
1950                                 perms &= ~ACCESS_SYSTEM_SECURITY;
1951                                 continue;
1952                         }
1953                         if (perms & WRITE_DAC) {
1954                                 perms &= ~WRITE_DAC;
1955                                 continue;
1956                         }
1957                         if (perms & WRITE_OWNER) {
1958                                 perms &= ~WRITE_OWNER;
1959                                 continue;
1960                         }
1961                 }
1962                 set_errno_from_nt_status(status);
1963                 ERROR_WITH_ERRNO("Can't open \"%ls\" to set metadata "
1964                                  "(status=0x%08"PRIx32")",
1965                                  current_path(ctx), (u32)status);
1966                 return WIMLIB_ERR_OPEN;
1967         }
1968
1969         ret = do_apply_metadata_to_file(h, inode, ctx);
1970
1971         (*func_NtClose)(h);
1972
1973         return ret;
1974 }
1975
1976 static int
1977 apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1978 {
1979         const struct wim_dentry *dentry;
1980         int ret;
1981
1982         /* We go in reverse so that metadata is set on all a directory's
1983          * children before the directory itself.  This avoids any potential
1984          * problems with attributes, timestamps, or security descriptors.  */
1985         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node)
1986         {
1987                 ret = apply_metadata_to_file(dentry, ctx);
1988                 if (ret)
1989                         return ret;
1990         }
1991         return 0;
1992 }
1993
1994 /* Issue warnings about problems during the extraction for which warnings were
1995  * not already issued (due to the high number of potential warnings if we issued
1996  * them per-file).  */
1997 static void
1998 do_warnings(const struct win32_apply_ctx *ctx)
1999 {
2000         if (ctx->partial_security_descriptors == 0 &&
2001             ctx->no_security_descriptors == 0)
2002                 return;
2003
2004         WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
2005                 ctx->common.target);
2006         if (ctx->partial_security_descriptors != 0) {
2007                 WARNING("- Could only partially set the security descriptor\n"
2008                         "            on %lu files or directories.",
2009                         ctx->partial_security_descriptors);
2010         }
2011         if (ctx->no_security_descriptors != 0) {
2012                 WARNING("- Could not set security descriptor at all\n"
2013                         "            on %lu files or directories.",
2014                         ctx->no_security_descriptors);
2015         }
2016         WARNING("To fully restore all security descriptors, run the program\n"
2017                 "          with Administrator rights.");
2018 }
2019
2020 /* Extract files from a WIM image to a directory on Windows  */
2021 static int
2022 win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
2023 {
2024         int ret;
2025         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
2026
2027         ret = prepare_target(dentry_list, ctx);
2028         if (ret)
2029                 goto out;
2030
2031         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
2032                 ret = start_wimboot_extraction(ctx);
2033                 if (ret)
2034                         goto out;
2035         }
2036
2037         ret = create_directories(dentry_list, ctx);
2038         if (ret)
2039                 goto out;
2040
2041         ret = create_nondirectories(dentry_list, ctx);
2042         if (ret)
2043                 goto out;
2044
2045         struct read_stream_list_callbacks cbs = {
2046                 .begin_stream      = begin_extract_stream,
2047                 .begin_stream_ctx  = ctx,
2048                 .consume_chunk     = extract_chunk,
2049                 .consume_chunk_ctx = ctx,
2050                 .end_stream        = end_extract_stream,
2051                 .end_stream_ctx    = ctx,
2052         };
2053         ret = extract_stream_list(&ctx->common, &cbs);
2054         if (ret)
2055                 goto out;
2056
2057         ret = apply_metadata(dentry_list, ctx);
2058         if (ret)
2059                 goto out;
2060
2061         do_warnings(ctx);
2062 out:
2063         if (ctx->h_target)
2064                 (*func_NtClose)(ctx->h_target);
2065         if (ctx->target_ntpath.Buffer)
2066                 HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
2067         FREE(ctx->pathbuf.Buffer);
2068         FREE(ctx->print_buffer);
2069         if (ctx->wimboot.prepopulate_pats) {
2070                 FREE(ctx->wimboot.prepopulate_pats->strings);
2071                 FREE(ctx->wimboot.prepopulate_pats);
2072         }
2073         FREE(ctx->wimboot.mem_prepopulate_pats);
2074         FREE(ctx->data_buffer);
2075         return ret;
2076 }
2077
2078 const struct apply_operations win32_apply_ops = {
2079         .name                   = "Windows",
2080         .get_supported_features = win32_get_supported_features,
2081         .extract                = win32_extract,
2082         .context_size           = sizeof(struct win32_apply_ctx),
2083 };