win32_apply: Stricter checks of resource format for WOF external backing
[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, 2015 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21
22 #ifdef __WIN32__
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/assert.h"
32 #include "wimlib/blob_table.h"
33 #include "wimlib/capture.h" /* for mangle_pat() and match_pattern_list()  */
34 #include "wimlib/dentry.h"
35 #include "wimlib/encoding.h"
36 #include "wimlib/error.h"
37 #include "wimlib/metadata.h"
38 #include "wimlib/paths.h"
39 #include "wimlib/reparse.h"
40 #include "wimlib/textfile.h"
41 #include "wimlib/xml.h"
42 #include "wimlib/wildcard.h"
43 #include "wimlib/wimboot.h"
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                 /* This array contains the WIM files registered with WOF on the
54                  * target volume for this extraction operation.  All WIMStructs
55                  * in this array are distinct and have ->filename != NULL.  */
56                 struct wimboot_wim {
57                         WIMStruct *wim;
58                         u64 data_source_id;
59                         u8 blob_table_hash[SHA1_HASH_SIZE];
60                 } *wims;
61                 size_t num_wims;
62                 struct string_set *prepopulate_pats;
63                 void *mem_prepopulate_pats;
64                 bool wof_running;
65                 bool tried_to_load_prepopulate_list;
66         } wimboot;
67
68         /* Open handle to the target directory  */
69         HANDLE h_target;
70
71         /* NT namespace path to the target directory (buffer allocated)  */
72         UNICODE_STRING target_ntpath;
73
74         /* Temporary buffer for building paths (buffer allocated)  */
75         UNICODE_STRING pathbuf;
76
77         /* Object attributes to reuse for opening files in the target directory.
78          * (attr.ObjectName == &pathbuf) and (attr.RootDirectory == h_target).
79          */
80         OBJECT_ATTRIBUTES attr;
81
82         /* Temporary I/O status block for system calls  */
83         IO_STATUS_BLOCK iosb;
84
85         /* Allocated buffer for creating "printable" paths from our
86          * target-relative NT paths  */
87         wchar_t *print_buffer;
88
89         /* Allocated buffer for reading blob data when it cannot be extracted
90          * directly  */
91         u8 *data_buffer;
92
93         /* Pointer to the next byte in @data_buffer to fill  */
94         u8 *data_buffer_ptr;
95
96         /* Size allocated in @data_buffer  */
97         size_t data_buffer_size;
98
99         /* Current offset in the raw encrypted file being written  */
100         size_t encrypted_offset;
101
102         /* Current size of the raw encrypted file being written  */
103         size_t encrypted_size;
104
105         /* Temporary buffer for reparse data  */
106         struct reparse_buffer_disk rpbuf;
107
108         /* Temporary buffer for reparse data of "fixed" absolute symbolic links
109          * and junctions  */
110         struct reparse_buffer_disk rpfixbuf;
111
112         /* Array of open handles to filesystem streams currently being written
113          */
114         HANDLE open_handles[MAX_OPEN_FILES];
115
116         /* Number of handles in @open_handles currently open (filled in from the
117          * beginning of the array)  */
118         unsigned num_open_handles;
119
120         /* List of dentries, joined by @tmp_list, that need to have reparse data
121          * extracted as soon as the whole blob has been read into @data_buffer.
122          * */
123         struct list_head reparse_dentries;
124
125         /* List of dentries, joined by @tmp_list, that need to have raw
126          * encrypted data extracted as soon as the whole blob has been read into
127          * @data_buffer.  */
128         struct list_head encrypted_dentries;
129
130         /* Number of files for which we didn't have permission to set the full
131          * security descriptor.  */
132         unsigned long partial_security_descriptors;
133
134         /* Number of files for which we didn't have permission to set any part
135          * of the security descriptor.  */
136         unsigned long no_security_descriptors;
137
138         /* Number of files for which we couldn't set the short name.  */
139         unsigned long num_set_short_name_failures;
140
141         /* Number of files for which we couldn't remove the short name.  */
142         unsigned long num_remove_short_name_failures;
143
144         /* Have we tried to enable short name support on the target volume yet?
145          */
146         bool tried_to_enable_short_names;
147 };
148
149 /* Get the drive letter from a Windows path, or return the null character if the
150  * path is relative.  */
151 static wchar_t
152 get_drive_letter(const wchar_t *path)
153 {
154         /* Skip \\?\ prefix  */
155         if (!wcsncmp(path, L"\\\\?\\", 4))
156                 path += 4;
157
158         /* Return drive letter if valid  */
159         if (((path[0] >= L'a' && path[0] <= L'z') ||
160              (path[0] >= L'A' && path[0] <= L'Z')) && path[1] == L':')
161                 return path[0];
162
163         return L'\0';
164 }
165
166 static void
167 get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
168               bool *short_names_supported_ret)
169 {
170         wchar_t filesystem_name[MAX_PATH + 1];
171         wchar_t drive[4];
172         wchar_t *volume = NULL;
173
174         *vol_flags_ret = 0;
175         *short_names_supported_ret = false;
176
177         drive[0] = get_drive_letter(target);
178         if (drive[0]) {
179                 drive[1] = L':';
180                 drive[2] = L'\\';
181                 drive[3] = L'\0';
182                 volume = drive;
183         }
184
185         if (!GetVolumeInformation(volume, NULL, 0, NULL, NULL,
186                                   vol_flags_ret, filesystem_name,
187                                   ARRAY_LEN(filesystem_name)))
188         {
189                 win32_warning(GetLastError(),
190                               L"Failed to get volume information for \"%ls\"",
191                               target);
192                 return;
193         }
194
195         if (wcsstr(filesystem_name, L"NTFS")) {
196                 /* FILE_SUPPORTS_HARD_LINKS is only supported on Windows 7 and
197                  * later.  Force it on anyway if filesystem is NTFS.  */
198                 *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS;
199
200                 /* There's no volume flag for short names, but according to the
201                  * MS documentation they are only user-settable on NTFS.  */
202                 *short_names_supported_ret = true;
203         }
204 }
205
206 static const wchar_t *
207 current_path(struct win32_apply_ctx *ctx);
208
209 static void
210 build_extraction_path(const struct wim_dentry *dentry,
211                       struct win32_apply_ctx *ctx);
212
213 static int
214 report_dentry_apply_error(const struct wim_dentry *dentry,
215                           struct win32_apply_ctx *ctx, int ret)
216 {
217         build_extraction_path(dentry, ctx);
218         return report_apply_error(&ctx->common, ret, current_path(ctx));
219 }
220
221 static inline int
222 check_apply_error(const struct wim_dentry *dentry,
223                   struct win32_apply_ctx *ctx, int ret)
224 {
225         if (unlikely(ret))
226                 ret = report_dentry_apply_error(dentry, ctx, ret);
227         return ret;
228 }
229
230 static int
231 win32_get_supported_features(const wchar_t *target,
232                              struct wim_features *supported_features)
233 {
234         DWORD vol_flags;
235         bool short_names_supported;
236
237         /* Query the features of the target volume.  */
238
239         get_vol_flags(target, &vol_flags, &short_names_supported);
240
241         supported_features->archive_files = 1;
242         supported_features->hidden_files = 1;
243         supported_features->system_files = 1;
244
245         if (vol_flags & FILE_FILE_COMPRESSION)
246                 supported_features->compressed_files = 1;
247
248         if (vol_flags & FILE_SUPPORTS_ENCRYPTION) {
249                 supported_features->encrypted_files = 1;
250                 supported_features->encrypted_directories = 1;
251         }
252
253         supported_features->not_context_indexed_files = 1;
254
255         /* Don't do anything with FILE_SUPPORTS_SPARSE_FILES.  */
256
257         if (vol_flags & FILE_NAMED_STREAMS)
258                 supported_features->named_data_streams = 1;
259
260         if (vol_flags & FILE_SUPPORTS_HARD_LINKS)
261                 supported_features->hard_links = 1;
262
263         if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS)
264                 supported_features->reparse_points = 1;
265
266         if (vol_flags & FILE_PERSISTENT_ACLS)
267                 supported_features->security_descriptors = 1;
268
269         if (short_names_supported)
270                 supported_features->short_names = 1;
271
272         supported_features->timestamps = 1;
273
274         /* Note: Windows does not support case sensitive filenames!  At least
275          * not without changing the registry and rebooting...  */
276
277         return 0;
278 }
279
280 /* Load the patterns from [PrepopulateList] of WimBootCompress.ini in the WIM
281  * image being extracted.  */
282 static int
283 load_prepopulate_pats(struct win32_apply_ctx *ctx)
284 {
285         const wchar_t *path = L"\\Windows\\System32\\WimBootCompress.ini";
286         struct wim_dentry *dentry;
287         struct blob_descriptor *blob;
288         int ret;
289         void *buf;
290         struct string_set *s;
291         void *mem;
292         struct text_file_section sec;
293
294         ctx->wimboot.tried_to_load_prepopulate_list = true;
295
296         dentry = get_dentry(ctx->common.wim, path, WIMLIB_CASE_INSENSITIVE);
297         if (!dentry ||
298             (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
299                                               FILE_ATTRIBUTE_REPARSE_POINT |
300                                               FILE_ATTRIBUTE_ENCRYPTED)) ||
301             !(blob = inode_get_blob_for_unnamed_data_stream(dentry->d_inode,
302                                                             ctx->common.wim->blob_table)))
303         {
304                 WARNING("%ls does not exist in WIM image!", path);
305                 return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
306         }
307
308         ret = read_full_blob_into_alloc_buf(blob, &buf);
309         if (ret)
310                 return ret;
311
312         s = CALLOC(1, sizeof(struct string_set));
313         if (!s) {
314                 FREE(buf);
315                 return WIMLIB_ERR_NOMEM;
316         }
317
318         sec.name = T("PrepopulateList");
319         sec.strings = s;
320
321         ret = do_load_text_file(path, buf, blob->size, &mem, &sec, 1,
322                                 LOAD_TEXT_FILE_REMOVE_QUOTES |
323                                         LOAD_TEXT_FILE_NO_WARNINGS,
324                                 mangle_pat);
325         BUILD_BUG_ON(OS_PREFERRED_PATH_SEPARATOR != WIM_PATH_SEPARATOR);
326         FREE(buf);
327         if (ret) {
328                 FREE(s);
329                 return ret;
330         }
331         ctx->wimboot.prepopulate_pats = s;
332         ctx->wimboot.mem_prepopulate_pats = mem;
333         return 0;
334 }
335
336 /* Returns %true if the specified absolute path to a file in the WIM image
337  * matches a pattern in [PrepopulateList] of WimBootCompress.ini.  Otherwise
338  * returns %false.  */
339 static bool
340 in_prepopulate_list(const wchar_t *path, size_t path_nchars,
341                     const struct win32_apply_ctx *ctx)
342 {
343         const struct string_set *pats = ctx->wimboot.prepopulate_pats;
344
345         if (!pats || !pats->num_strings)
346                 return false;
347
348         return match_pattern_list(path, path_nchars, pats);
349 }
350
351 /* Returns %true if the specified absolute path to a file in the WIM image can
352  * be subject to external backing when extracted.  Otherwise returns %false.  */
353 static bool
354 can_externally_back_path(const wchar_t *path, size_t path_nchars,
355                          const struct win32_apply_ctx *ctx)
356 {
357         if (in_prepopulate_list(path, path_nchars, ctx))
358                 return false;
359
360         /* Since we attempt to modify the SYSTEM registry after it's extracted
361          * (see end_wimboot_extraction()), it can't be extracted as externally
362          * backed.  This extends to associated files such as SYSTEM.LOG that
363          * also must be writable in order to write to the registry.  Normally,
364          * SYSTEM is in [PrepopulateList], and the SYSTEM.* files match patterns
365          * in [ExclusionList] and therefore are not captured in the WIM at all.
366          * However, a WIM that wasn't specifically captured in "WIMBoot mode"
367          * may contain SYSTEM.* files.  So to make things "just work", hard-code
368          * the pattern.  */
369         if (match_path(path, path_nchars, L"\\Windows\\System32\\config\\SYSTEM*",
370                        OS_PREFERRED_PATH_SEPARATOR, false))
371                 return false;
372
373         return true;
374 }
375
376 static bool
377 is_resource_valid_for_external_backing(const struct wim_resource_descriptor *rdesc)
378 {
379         /* Must be the original WIM file format.  This check excludes pipable
380          * resources and solid resources.  It also excludes other resources
381          * contained in such files even if they would be otherwise compatible.
382          */
383         if (rdesc->wim->hdr.magic != WIM_MAGIC ||
384             rdesc->wim->hdr.wim_version != WIM_VERSION_DEFAULT)
385                 return false;
386
387         /*
388          * Whitelist of compression types and chunk sizes supported by
389          * Microsoft's WOF driver.
390          *
391          * Notes:
392          *    - Uncompressed WIMs result in BSOD.  However, this only applies to
393          *      the WIM file itself, not to uncompressed resources in a WIM file
394          *      that is otherwise compressed.
395          *    - XPRESS 64K sometimes appears to work, but sometimes it causes
396          *      reads to fail with STATUS_UNSUCCESSFUL.
397          */
398         switch (rdesc->compression_type) {
399         case WIMLIB_COMPRESSION_TYPE_NONE:
400                 if (rdesc->wim->compression_type == WIMLIB_COMPRESSION_TYPE_NONE)
401                         return false;
402                 break;
403         case WIMLIB_COMPRESSION_TYPE_XPRESS:
404                 switch (rdesc->chunk_size) {
405                 case 4096:
406                 case 8192:
407                 case 16384:
408                 case 32768:
409                         break;
410                 default:
411                         return false;
412                 }
413                 break;
414         case WIMLIB_COMPRESSION_TYPE_LZX:
415                 switch (rdesc->chunk_size) {
416                 case 32768:
417                         break;
418                 default:
419                         return false;
420                 }
421                 break;
422         default:
423                 return false;
424         }
425
426         /* Microsoft's WoF driver errors out if it tries to satisfy a read with
427          * ending offset >= 4 GiB from an externally backed file.  */
428         if (rdesc->uncompressed_size > 4200000000)
429                 return false;
430
431         return true;
432 }
433
434 #define WIM_BACKING_NOT_ENABLED         -1
435 #define WIM_BACKING_NOT_POSSIBLE        -2
436 #define WIM_BACKING_EXCLUDED            -3
437
438 static int
439 will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx,
440                            const struct wim_dentry **excluded_dentry_ret)
441 {
442         struct list_head *next;
443         struct wim_dentry *dentry;
444         struct blob_descriptor *blob;
445         int ret;
446
447         if (inode->i_can_externally_back)
448                 return 0;
449
450         /* This may do redundant checks because the cached value
451          * i_can_externally_back is 2-state (as opposed to 3-state:
452          * unknown/no/yes).  But most files can be externally backed, so this
453          * way is fine.  */
454
455         if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
456                                    FILE_ATTRIBUTE_REPARSE_POINT |
457                                    FILE_ATTRIBUTE_ENCRYPTED))
458                 return WIM_BACKING_NOT_POSSIBLE;
459
460         blob = inode_get_blob_for_unnamed_data_stream_resolved(inode);
461
462         if (!blob || blob->blob_location != BLOB_IN_WIM ||
463             !is_resource_valid_for_external_backing(blob->rdesc, ctx))
464                 return WIM_BACKING_NOT_POSSIBLE;
465
466         /*
467          * We need to check the patterns in [PrepopulateList] against every name
468          * of the inode, in case any of them match.
469          */
470         next = inode->i_extraction_aliases.next;
471         do {
472                 dentry = list_entry(next, struct wim_dentry,
473                                     d_extraction_alias_node);
474
475                 ret = calculate_dentry_full_path(dentry);
476                 if (ret)
477                         return ret;
478
479                 if (!can_externally_back_path(dentry->_full_path,
480                                               wcslen(dentry->_full_path), ctx))
481                 {
482                         if (excluded_dentry_ret)
483                                 *excluded_dentry_ret = dentry;
484                         return WIM_BACKING_EXCLUDED;
485                 }
486                 next = next->next;
487         } while (next != &inode->i_extraction_aliases);
488
489         inode->i_can_externally_back = 1;
490         return 0;
491 }
492
493 /*
494  * Determines if the unnamed data stream of a file will be created as an
495  * external backing, as opposed to a standard extraction.
496  */
497 static int
498 win32_will_externally_back(struct wim_dentry *dentry, struct apply_ctx *_ctx)
499 {
500         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
501
502         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
503                 return WIM_BACKING_NOT_ENABLED;
504
505         if (!ctx->wimboot.tried_to_load_prepopulate_list)
506                 if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM)
507                         return WIMLIB_ERR_NOMEM;
508
509         return will_externally_back_inode(dentry->d_inode, ctx, NULL);
510 }
511
512 /* Find the WOF registration information for the specified WIM file.  */
513 static struct wimboot_wim *
514 find_wimboot_wim(WIMStruct *wim_to_find, struct win32_apply_ctx *ctx)
515 {
516         for (size_t i = 0; i < ctx->wimboot.num_wims; i++)
517                 if (wim_to_find == ctx->wimboot.wims[i].wim)
518                         return &ctx->wimboot.wims[i];
519
520         wimlib_assert(0);
521         return NULL;
522 }
523
524 static int
525 set_external_backing(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *ctx)
526 {
527         int ret;
528         const struct wim_dentry *excluded_dentry;
529         const struct blob_descriptor *blob;
530         const struct wimboot_wim *wimboot_wim;
531
532         ret = will_externally_back_inode(inode, ctx, &excluded_dentry);
533         if (ret > 0) /* Error.  */
534                 return ret;
535
536         if (ret < 0 && ret != WIM_BACKING_EXCLUDED)
537                 return 0; /* Not externally backing, other than due to exclusion.  */
538
539         if (unlikely(ret == WIM_BACKING_EXCLUDED)) {
540                 /* Not externally backing due to exclusion.  */
541                 union wimlib_progress_info info;
542
543                 build_extraction_path(excluded_dentry, ctx);
544
545                 info.wimboot_exclude.path_in_wim = excluded_dentry->_full_path;
546                 info.wimboot_exclude.extraction_path = current_path(ctx);
547
548                 return call_progress(ctx->common.progfunc,
549                                      WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
550                                      &info, ctx->common.progctx);
551         }
552
553         /* Externally backing.  */
554
555         blob = inode_get_blob_for_unnamed_data_stream_resolved(inode);
556         wimboot_wim = find_wimboot_wim(blob->rdesc->wim, ctx);
557
558         if (unlikely(!wimboot_set_pointer(h,
559                                           blob,
560                                           wimboot_wim->data_source_id,
561                                           wimboot_wim->blob_table_hash,
562                                           ctx->wimboot.wof_running)))
563         {
564                 const DWORD err = GetLastError();
565
566                 build_extraction_path(inode_first_extraction_dentry(inode), ctx);
567                 win32_error(err, L"\"%ls\": Couldn't set WIMBoot pointer data",
568                             current_path(ctx));
569                 return WIMLIB_ERR_WIMBOOT;
570         }
571         return 0;
572 }
573
574 /* Calculates the SHA-1 message digest of the WIM's blob table.  */
575 static int
576 hash_blob_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE])
577 {
578         return wim_reshdr_to_hash(&wim->hdr.blob_table_reshdr, wim, hash);
579 }
580
581 static int
582 register_wim_with_wof(WIMStruct *wim, struct win32_apply_ctx *ctx)
583 {
584         struct wimboot_wim *p;
585         int ret;
586
587         /* Check if already registered  */
588         for (size_t i = 0; i < ctx->wimboot.num_wims; i++)
589                 if (wim == ctx->wimboot.wims[i].wim)
590                         return 0;
591
592         /* Not yet registered  */
593
594         p = REALLOC(ctx->wimboot.wims,
595                     (ctx->wimboot.num_wims + 1) * sizeof(ctx->wimboot.wims[0]));
596         if (!p)
597                 return WIMLIB_ERR_NOMEM;
598         ctx->wimboot.wims = p;
599
600         ctx->wimboot.wims[ctx->wimboot.num_wims].wim = wim;
601
602         ret = hash_blob_table(wim, ctx->wimboot.wims[ctx->wimboot.num_wims].blob_table_hash);
603         if (ret)
604                 return ret;
605
606         ret = wimboot_alloc_data_source_id(wim->filename,
607                                            wim->hdr.guid,
608                                            ctx->common.wim->current_image,
609                                            ctx->common.target,
610                                            &ctx->wimboot.wims[ctx->wimboot.num_wims].data_source_id,
611                                            &ctx->wimboot.wof_running);
612         if (ret)
613                 return ret;
614
615         ctx->wimboot.num_wims++;
616         return 0;
617 }
618
619 /* Prepare for doing a "WIMBoot" extraction by loading patterns from
620  * [PrepopulateList] of WimBootCompress.ini and registering each source WIM file
621  * with WOF on the target volume.  */
622 static int
623 start_wimboot_extraction(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
624 {
625         int ret;
626         struct wim_dentry *dentry;
627
628         if (!ctx->wimboot.tried_to_load_prepopulate_list)
629                 if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM)
630                         return WIMLIB_ERR_NOMEM;
631
632         if (!wim_info_get_wimboot(ctx->common.wim->wim_info,
633                                   ctx->common.wim->current_image))
634                 WARNING("Image is not marked as WIMBoot compatible!");
635
636         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
637                 struct blob_descriptor *blob;
638
639                 ret = win32_will_externally_back(dentry, &ctx->common);
640                 if (ret > 0) /* Error */
641                         return ret;
642                 if (ret < 0) /* Won't externally back */
643                         continue;
644
645                 blob = inode_get_blob_for_unnamed_data_stream_resolved(dentry->d_inode);
646                 ret = register_wim_with_wof(blob->rdesc->wim, ctx);
647                 if (ret)
648                         return ret;
649         }
650
651         return 0;
652 }
653
654 static void
655 build_win32_extraction_path(const struct wim_dentry *dentry,
656                             struct win32_apply_ctx *ctx);
657
658 /* Sets WimBoot=1 in the extracted SYSTEM registry hive.
659  *
660  * WIMGAPI does this, and it's possible that it's important.
661  * But I don't know exactly what this value means to Windows.  */
662 static int
663 end_wimboot_extraction(struct win32_apply_ctx *ctx)
664 {
665         struct wim_dentry *dentry;
666         wchar_t subkeyname[32];
667         LONG res;
668         LONG res2;
669         HKEY key;
670         DWORD value;
671
672         dentry = get_dentry(ctx->common.wim, L"\\Windows\\System32\\config\\SYSTEM",
673                             WIMLIB_CASE_INSENSITIVE);
674
675         if (!dentry || !will_extract_dentry(dentry))
676                 goto out;
677
678         if (!will_extract_dentry(wim_get_current_root_dentry(ctx->common.wim)))
679                 goto out;
680
681         /* Not bothering to use the native routines (e.g. NtLoadKey()) for this.
682          * If this doesn't work, you probably also have many other problems.  */
683
684         build_win32_extraction_path(dentry, ctx);
685
686         randomize_char_array_with_alnum(subkeyname, 20);
687         subkeyname[20] = L'\0';
688
689         res = RegLoadKey(HKEY_LOCAL_MACHINE, subkeyname, ctx->pathbuf.Buffer);
690         if (res)
691                 goto out_check_res;
692
693         wcscpy(&subkeyname[20], L"\\Setup");
694
695         res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, subkeyname, 0, NULL,
696                              REG_OPTION_BACKUP_RESTORE, 0, NULL, &key, NULL);
697         if (res)
698                 goto out_unload_key;
699
700         value = 1;
701
702         res = RegSetValueEx(key, L"WimBoot", 0, REG_DWORD,
703                             (const BYTE *)&value, sizeof(DWORD));
704         if (res)
705                 goto out_close_key;
706
707         res = RegFlushKey(key);
708
709 out_close_key:
710         res2 = RegCloseKey(key);
711         if (!res)
712                 res = res2;
713 out_unload_key:
714         subkeyname[20] = L'\0';
715         RegUnLoadKey(HKEY_LOCAL_MACHINE, subkeyname);
716 out_check_res:
717         if (res) {
718                 /* Warning only.  */
719                 win32_warning(res, L"Failed to set \\Setup: dword \"WimBoot\"=1 "
720                               "value in registry hive \"%ls\"",
721                               ctx->pathbuf.Buffer);
722         }
723 out:
724         return 0;
725 }
726
727 /* Returns the number of wide characters needed to represent the path to the
728  * specified @dentry, relative to the target directory, when extracted.
729  *
730  * Does not include null terminator (not needed for NtCreateFile).  */
731 static size_t
732 dentry_extraction_path_length(const struct wim_dentry *dentry)
733 {
734         size_t len = 0;
735         const struct wim_dentry *d;
736
737         d = dentry;
738         do {
739                 len += d->d_extraction_name_nchars + 1;
740                 d = d->d_parent;
741         } while (!dentry_is_root(d) && will_extract_dentry(d));
742
743         return --len;  /* No leading slash  */
744 }
745
746 /* Returns the length of the longest string that might need to be appended to
747  * the path to an alias of an inode to open or create a named data stream.
748  *
749  * If the inode has no named data streams, this will be 0.  Otherwise, this will
750  * be 1 plus the length of the longest-named data stream, since the data stream
751  * name must be separated from the path by the ':' character.  */
752 static size_t
753 inode_longest_named_data_stream_spec(const struct wim_inode *inode)
754 {
755         size_t max = 0;
756         for (unsigned i = 0; i < inode->i_num_streams; i++) {
757                 const struct wim_inode_stream *strm = &inode->i_streams[i];
758                 if (!stream_is_named_data_stream(strm))
759                         continue;
760                 size_t len = utf16le_len_chars(strm->stream_name);
761                 if (len > max)
762                         max = len;
763         }
764         if (max)
765                 max += 1;
766         return max;
767 }
768
769 /* Find the length, in wide characters, of the longest path needed for
770  * extraction of any file in @dentry_list relative to the target directory.
771  *
772  * Accounts for named data streams, but does not include null terminator (not
773  * needed for NtCreateFile).  */
774 static size_t
775 compute_path_max(struct list_head *dentry_list)
776 {
777         size_t max = 0;
778         const struct wim_dentry *dentry;
779
780         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
781                 size_t len;
782
783                 len = dentry_extraction_path_length(dentry);
784
785                 /* Account for named data streams  */
786                 len += inode_longest_named_data_stream_spec(dentry->d_inode);
787
788                 if (len > max)
789                         max = len;
790         }
791
792         return max;
793 }
794
795 /* Build the path at which to extract the @dentry, relative to the target
796  * directory.
797  *
798  * The path is saved in ctx->pathbuf.  */
799 static void
800 build_extraction_path(const struct wim_dentry *dentry,
801                       struct win32_apply_ctx *ctx)
802 {
803         size_t len;
804         wchar_t *p;
805         const struct wim_dentry *d;
806
807         len = dentry_extraction_path_length(dentry);
808
809         ctx->pathbuf.Length = len * sizeof(wchar_t);
810         p = ctx->pathbuf.Buffer + len;
811         for (d = dentry;
812              !dentry_is_root(d->d_parent) && will_extract_dentry(d->d_parent);
813              d = d->d_parent)
814         {
815                 p -= d->d_extraction_name_nchars;
816                 wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
817                 *--p = '\\';
818         }
819         /* No leading slash  */
820         p -= d->d_extraction_name_nchars;
821         wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
822 }
823
824 /* Build the path at which to extract the @dentry, relative to the target
825  * directory, adding the suffix for a named data stream.
826  *
827  * The path is saved in ctx->pathbuf.  */
828 static void
829 build_extraction_path_with_ads(const struct wim_dentry *dentry,
830                                struct win32_apply_ctx *ctx,
831                                const wchar_t *stream_name,
832                                size_t stream_name_nchars)
833 {
834         wchar_t *p;
835
836         build_extraction_path(dentry, ctx);
837
838         /* Add :NAME for named data stream  */
839         p = ctx->pathbuf.Buffer + (ctx->pathbuf.Length / sizeof(wchar_t));
840         *p++ = L':';
841         wmemcpy(p, stream_name, stream_name_nchars);
842         ctx->pathbuf.Length += (1 + stream_name_nchars) * sizeof(wchar_t);
843 }
844
845 /* Build the Win32 namespace path to the specified @dentry when extracted.
846  *
847  * The path is saved in ctx->pathbuf and will be null terminated.
848  *
849  * XXX: We could get rid of this if it wasn't needed for the file encryption
850  * APIs, and the registry manipulation in WIMBoot mode.  */
851 static void
852 build_win32_extraction_path(const struct wim_dentry *dentry,
853                             struct win32_apply_ctx *ctx)
854 {
855         build_extraction_path(dentry, ctx);
856
857         /* Prepend target_ntpath to our relative path, then change \??\ into \\?\  */
858
859         memmove(ctx->pathbuf.Buffer +
860                         (ctx->target_ntpath.Length / sizeof(wchar_t)) + 1,
861                 ctx->pathbuf.Buffer, ctx->pathbuf.Length);
862         memcpy(ctx->pathbuf.Buffer, ctx->target_ntpath.Buffer,
863                 ctx->target_ntpath.Length);
864         ctx->pathbuf.Buffer[ctx->target_ntpath.Length / sizeof(wchar_t)] = L'\\';
865         ctx->pathbuf.Length += ctx->target_ntpath.Length + sizeof(wchar_t);
866         ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)] = L'\0';
867
868         wimlib_assert(ctx->pathbuf.Length >= 4 * sizeof(wchar_t) &&
869                       !wmemcmp(ctx->pathbuf.Buffer, L"\\??\\", 4));
870
871         ctx->pathbuf.Buffer[1] = L'\\';
872
873 }
874
875 /* Returns a "printable" representation of the last relative NT path that was
876  * constructed with build_extraction_path() or build_extraction_path_with_ads().
877  *
878  * This will be overwritten by the next call to this function.  */
879 static const wchar_t *
880 current_path(struct win32_apply_ctx *ctx)
881 {
882         wchar_t *p = ctx->print_buffer;
883
884         p = wmempcpy(p, ctx->common.target, ctx->common.target_nchars);
885         *p++ = L'\\';
886         p = wmempcpy(p, ctx->pathbuf.Buffer, ctx->pathbuf.Length / sizeof(wchar_t));
887         *p = L'\0';
888         return ctx->print_buffer;
889 }
890
891 /* Open handle to the target directory if it is not already open.  If the target
892  * directory does not exist, this creates it.  */
893 static int
894 open_target_directory(struct win32_apply_ctx *ctx)
895 {
896         NTSTATUS status;
897
898         if (ctx->h_target)
899                 return 0;
900
901         ctx->attr.Length = sizeof(ctx->attr);
902         ctx->attr.RootDirectory = NULL;
903         ctx->attr.ObjectName = &ctx->target_ntpath;
904         status = (*func_NtCreateFile)(&ctx->h_target,
905                                       FILE_TRAVERSE,
906                                       &ctx->attr,
907                                       &ctx->iosb,
908                                       NULL,
909                                       0,
910                                       FILE_SHARE_VALID_FLAGS,
911                                       FILE_OPEN_IF,
912                                       FILE_DIRECTORY_FILE |
913                                               FILE_OPEN_REPARSE_POINT |
914                                               FILE_OPEN_FOR_BACKUP_INTENT,
915                                       NULL,
916                                       0);
917         if (!NT_SUCCESS(status)) {
918                 winnt_error(status, L"Can't open or create directory \"%ls\"",
919                             ctx->common.target);
920                 return WIMLIB_ERR_OPENDIR;
921         }
922         ctx->attr.RootDirectory = ctx->h_target;
923         ctx->attr.ObjectName = &ctx->pathbuf;
924         return 0;
925 }
926
927 static void
928 close_target_directory(struct win32_apply_ctx *ctx)
929 {
930         if (ctx->h_target) {
931                 (*func_NtClose)(ctx->h_target);
932                 ctx->h_target = NULL;
933                 ctx->attr.RootDirectory = NULL;
934         }
935 }
936
937 /*
938  * Ensures the target directory exists and opens a handle to it, in preparation
939  * of using paths relative to it.
940  */
941 static int
942 prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
943 {
944         int ret;
945         size_t path_max;
946
947         ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath);
948         if (ret)
949                 return ret;
950
951         ret = open_target_directory(ctx);
952         if (ret)
953                 return ret;
954
955         path_max = compute_path_max(dentry_list);
956         /* Add some extra for building Win32 paths for the file encryption APIs,
957          * and ensure we have at least enough to potentially use a 8.3 name for
958          * the last component.  */
959         path_max += max(2 + (ctx->target_ntpath.Length / sizeof(wchar_t)),
960                         8 + 1 + 3);
961
962         ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t);
963         ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength);
964         if (!ctx->pathbuf.Buffer)
965                 return WIMLIB_ERR_NOMEM;
966
967         ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) *
968                                    sizeof(wchar_t));
969         if (!ctx->print_buffer)
970                 return WIMLIB_ERR_NOMEM;
971
972         return 0;
973 }
974
975 /* When creating an inode that will have a short (DOS) name, we create it using
976  * the long name associated with the short name.  This ensures that the short
977  * name gets associated with the correct long name.  */
978 static struct wim_dentry *
979 first_extraction_alias(const struct wim_inode *inode)
980 {
981         struct list_head *next = inode->i_extraction_aliases.next;
982         struct wim_dentry *dentry;
983
984         do {
985                 dentry = list_entry(next, struct wim_dentry,
986                                     d_extraction_alias_node);
987                 if (dentry_has_short_name(dentry))
988                         break;
989                 next = next->next;
990         } while (next != &inode->i_extraction_aliases);
991         return dentry;
992 }
993
994 /*
995  * Set or clear FILE_ATTRIBUTE_COMPRESSED if the inherited value is different
996  * from the desired value.
997  *
998  * Note that you can NOT override the inherited value of
999  * FILE_ATTRIBUTE_COMPRESSED directly with NtCreateFile().
1000  */
1001 static int
1002 adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
1003                              struct win32_apply_ctx *ctx)
1004 {
1005         const bool compressed = (dentry->d_inode->i_attributes &
1006                                  FILE_ATTRIBUTE_COMPRESSED);
1007
1008         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)
1009                 return 0;
1010
1011         if (!ctx->common.supported_features.compressed_files)
1012                 return 0;
1013
1014         FILE_BASIC_INFORMATION info;
1015         NTSTATUS status;
1016         USHORT compression_state;
1017
1018         /* Get current attributes  */
1019         status = (*func_NtQueryInformationFile)(h, &ctx->iosb,
1020                                                 &info, sizeof(info),
1021                                                 FileBasicInformation);
1022         if (NT_SUCCESS(status) &&
1023             compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED))
1024         {
1025                 /* Nothing needs to be done.  */
1026                 return 0;
1027         }
1028
1029         /* Set the new compression state  */
1030
1031         if (compressed)
1032                 compression_state = COMPRESSION_FORMAT_DEFAULT;
1033         else
1034                 compression_state = COMPRESSION_FORMAT_NONE;
1035
1036         status = (*func_NtFsControlFile)(h,
1037                                          NULL,
1038                                          NULL,
1039                                          NULL,
1040                                          &ctx->iosb,
1041                                          FSCTL_SET_COMPRESSION,
1042                                          &compression_state,
1043                                          sizeof(USHORT),
1044                                          NULL,
1045                                          0);
1046         if (NT_SUCCESS(status))
1047                 return 0;
1048
1049         winnt_error(status, L"Can't %s compression attribute on \"%ls\"",
1050                     (compressed ? "set" : "clear"), current_path(ctx));
1051         return WIMLIB_ERR_SET_ATTRIBUTES;
1052 }
1053
1054 /* Try to enable short name support on the target volume.  If successful, return
1055  * true.  If unsuccessful, issue a warning and return false.  */
1056 static bool
1057 try_to_enable_short_names(const wchar_t *volume)
1058 {
1059         HANDLE h;
1060         FILE_FS_PERSISTENT_VOLUME_INFORMATION info;
1061         BOOL bret;
1062         DWORD bytesReturned;
1063
1064         h = CreateFile(volume, GENERIC_WRITE,
1065                        FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
1066                        FILE_FLAG_BACKUP_SEMANTICS, NULL);
1067         if (h == INVALID_HANDLE_VALUE)
1068                 goto fail;
1069
1070         info.VolumeFlags = 0;
1071         info.FlagMask = PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED;
1072         info.Version = 1;
1073         info.Reserved = 0;
1074
1075         bret = DeviceIoControl(h, FSCTL_SET_PERSISTENT_VOLUME_STATE,
1076                                &info, sizeof(info), NULL, 0,
1077                                &bytesReturned, NULL);
1078
1079         CloseHandle(h);
1080
1081         if (!bret)
1082                 goto fail;
1083         return true;
1084
1085 fail:
1086         win32_warning(GetLastError(),
1087                       L"Failed to enable short name support on %ls",
1088                       volume + 4);
1089         return false;
1090 }
1091
1092 static NTSTATUS
1093 remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
1094 {
1095         wchar_t *name;
1096         wchar_t *end;
1097         NTSTATUS status;
1098         HANDLE h;
1099         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
1100                          (13 * sizeof(wchar_t));
1101         u8 buf[bufsize] _aligned_attribute(8);
1102         bool retried = false;
1103         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
1104
1105         memset(buf, 0, bufsize);
1106
1107         /* Build the path with the short name.  */
1108         name = &ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)];
1109         while (name != ctx->pathbuf.Buffer && *(name - 1) != L'\\')
1110                 name--;
1111         end = mempcpy(name, dentry->short_name, dentry->short_name_nbytes);
1112         ctx->pathbuf.Length = ((u8 *)end - (u8 *)ctx->pathbuf.Buffer);
1113
1114         /* Open the conflicting file (by short name).  */
1115         status = (*func_NtOpenFile)(&h, GENERIC_WRITE | DELETE,
1116                                     &ctx->attr, &ctx->iosb,
1117                                     FILE_SHARE_VALID_FLAGS,
1118                                     FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT);
1119         if (!NT_SUCCESS(status)) {
1120                 winnt_warning(status, L"Can't open \"%ls\"", current_path(ctx));
1121                 goto out;
1122         }
1123
1124 #if 0
1125         WARNING("Overriding conflicting short name; path=\"%ls\"",
1126                 current_path(ctx));
1127 #endif
1128
1129         /* Try to remove the short name on the conflicting file.  */
1130
1131 retry:
1132         status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
1133                                               FileShortNameInformation);
1134
1135         if (status == STATUS_INVALID_PARAMETER && !retried) {
1136
1137                 /* Microsoft forgot to make it possible to remove short names
1138                  * until Windows 7.  Oops.  Use a random short name instead.  */
1139
1140                 info->FileNameLength = 12 * sizeof(wchar_t);
1141                 for (int i = 0; i < 8; i++)
1142                         info->FileName[i] = 'A' + (rand() % 26);
1143                 info->FileName[8] = L'.';
1144                 info->FileName[9] = L'W';
1145                 info->FileName[10] = L'L';
1146                 info->FileName[11] = L'B';
1147                 info->FileName[12] = L'\0';
1148                 retried = true;
1149                 goto retry;
1150         }
1151         (*func_NtClose)(h);
1152 out:
1153         build_extraction_path(dentry, ctx);
1154         return status;
1155 }
1156
1157 /* Set the short name on the open file @h which has been created at the location
1158  * indicated by @dentry.
1159  *
1160  * Note that this may add, change, or remove the short name.
1161  *
1162  * @h must be opened with DELETE access.
1163  *
1164  * Returns 0 or WIMLIB_ERR_SET_SHORT_NAME.  The latter only happens in
1165  * STRICT_SHORT_NAMES mode.
1166  */
1167 static int
1168 set_short_name(HANDLE h, const struct wim_dentry *dentry,
1169                struct win32_apply_ctx *ctx)
1170 {
1171
1172         if (!ctx->common.supported_features.short_names)
1173                 return 0;
1174
1175         /*
1176          * Note: The size of the FILE_NAME_INFORMATION buffer must be such that
1177          * FileName contains at least 2 wide characters (4 bytes).  Otherwise,
1178          * NtSetInformationFile() will return STATUS_INFO_LENGTH_MISMATCH.  This
1179          * is despite the fact that FileNameLength can validly be 0 or 2 bytes,
1180          * with the former case being removing the existing short name if
1181          * present, rather than setting one.
1182          *
1183          * The null terminator is seemingly optional, but to be safe we include
1184          * space for it and zero all unused space.
1185          */
1186
1187         size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
1188                          max(dentry->short_name_nbytes, sizeof(wchar_t)) +
1189                          sizeof(wchar_t);
1190         u8 buf[bufsize] _aligned_attribute(8);
1191         FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
1192         NTSTATUS status;
1193         bool tried_to_remove_existing = false;
1194
1195         memset(buf, 0, bufsize);
1196
1197         info->FileNameLength = dentry->short_name_nbytes;
1198         memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
1199
1200 retry:
1201         status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
1202                                               FileShortNameInformation);
1203         if (NT_SUCCESS(status))
1204                 return 0;
1205
1206         if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
1207                 if (dentry->short_name_nbytes == 0)
1208                         return 0;
1209                 if (!ctx->tried_to_enable_short_names) {
1210                         wchar_t volume[7];
1211                         int ret;
1212
1213                         ctx->tried_to_enable_short_names = true;
1214
1215                         ret = win32_get_drive_path(ctx->common.target,
1216                                                    volume);
1217                         if (ret)
1218                                 return ret;
1219                         if (try_to_enable_short_names(volume))
1220                                 goto retry;
1221                 }
1222         }
1223
1224         /*
1225          * Short names can conflict in several cases:
1226          *
1227          * - a file being extracted has a short name conflicting with an
1228          *   existing file
1229          *
1230          * - a file being extracted has a short name conflicting with another
1231          *   file being extracted (possible, but shouldn't happen)
1232          *
1233          * - a file being extracted has a short name that conflicts with the
1234          *   automatically generated short name of a file we previously
1235          *   extracted, but failed to set the short name for.  Sounds unlikely,
1236          *   but this actually does happen fairly often on versions of Windows
1237          *   prior to Windows 7 because they do not support removing short names
1238          *   from files.
1239          */
1240         if (unlikely(status == STATUS_OBJECT_NAME_COLLISION) &&
1241             dentry->short_name_nbytes && !tried_to_remove_existing)
1242         {
1243                 tried_to_remove_existing = true;
1244                 status = remove_conflicting_short_name(dentry, ctx);
1245                 if (NT_SUCCESS(status))
1246                         goto retry;
1247         }
1248
1249         /* By default, failure to set short names is not an error (since short
1250          * names aren't too important anymore...).  */
1251         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES)) {
1252                 if (dentry->short_name_nbytes)
1253                         ctx->num_set_short_name_failures++;
1254                 else
1255                         ctx->num_remove_short_name_failures++;
1256                 return 0;
1257         }
1258
1259         winnt_error(status, L"Can't set short name on \"%ls\"", current_path(ctx));
1260         return WIMLIB_ERR_SET_SHORT_NAME;
1261 }
1262
1263 /*
1264  * A wrapper around NtCreateFile() to make it slightly more usable...
1265  * This uses the path currently constructed in ctx->pathbuf.
1266  *
1267  * Also, we always specify FILE_OPEN_FOR_BACKUP_INTENT and
1268  * FILE_OPEN_REPARSE_POINT.
1269  */
1270 static NTSTATUS
1271 do_create_file(PHANDLE FileHandle,
1272                ACCESS_MASK DesiredAccess,
1273                PLARGE_INTEGER AllocationSize,
1274                ULONG FileAttributes,
1275                ULONG CreateDisposition,
1276                ULONG CreateOptions,
1277                struct win32_apply_ctx *ctx)
1278 {
1279         return (*func_NtCreateFile)(FileHandle,
1280                                     DesiredAccess,
1281                                     &ctx->attr,
1282                                     &ctx->iosb,
1283                                     AllocationSize,
1284                                     FileAttributes,
1285                                     FILE_SHARE_VALID_FLAGS,
1286                                     CreateDisposition,
1287                                     CreateOptions |
1288                                         FILE_OPEN_FOR_BACKUP_INTENT |
1289                                         FILE_OPEN_REPARSE_POINT,
1290                                     NULL,
1291                                     0);
1292 }
1293
1294 /* Like do_create_file(), but builds the extraction path of the @dentry first.
1295  */
1296 static NTSTATUS
1297 create_file(PHANDLE FileHandle,
1298             ACCESS_MASK DesiredAccess,
1299             PLARGE_INTEGER AllocationSize,
1300             ULONG FileAttributes,
1301             ULONG CreateDisposition,
1302             ULONG CreateOptions,
1303             const struct wim_dentry *dentry,
1304             struct win32_apply_ctx *ctx)
1305 {
1306         build_extraction_path(dentry, ctx);
1307         return do_create_file(FileHandle,
1308                               DesiredAccess,
1309                               AllocationSize,
1310                               FileAttributes,
1311                               CreateDisposition,
1312                               CreateOptions,
1313                               ctx);
1314 }
1315
1316 static int
1317 delete_file_or_stream(struct win32_apply_ctx *ctx)
1318 {
1319         NTSTATUS status;
1320         HANDLE h;
1321         FILE_DISPOSITION_INFORMATION disposition_info;
1322         FILE_BASIC_INFORMATION basic_info;
1323         bool retried = false;
1324
1325         status = do_create_file(&h,
1326                                 DELETE,
1327                                 NULL,
1328                                 0,
1329                                 FILE_OPEN,
1330                                 FILE_NON_DIRECTORY_FILE,
1331                                 ctx);
1332         if (unlikely(!NT_SUCCESS(status))) {
1333                 winnt_error(status, L"Can't open \"%ls\" for deletion",
1334                             current_path(ctx));
1335                 return WIMLIB_ERR_OPEN;
1336         }
1337
1338 retry:
1339         disposition_info.DoDeleteFile = TRUE;
1340         status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1341                                               &disposition_info,
1342                                               sizeof(disposition_info),
1343                                               FileDispositionInformation);
1344         (*func_NtClose)(h);
1345         if (likely(NT_SUCCESS(status)))
1346                 return 0;
1347
1348         if (status == STATUS_CANNOT_DELETE && !retried) {
1349                 /* Clear file attributes and try again.  This is necessary for
1350                  * FILE_ATTRIBUTE_READONLY files.  */
1351                 status = do_create_file(&h,
1352                                         FILE_WRITE_ATTRIBUTES | DELETE,
1353                                         NULL,
1354                                         0,
1355                                         FILE_OPEN,
1356                                         FILE_NON_DIRECTORY_FILE,
1357                                         ctx);
1358                 if (!NT_SUCCESS(status)) {
1359                         winnt_error(status,
1360                                     L"Can't open \"%ls\" to reset attributes",
1361                                     current_path(ctx));
1362                         return WIMLIB_ERR_OPEN;
1363                 }
1364                 memset(&basic_info, 0, sizeof(basic_info));
1365                 basic_info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
1366                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1367                                                       &basic_info,
1368                                                       sizeof(basic_info),
1369                                                       FileBasicInformation);
1370                 if (!NT_SUCCESS(status)) {
1371                         winnt_error(status,
1372                                     L"Can't reset file attributes on \"%ls\"",
1373                                     current_path(ctx));
1374                         (*func_NtClose)(h);
1375                         return WIMLIB_ERR_SET_ATTRIBUTES;
1376                 }
1377                 retried = true;
1378                 goto retry;
1379         }
1380         winnt_error(status, L"Can't delete \"%ls\"", current_path(ctx));
1381         return WIMLIB_ERR_OPEN;
1382 }
1383
1384 /*
1385  * Create a nondirectory file or named data stream at the current path,
1386  * superseding any that already exists at that path.  If successful, return an
1387  * open handle to the file or named data stream.
1388  */
1389 static int
1390 supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret)
1391 {
1392         NTSTATUS status;
1393         bool retried = false;
1394
1395         /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that
1396          * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be.  */
1397 retry:
1398         status = do_create_file(h_ret,
1399                                 GENERIC_READ | GENERIC_WRITE | DELETE,
1400                                 NULL,
1401                                 FILE_ATTRIBUTE_SYSTEM,
1402                                 FILE_CREATE,
1403                                 FILE_NON_DIRECTORY_FILE,
1404                                 ctx);
1405         if (likely(NT_SUCCESS(status)))
1406                 return 0;
1407
1408         /* STATUS_OBJECT_NAME_COLLISION means that the file or stream already
1409          * exists.  Delete the existing file or stream, then try again.
1410          *
1411          * Note: we don't use FILE_OVERWRITE_IF or FILE_SUPERSEDE because of
1412          * problems with certain file attributes, especially
1413          * FILE_ATTRIBUTE_ENCRYPTED.  FILE_SUPERSEDE is also broken in the
1414          * Windows PE ramdisk.  */
1415         if (status == STATUS_OBJECT_NAME_COLLISION && !retried) {
1416                 int ret = delete_file_or_stream(ctx);
1417                 if (ret)
1418                         return ret;
1419                 retried = true;
1420                 goto retry;
1421         }
1422         winnt_error(status, L"Can't create \"%ls\"", current_path(ctx));
1423         return WIMLIB_ERR_OPEN;
1424 }
1425
1426 /*
1427  * Create empty named data streams for the specified file, if there are any.
1428  *
1429  * Since these won't have blob descriptors, they won't show up in the call to
1430  * extract_blob_list().  Hence the need for the special case.
1431  */
1432 static int
1433 create_empty_named_data_streams(const struct wim_dentry *dentry,
1434                                 struct win32_apply_ctx *ctx)
1435 {
1436         const struct wim_inode *inode = dentry->d_inode;
1437         bool path_modified = false;
1438         int ret = 0;
1439
1440         if (!ctx->common.supported_features.named_data_streams)
1441                 return 0;
1442
1443         for (unsigned i = 0; i < inode->i_num_streams; i++) {
1444                 const struct wim_inode_stream *strm = &inode->i_streams[i];
1445                 HANDLE h;
1446
1447                 if (!stream_is_named_data_stream(strm) ||
1448                     stream_blob_resolved(strm) != NULL)
1449                         continue;
1450
1451                 build_extraction_path_with_ads(dentry, ctx,
1452                                                strm->stream_name,
1453                                                utf16le_len_chars(strm->stream_name));
1454                 path_modified = true;
1455                 ret = supersede_file_or_stream(ctx, &h);
1456                 if (ret)
1457                         break;
1458                 (*func_NtClose)(h);
1459         }
1460         /* Restore the path to the dentry itself  */
1461         if (path_modified)
1462                 build_extraction_path(dentry, ctx);
1463         return ret;
1464 }
1465
1466 /*
1467  * Creates the directory named by @dentry, or uses an existing directory at that
1468  * location.  If necessary, sets the short name and/or fixes compression and
1469  * encryption attributes.
1470  *
1471  * Returns 0, WIMLIB_ERR_MKDIR, or WIMLIB_ERR_SET_SHORT_NAME.
1472  */
1473 static int
1474 create_directory(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
1475 {
1476         DWORD perms;
1477         NTSTATUS status;
1478         HANDLE h;
1479         int ret;
1480
1481         /* DELETE is needed for set_short_name(); GENERIC_READ and GENERIC_WRITE
1482          * are needed for adjust_compression_attribute().  */
1483         perms = GENERIC_READ | GENERIC_WRITE;
1484         if (!dentry_is_root(dentry))
1485                 perms |= DELETE;
1486
1487         /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that
1488          * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be.  */
1489         status = create_file(&h, perms, NULL, FILE_ATTRIBUTE_SYSTEM,
1490                              FILE_OPEN_IF, FILE_DIRECTORY_FILE, dentry, ctx);
1491         if (!NT_SUCCESS(status)) {
1492                 winnt_error(status, L"Can't create directory \"%ls\"",
1493                             current_path(ctx));
1494                 return WIMLIB_ERR_MKDIR;
1495         }
1496
1497         if (ctx->iosb.Information == FILE_OPENED) {
1498                 /* If we opened an existing directory, try to clear its file
1499                  * attributes.  As far as I know, this only actually makes a
1500                  * difference in the case where a FILE_ATTRIBUTE_READONLY
1501                  * directory has a named data stream which needs to be
1502                  * extracted.  You cannot create a named data stream of such a
1503                  * directory, even though this contradicts Microsoft's
1504                  * documentation for FILE_ATTRIBUTE_READONLY which states it is
1505                  * not honored for directories!  */
1506                 FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL };
1507                 (*func_NtSetInformationFile)(h, &ctx->iosb, &basic_info,
1508                                              sizeof(basic_info), FileBasicInformation);
1509         }
1510
1511         if (!dentry_is_root(dentry)) {
1512                 ret = set_short_name(h, dentry, ctx);
1513                 if (ret)
1514                         goto out;
1515         }
1516
1517         ret = adjust_compression_attribute(h, dentry, ctx);
1518 out:
1519         (*func_NtClose)(h);
1520         return ret;
1521 }
1522
1523 /*
1524  * Create all the directories being extracted, other than the target directory
1525  * itself.
1526  *
1527  * Note: we don't honor directory hard links.  However, we don't allow them to
1528  * exist in WIM images anyway (see inode_fixup.c).
1529  */
1530 static int
1531 create_directories(struct list_head *dentry_list,
1532                    struct win32_apply_ctx *ctx)
1533 {
1534         const struct wim_dentry *dentry;
1535         int ret;
1536
1537         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1538
1539                 if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
1540                         continue;
1541
1542                 /* Note: Here we include files with
1543                  * FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT, but we
1544                  * wait until later to actually set the reparse data.  */
1545
1546                 ret = create_directory(dentry, ctx);
1547
1548                 if (!ret)
1549                         ret = create_empty_named_data_streams(dentry, ctx);
1550
1551                 ret = check_apply_error(dentry, ctx, ret);
1552                 if (ret)
1553                         return ret;
1554
1555                 ret = report_file_created(&ctx->common);
1556                 if (ret)
1557                         return ret;
1558         }
1559         return 0;
1560 }
1561
1562 /*
1563  * Creates the nondirectory file named by @dentry.
1564  *
1565  * On success, returns an open handle to the file in @h_ret, with GENERIC_READ,
1566  * GENERIC_WRITE, and DELETE access.  Also, the path to the file will be saved
1567  * in ctx->pathbuf.  On failure, returns an error code.
1568  */
1569 static int
1570 create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
1571                           struct win32_apply_ctx *ctx)
1572 {
1573         int ret;
1574         HANDLE h;
1575
1576         build_extraction_path(dentry, ctx);
1577
1578         ret = supersede_file_or_stream(ctx, &h);
1579         if (ret)
1580                 goto out;
1581
1582         ret = adjust_compression_attribute(h, dentry, ctx);
1583         if (ret)
1584                 goto out_close;
1585
1586         ret = create_empty_named_data_streams(dentry, ctx);
1587         if (ret)
1588                 goto out_close;
1589
1590         *h_ret = h;
1591         return 0;
1592
1593 out_close:
1594         (*func_NtClose)(h);
1595 out:
1596         return ret;
1597 }
1598
1599 /* Creates a hard link at the location named by @dentry to the file represented
1600  * by the open handle @h.  Or, if the target volume does not support hard links,
1601  * create a separate file instead.  */
1602 static int
1603 create_link(HANDLE h, const struct wim_dentry *dentry,
1604             struct win32_apply_ctx *ctx)
1605 {
1606         if (ctx->common.supported_features.hard_links) {
1607
1608                 build_extraction_path(dentry, ctx);
1609
1610                 size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
1611                                  ctx->pathbuf.Length + sizeof(wchar_t);
1612                 u8 buf[bufsize] _aligned_attribute(8);
1613                 FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
1614                 NTSTATUS status;
1615
1616                 info->ReplaceIfExists = TRUE;
1617                 info->RootDirectory = ctx->attr.RootDirectory;
1618                 info->FileNameLength = ctx->pathbuf.Length;
1619                 memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
1620                 info->FileName[info->FileNameLength / 2] = L'\0';
1621
1622                 /* Note: the null terminator isn't actually necessary,
1623                  * but if you don't add the extra character, you get
1624                  * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
1625                  * happens to be 2  */
1626
1627                 status = (*func_NtSetInformationFile)(h, &ctx->iosb,
1628                                                       info, bufsize,
1629                                                       FileLinkInformation);
1630                 if (NT_SUCCESS(status))
1631                         return 0;
1632                 winnt_error(status, L"Failed to create link \"%ls\"",
1633                             current_path(ctx));
1634                 return WIMLIB_ERR_LINK;
1635         } else {
1636                 HANDLE h2;
1637                 int ret;
1638
1639                 ret = create_nondirectory_inode(&h2, dentry, ctx);
1640                 if (ret)
1641                         return ret;
1642
1643                 (*func_NtClose)(h2);
1644                 return 0;
1645         }
1646 }
1647
1648 /* Given an inode (represented by the open handle @h) for which one link has
1649  * been created (named by @first_dentry), create the other links.
1650  *
1651  * Or, if the target volume does not support hard links, create separate files.
1652  *
1653  * Note: This uses ctx->pathbuf and does not reset it.
1654  */
1655 static int
1656 create_links(HANDLE h, const struct wim_dentry *first_dentry,
1657              struct win32_apply_ctx *ctx)
1658 {
1659         const struct wim_inode *inode;
1660         const struct list_head *next;
1661         const struct wim_dentry *dentry;
1662         int ret;
1663
1664         inode = first_dentry->d_inode;
1665         next = inode->i_extraction_aliases.next;
1666         do {
1667                 dentry = list_entry(next, struct wim_dentry,
1668                                     d_extraction_alias_node);
1669                 if (dentry != first_dentry) {
1670                         ret = create_link(h, dentry, ctx);
1671                         if (ret)
1672                                 return ret;
1673                 }
1674                 next = next->next;
1675         } while (next != &inode->i_extraction_aliases);
1676         return 0;
1677 }
1678
1679 /* Create a nondirectory file, including all links.  */
1680 static int
1681 create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx)
1682 {
1683         struct wim_dentry *first_dentry;
1684         HANDLE h;
1685         int ret;
1686
1687         first_dentry = first_extraction_alias(inode);
1688
1689         /* Create first link.  */
1690         ret = create_nondirectory_inode(&h, first_dentry, ctx);
1691         if (ret)
1692                 return ret;
1693
1694         /* Set short name.  */
1695         ret = set_short_name(h, first_dentry, ctx);
1696
1697         /* Create additional links, OR if hard links are not supported just
1698          * create more files.  */
1699         if (!ret)
1700                 ret = create_links(h, first_dentry, ctx);
1701
1702         /* "WIMBoot" extraction: set external backing by the WIM file if needed.  */
1703         if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
1704                 ret = set_external_backing(h, inode, ctx);
1705
1706         (*func_NtClose)(h);
1707         return ret;
1708 }
1709
1710 /* Create all the nondirectory files being extracted, including all aliases
1711  * (hard links).  */
1712 static int
1713 create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
1714 {
1715         struct wim_dentry *dentry;
1716         struct wim_inode *inode;
1717         int ret;
1718
1719         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
1720                 inode = dentry->d_inode;
1721                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
1722                         continue;
1723                 /* Call create_nondirectory() only once per inode  */
1724                 if (dentry == inode_first_extraction_dentry(inode)) {
1725                         ret = create_nondirectory(inode, ctx);
1726                         ret = check_apply_error(dentry, ctx, ret);
1727                         if (ret)
1728                                 return ret;
1729                 }
1730                 ret = report_file_created(&ctx->common);
1731                 if (ret)
1732                         return ret;
1733         }
1734         return 0;
1735 }
1736
1737 static void
1738 close_handles(struct win32_apply_ctx *ctx)
1739 {
1740         for (unsigned i = 0; i < ctx->num_open_handles; i++)
1741                 (*func_NtClose)(ctx->open_handles[i]);
1742 }
1743
1744 /* Prepare to read the next blob, which has size @blob_size, into an in-memory
1745  * buffer.  */
1746 static bool
1747 prepare_data_buffer(struct win32_apply_ctx *ctx, u64 blob_size)
1748 {
1749         if (blob_size > ctx->data_buffer_size) {
1750                 /* Larger buffer needed.  */
1751                 void *new_buffer;
1752                 if ((size_t)blob_size != blob_size)
1753                         return false;
1754                 new_buffer = REALLOC(ctx->data_buffer, blob_size);
1755                 if (!new_buffer)
1756                         return false;
1757                 ctx->data_buffer = new_buffer;
1758                 ctx->data_buffer_size = blob_size;
1759         }
1760         /* On the first call this changes data_buffer_ptr from NULL, which tells
1761          * extract_chunk() that the data buffer needs to be filled while reading
1762          * the stream data.  */
1763         ctx->data_buffer_ptr = ctx->data_buffer;
1764         return true;
1765 }
1766
1767 static int
1768 begin_extract_blob_instance(const struct blob_descriptor *blob,
1769                             struct wim_dentry *dentry,
1770                             const struct wim_inode_stream *strm,
1771                             struct win32_apply_ctx *ctx)
1772 {
1773         FILE_ALLOCATION_INFORMATION alloc_info;
1774         HANDLE h;
1775         NTSTATUS status;
1776
1777         if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) {
1778                 /* We can't write the reparse point stream directly; we must set
1779                  * it with FSCTL_SET_REPARSE_POINT, which requires that all the
1780                  * data be available.  So, stage the data in a buffer.  */
1781                 if (!prepare_data_buffer(ctx, blob->size))
1782                         return WIMLIB_ERR_NOMEM;
1783                 list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
1784                 return 0;
1785         }
1786
1787         if (unlikely(strm->stream_type == STREAM_TYPE_EFSRPC_RAW_DATA)) {
1788                 /* We can't write encrypted files directly; we must use
1789                  * WriteEncryptedFileRaw(), which requires providing the data
1790                  * through a callback function.  This can't easily be combined
1791                  * with our own callback-based approach.
1792                  *
1793                  * The current workaround is to simply read the blob into memory
1794                  * and write the encrypted file from that.
1795                  *
1796                  * TODO: This isn't sufficient for extremely large encrypted
1797                  * files.  Perhaps we should create an extra thread to write
1798                  * such files...  */
1799                 if (!prepare_data_buffer(ctx, blob->size))
1800                         return WIMLIB_ERR_NOMEM;
1801                 list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries);
1802                 return 0;
1803         }
1804
1805         /* It's a data stream (may be unnamed or named).  */
1806         wimlib_assert(strm->stream_type == STREAM_TYPE_DATA);
1807
1808         if (ctx->num_open_handles == MAX_OPEN_FILES) {
1809                 /* XXX: Fix this.  But because of the checks in
1810                  * extract_blob_list(), this can now only happen on a filesystem
1811                  * that does not support hard links.  */
1812                 ERROR("Can't extract data: too many open files!");
1813                 return WIMLIB_ERR_UNSUPPORTED;
1814         }
1815
1816
1817         if (unlikely(stream_is_named(strm))) {
1818                 build_extraction_path_with_ads(dentry, ctx,
1819                                                strm->stream_name,
1820                                                utf16le_len_chars(strm->stream_name));
1821         } else {
1822                 build_extraction_path(dentry, ctx);
1823         }
1824
1825
1826         /* Open a new handle  */
1827         status = do_create_file(&h,
1828                                 FILE_WRITE_DATA | SYNCHRONIZE,
1829                                 NULL, 0, FILE_OPEN_IF,
1830                                 FILE_SEQUENTIAL_ONLY |
1831                                         FILE_SYNCHRONOUS_IO_NONALERT,
1832                                 ctx);
1833         if (!NT_SUCCESS(status)) {
1834                 winnt_error(status, L"Can't open \"%ls\" for writing",
1835                             current_path(ctx));
1836                 return WIMLIB_ERR_OPEN;
1837         }
1838
1839         ctx->open_handles[ctx->num_open_handles++] = h;
1840
1841         /* Allocate space for the data.  */
1842         alloc_info.AllocationSize.QuadPart = blob->size;
1843         (*func_NtSetInformationFile)(h, &ctx->iosb,
1844                                      &alloc_info, sizeof(alloc_info),
1845                                      FileAllocationInformation);
1846         return 0;
1847 }
1848
1849 /* Set the reparse data @rpbuf of length @rpbuflen on the extracted file
1850  * corresponding to the WIM dentry @dentry.  */
1851 static int
1852 do_set_reparse_data(const struct wim_dentry *dentry,
1853                     const void *rpbuf, u16 rpbuflen,
1854                     struct win32_apply_ctx *ctx)
1855 {
1856         NTSTATUS status;
1857         HANDLE h;
1858
1859         status = create_file(&h, GENERIC_WRITE, NULL,
1860                              0, FILE_OPEN, 0, dentry, ctx);
1861         if (!NT_SUCCESS(status))
1862                 goto fail;
1863
1864         status = (*func_NtFsControlFile)(h, NULL, NULL, NULL,
1865                                          &ctx->iosb, FSCTL_SET_REPARSE_POINT,
1866                                          (void *)rpbuf, rpbuflen,
1867                                          NULL, 0);
1868         (*func_NtClose)(h);
1869
1870         if (NT_SUCCESS(status))
1871                 return 0;
1872
1873         /* On Windows, by default only the Administrator can create symbolic
1874          * links for some reason.  By default we just issue a warning if this
1875          * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
1876          * to get a hard error.  */
1877         if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
1878             && (status == STATUS_PRIVILEGE_NOT_HELD ||
1879                 status == STATUS_ACCESS_DENIED)
1880             && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1881                 dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1882         {
1883                 WARNING("Can't create symbolic link \"%ls\"!              \n"
1884                         "          (Need Administrator rights, or at least "
1885                         "the\n"
1886                         "          SeCreateSymbolicLink privilege.)",
1887                         current_path(ctx));
1888                 return 0;
1889         }
1890
1891 fail:
1892         winnt_error(status, L"Can't set reparse data on \"%ls\"",
1893                     current_path(ctx));
1894         return WIMLIB_ERR_SET_REPARSE_DATA;
1895 }
1896
1897 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1898  * pointer to the suffix of the path that begins with the device directly, such
1899  * as e:\Windows\System32.  */
1900 static const wchar_t *
1901 skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars)
1902 {
1903         static const wchar_t * const dirs[] = {
1904                 L"\\??\\",
1905                 L"\\DosDevices\\",
1906                 L"\\Device\\",
1907         };
1908         size_t first_dir_len = 0;
1909         const wchar_t * const end = path + path_nchars;
1910
1911         for (size_t i = 0; i < ARRAY_LEN(dirs); i++) {
1912                 size_t len = wcslen(dirs[i]);
1913                 if (len <= (end - path) && !wcsnicmp(path, dirs[i], len)) {
1914                         first_dir_len = len;
1915                         break;
1916                 }
1917         }
1918         if (first_dir_len == 0)
1919                 return path;
1920         path += first_dir_len;
1921         while (path != end && *path == L'\\')
1922                 path++;
1923         return path;
1924 }
1925
1926 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
1927  * pointer to the suffix of the path that is device-relative, such as
1928  * Windows\System32.
1929  *
1930  * The path has an explicit length and is not necessarily null terminated.
1931  *
1932  * If the path just something like \??\e: then the returned pointer will point
1933  * just past the colon.  In this case the length of the result will be 0
1934  * characters.  */
1935 static const wchar_t *
1936 get_device_relative_path(const wchar_t *path, size_t path_nchars)
1937 {
1938         const wchar_t * const orig_path = path;
1939         const wchar_t * const end = path + path_nchars;
1940
1941         path = skip_nt_toplevel_component(path, path_nchars);
1942         if (path == orig_path)
1943                 return orig_path;
1944
1945         path = wmemchr(path, L'\\', (end - path));
1946         if (!path)
1947                 return end;
1948         do {
1949                 path++;
1950         } while (path != end && *path == L'\\');
1951         return path;
1952 }
1953
1954 /*
1955  * Given a reparse point buffer for a symbolic link or junction, adjust its
1956  * contents so that the target of the link is consistent with the new location
1957  * of the files.
1958  */
1959 static void
1960 try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, struct win32_apply_ctx *ctx)
1961 {
1962         struct reparse_data rpdata;
1963         size_t orig_subst_name_nchars;
1964         const wchar_t *relpath;
1965         size_t relpath_nchars;
1966         size_t target_ntpath_nchars;
1967         size_t fixed_subst_name_nchars;
1968         const wchar_t *fixed_print_name;
1969         size_t fixed_print_name_nchars;
1970
1971         if (parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata)) {
1972                 /* Do nothing if the reparse data is invalid.  */
1973                 return;
1974         }
1975
1976         if (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK &&
1977             (rpdata.rpflags & SYMBOLIC_LINK_RELATIVE))
1978         {
1979                 /* Do nothing if it's a relative symbolic link.  */
1980                 return;
1981         }
1982
1983         /* Build the new substitute name from the NT namespace path to the
1984          * target directory, then a path separator, then the "device relative"
1985          * part of the old substitute name.  */
1986
1987         orig_subst_name_nchars = rpdata.substitute_name_nbytes / sizeof(wchar_t);
1988
1989         relpath = get_device_relative_path(rpdata.substitute_name,
1990                                            orig_subst_name_nchars);
1991         relpath_nchars = orig_subst_name_nchars -
1992                          (relpath - rpdata.substitute_name);
1993
1994         target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t);
1995
1996         fixed_subst_name_nchars = target_ntpath_nchars;
1997         if (relpath_nchars)
1998                 fixed_subst_name_nchars += 1 + relpath_nchars;
1999         wchar_t fixed_subst_name[fixed_subst_name_nchars];
2000
2001         wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer,
2002                 target_ntpath_nchars);
2003         if (relpath_nchars) {
2004                 fixed_subst_name[target_ntpath_nchars] = L'\\';
2005                 wmemcpy(&fixed_subst_name[target_ntpath_nchars + 1],
2006                         relpath, relpath_nchars);
2007         }
2008         /* Doesn't need to be null-terminated.  */
2009
2010         /* Print name should be Win32, but not all NT names can even be
2011          * translated to Win32 names.  But we can at least delete the top-level
2012          * directory, such as \??\, and this will have the expected result in
2013          * the usual case.  */
2014         fixed_print_name = skip_nt_toplevel_component(fixed_subst_name,
2015                                                       fixed_subst_name_nchars);
2016         fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name -
2017                                                              fixed_subst_name);
2018
2019         rpdata.substitute_name = fixed_subst_name;
2020         rpdata.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t);
2021         rpdata.print_name = (wchar_t *)fixed_print_name;
2022         rpdata.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t);
2023         make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
2024 }
2025
2026 /* Sets reparse data on the specified file.  This handles "fixing" the targets
2027  * of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX was
2028  * specified.  */
2029 static int
2030 set_reparse_data(const struct wim_dentry *dentry,
2031                  const void *_rpbuf, u16 rpbuflen, struct win32_apply_ctx *ctx)
2032 {
2033         const struct wim_inode *inode = dentry->d_inode;
2034         const void *rpbuf = _rpbuf;
2035
2036         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
2037             && !inode->i_not_rpfixed
2038             && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
2039                 inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
2040         {
2041                 memcpy(&ctx->rpfixbuf, _rpbuf, rpbuflen);
2042                 try_rpfix((u8 *)&ctx->rpfixbuf, &rpbuflen, ctx);
2043                 rpbuf = &ctx->rpfixbuf;
2044         }
2045         return do_set_reparse_data(dentry, rpbuf, rpbuflen, ctx);
2046
2047 }
2048
2049 /* Import the next block of raw encrypted data  */
2050 static DWORD WINAPI
2051 import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length)
2052 {
2053         struct win32_apply_ctx *ctx = pvCallbackContext;
2054         ULONG copy_len;
2055
2056         copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length);
2057         memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len);
2058         ctx->encrypted_offset += copy_len;
2059         *Length = copy_len;
2060         return ERROR_SUCCESS;
2061 }
2062
2063 /*
2064  * Write the raw encrypted data to the already-created file (or directory)
2065  * corresponding to @dentry.
2066  *
2067  * The raw encrypted data is provided in ctx->data_buffer, and its size is
2068  * ctx->encrypted_size.
2069  *
2070  * This function may close the target directory, in which case the caller needs
2071  * to re-open it if needed.
2072  */
2073 static int
2074 extract_encrypted_file(const struct wim_dentry *dentry,
2075                        struct win32_apply_ctx *ctx)
2076 {
2077         void *rawctx;
2078         DWORD err;
2079         ULONG flags;
2080         bool retried;
2081
2082         /* Temporarily build a Win32 path for OpenEncryptedFileRaw()  */
2083         build_win32_extraction_path(dentry, ctx);
2084
2085         flags = CREATE_FOR_IMPORT | OVERWRITE_HIDDEN;
2086         if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
2087                 flags |= CREATE_FOR_DIR;
2088
2089         retried = false;
2090 retry:
2091         err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, flags, &rawctx);
2092         if (err == ERROR_SHARING_VIOLATION && !retried) {
2093                 /* This can be caused by the handle we have open to the target
2094                  * directory.  Try closing it temporarily.  */
2095                 close_target_directory(ctx);
2096                 retried = true;
2097                 goto retry;
2098         }
2099
2100         /* Restore the NT namespace path  */
2101         build_extraction_path(dentry, ctx);
2102
2103         if (err != ERROR_SUCCESS) {
2104                 win32_error(err, L"Can't open \"%ls\" for encrypted import",
2105                             current_path(ctx));
2106                 return WIMLIB_ERR_OPEN;
2107         }
2108
2109         ctx->encrypted_offset = 0;
2110
2111         err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx);
2112
2113         CloseEncryptedFileRaw(rawctx);
2114
2115         if (err != ERROR_SUCCESS) {
2116                 win32_error(err, L"Can't import encrypted file \"%ls\"",
2117                             current_path(ctx));
2118                 return WIMLIB_ERR_WRITE;
2119         }
2120
2121         return 0;
2122 }
2123
2124 /* Called when starting to read a blob for extraction on Windows  */
2125 static int
2126 begin_extract_blob(struct blob_descriptor *blob, void *_ctx)
2127 {
2128         struct win32_apply_ctx *ctx = _ctx;
2129         const struct blob_extraction_target *targets = blob_extraction_targets(blob);
2130         int ret;
2131
2132         ctx->num_open_handles = 0;
2133         ctx->data_buffer_ptr = NULL;
2134         INIT_LIST_HEAD(&ctx->reparse_dentries);
2135         INIT_LIST_HEAD(&ctx->encrypted_dentries);
2136
2137         for (u32 i = 0; i < blob->out_refcnt; i++) {
2138                 const struct wim_inode *inode = targets[i].inode;
2139                 const struct wim_inode_stream *strm = targets[i].stream;
2140                 struct wim_dentry *dentry;
2141
2142                 /* A copy of the blob needs to be extracted to @inode.  */
2143
2144                 if (ctx->common.supported_features.hard_links) {
2145                         dentry = inode_first_extraction_dentry(inode);
2146                         ret = begin_extract_blob_instance(blob, dentry, strm, ctx);
2147                         ret = check_apply_error(dentry, ctx, ret);
2148                         if (ret)
2149                                 goto fail;
2150                 } else {
2151                         /* Hard links not supported.  Extract the blob
2152                          * separately to each alias of the inode.  */
2153                         struct list_head *next;
2154
2155                         next = inode->i_extraction_aliases.next;
2156                         do {
2157                                 dentry = list_entry(next, struct wim_dentry,
2158                                                     d_extraction_alias_node);
2159                                 ret = begin_extract_blob_instance(blob, dentry, strm, ctx);
2160                                 ret = check_apply_error(dentry, ctx, ret);
2161                                 if (ret)
2162                                         goto fail;
2163                                 next = next->next;
2164                         } while (next != &inode->i_extraction_aliases);
2165                 }
2166         }
2167
2168         return 0;
2169
2170 fail:
2171         close_handles(ctx);
2172         return ret;
2173 }
2174
2175 /* Called when the next chunk of a blob has been read for extraction on Windows
2176  */
2177 static int
2178 extract_chunk(const void *chunk, size_t size, void *_ctx)
2179 {
2180         struct win32_apply_ctx *ctx = _ctx;
2181
2182         /* Write the data chunk to each open handle  */
2183         for (unsigned i = 0; i < ctx->num_open_handles; i++) {
2184                 u8 *bufptr = (u8 *)chunk;
2185                 size_t bytes_remaining = size;
2186                 NTSTATUS status;
2187                 while (bytes_remaining) {
2188                         ULONG count = min(0xFFFFFFFF, bytes_remaining);
2189
2190                         status = (*func_NtWriteFile)(ctx->open_handles[i],
2191                                                      NULL, NULL, NULL,
2192                                                      &ctx->iosb, bufptr, count,
2193                                                      NULL, NULL);
2194                         if (!NT_SUCCESS(status)) {
2195                                 winnt_error(status, L"Error writing data to target volume");
2196                                 return WIMLIB_ERR_WRITE;
2197                         }
2198                         bufptr += ctx->iosb.Information;
2199                         bytes_remaining -= ctx->iosb.Information;
2200                 }
2201         }
2202
2203         /* Copy the data chunk into the buffer (if needed)  */
2204         if (ctx->data_buffer_ptr)
2205                 ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
2206                                                chunk, size);
2207         return 0;
2208 }
2209
2210 /* Called when a blob has been fully read for extraction on Windows  */
2211 static int
2212 end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
2213 {
2214         struct win32_apply_ctx *ctx = _ctx;
2215         int ret;
2216         const struct wim_dentry *dentry;
2217
2218         close_handles(ctx);
2219
2220         if (status)
2221                 return status;
2222
2223         if (likely(!ctx->data_buffer_ptr))
2224                 return 0;
2225
2226         if (!list_empty(&ctx->reparse_dentries)) {
2227                 if (blob->size > REPARSE_DATA_MAX_SIZE) {
2228                         dentry = list_first_entry(&ctx->reparse_dentries,
2229                                                   struct wim_dentry, tmp_list);
2230                         build_extraction_path(dentry, ctx);
2231                         ERROR("Reparse data of \"%ls\" has size "
2232                               "%"PRIu64" bytes (exceeds %u bytes)",
2233                               current_path(ctx), blob->size,
2234                               REPARSE_DATA_MAX_SIZE);
2235                         ret = WIMLIB_ERR_INVALID_REPARSE_DATA;
2236                         return check_apply_error(dentry, ctx, ret);
2237                 }
2238                 /* In the WIM format, reparse point streams are just the reparse
2239                  * data and omit the header.  But we can reconstruct the header.
2240                  */
2241                 memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, blob->size);
2242                 ctx->rpbuf.rpdatalen = blob->size;
2243                 ctx->rpbuf.rpreserved = 0;
2244                 list_for_each_entry(dentry, &ctx->reparse_dentries, tmp_list) {
2245                         ctx->rpbuf.rptag = dentry->d_inode->i_reparse_tag;
2246                         ret = set_reparse_data(dentry, &ctx->rpbuf,
2247                                                blob->size + REPARSE_DATA_OFFSET,
2248                                                ctx);
2249                         ret = check_apply_error(dentry, ctx, ret);
2250                         if (ret)
2251                                 return ret;
2252                 }
2253         }
2254
2255         if (!list_empty(&ctx->encrypted_dentries)) {
2256                 ctx->encrypted_size = blob->size;
2257                 list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) {
2258                         ret = extract_encrypted_file(dentry, ctx);
2259                         ret = check_apply_error(dentry, ctx, ret);
2260                         if (ret)
2261                                 return ret;
2262                         /* Re-open the target directory if needed.  */
2263                         ret = open_target_directory(ctx);
2264                         if (ret)
2265                                 return ret;
2266                 }
2267         }
2268
2269         return 0;
2270 }
2271
2272 /* Attributes that can't be set directly  */
2273 #define SPECIAL_ATTRIBUTES                      \
2274         (FILE_ATTRIBUTE_REPARSE_POINT   |       \
2275          FILE_ATTRIBUTE_DIRECTORY       |       \
2276          FILE_ATTRIBUTE_ENCRYPTED       |       \
2277          FILE_ATTRIBUTE_SPARSE_FILE     |       \
2278          FILE_ATTRIBUTE_COMPRESSED)
2279
2280 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
2281  * handle @h.  */
2282 static NTSTATUS
2283 set_security_descriptor(HANDLE h, const void *_desc,
2284                         size_t desc_size, struct win32_apply_ctx *ctx)
2285 {
2286         SECURITY_INFORMATION info;
2287         NTSTATUS status;
2288         SECURITY_DESCRIPTOR_RELATIVE *desc;
2289
2290         /*
2291          * Ideally, we would just pass in the security descriptor buffer as-is.
2292          * But it turns out that Windows can mess up the security descriptor
2293          * even when using the low-level NtSetSecurityObject() function:
2294          *
2295          * - Windows will clear SE_DACL_AUTO_INHERITED if it is set in the
2296          *   passed buffer.  To actually get Windows to set
2297          *   SE_DACL_AUTO_INHERITED, the application must set the non-persistent
2298          *   flag SE_DACL_AUTO_INHERIT_REQ.  As usual, Microsoft didn't bother
2299          *   to properly document either of these flags.  It's unclear how
2300          *   important SE_DACL_AUTO_INHERITED actually is, but to be safe we use
2301          *   the SE_DACL_AUTO_INHERIT_REQ workaround to set it if needed.
2302          *
2303          * - The above also applies to the equivalent SACL flags,
2304          *   SE_SACL_AUTO_INHERITED and SE_SACL_AUTO_INHERIT_REQ.
2305          *
2306          * - If the application says that it's setting
2307          *   DACL_SECURITY_INFORMATION, then Windows sets SE_DACL_PRESENT in the
2308          *   resulting security descriptor, even if the security descriptor the
2309          *   application provided did not have a DACL.  This seems to be
2310          *   unavoidable, since omitting DACL_SECURITY_INFORMATION would cause a
2311          *   default DACL to remain.  Fortunately, this behavior seems harmless,
2312          *   since the resulting DACL will still be "null" --- but it will be
2313          *   "the other representation of null".
2314          *
2315          * - The above also applies to SACL_SECURITY_INFORMATION and
2316          *   SE_SACL_PRESENT.  Again, it's seemingly unavoidable but "harmless"
2317          *   that Windows changes the representation of a "null SACL".
2318          */
2319         if (likely(desc_size <= STACK_MAX)) {
2320                 desc = alloca(desc_size);
2321         } else {
2322                 desc = MALLOC(desc_size);
2323                 if (!desc)
2324                         return STATUS_NO_MEMORY;
2325         }
2326
2327         memcpy(desc, _desc, desc_size);
2328
2329         if (likely(desc_size >= 4)) {
2330
2331                 if (desc->Control & SE_DACL_AUTO_INHERITED)
2332                         desc->Control |= SE_DACL_AUTO_INHERIT_REQ;
2333
2334                 if (desc->Control & SE_SACL_AUTO_INHERITED)
2335                         desc->Control |= SE_SACL_AUTO_INHERIT_REQ;
2336         }
2337
2338         /*
2339          * More API insanity.  We want to set the entire security descriptor
2340          * as-is.  But all available APIs require specifying the specific parts
2341          * of the security descriptor being set.  Especially annoying is that
2342          * mandatory integrity labels are part of the SACL, but they aren't set
2343          * with SACL_SECURITY_INFORMATION.  Instead, applications must also
2344          * specify LABEL_SECURITY_INFORMATION (Windows Vista, Windows 7) or
2345          * BACKUP_SECURITY_INFORMATION (Windows 8).  But at least older versions
2346          * of Windows don't error out if you provide these newer flags...
2347          *
2348          * Also, if the process isn't running as Administrator, then it probably
2349          * doesn't have SE_RESTORE_PRIVILEGE.  In this case, it will always get
2350          * the STATUS_PRIVILEGE_NOT_HELD error by trying to set the SACL, even
2351          * if the security descriptor it provided did not have a SACL.  By
2352          * default, in this case we try to recover and set as much of the
2353          * security descriptor as possible --- potentially excluding the DACL, and
2354          * even the owner, as well as the SACL.
2355          */
2356
2357         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
2358                DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION |
2359                LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION;
2360
2361
2362         /*
2363          * It's also worth noting that SetFileSecurity() is unusable because it
2364          * doesn't request "backup semantics" when it opens the file internally.
2365          * NtSetSecurityObject() seems to be the best function to use in backup
2366          * applications.  (SetSecurityInfo() should also work, but it's harder
2367          * to use and must call NtSetSecurityObject() internally anyway.
2368          * BackupWrite() is theoretically usable as well, but it's inflexible
2369          * and poorly documented.)
2370          */
2371
2372 retry:
2373         status = (*func_NtSetSecurityObject)(h, info, desc);
2374         if (NT_SUCCESS(status))
2375                 goto out_maybe_free_desc;
2376
2377         /* Failed to set the requested parts of the security descriptor.  If the
2378          * error was permissions-related, try to set fewer parts of the security
2379          * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
2380         if ((status == STATUS_PRIVILEGE_NOT_HELD ||
2381              status == STATUS_ACCESS_DENIED) &&
2382             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
2383         {
2384                 if (info & SACL_SECURITY_INFORMATION) {
2385                         info &= ~(SACL_SECURITY_INFORMATION |
2386                                   LABEL_SECURITY_INFORMATION |
2387                                   BACKUP_SECURITY_INFORMATION);
2388                         ctx->partial_security_descriptors++;
2389                         goto retry;
2390                 }
2391                 if (info & DACL_SECURITY_INFORMATION) {
2392                         info &= ~DACL_SECURITY_INFORMATION;
2393                         goto retry;
2394                 }
2395                 if (info & OWNER_SECURITY_INFORMATION) {
2396                         info &= ~OWNER_SECURITY_INFORMATION;
2397                         goto retry;
2398                 }
2399                 /* Nothing left except GROUP, and if we removed it we
2400                  * wouldn't have anything at all.  */
2401         }
2402
2403         /* No part of the security descriptor could be set, or
2404          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security
2405          * descriptor could not be set.  */
2406         if (!(info & SACL_SECURITY_INFORMATION))
2407                 ctx->partial_security_descriptors--;
2408         ctx->no_security_descriptors++;
2409
2410 out_maybe_free_desc:
2411         if (unlikely(desc_size > STACK_MAX))
2412                 FREE(desc);
2413         return status;
2414 }
2415
2416 /* Set metadata on the open file @h from the WIM inode @inode.  */
2417 static int
2418 do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
2419                           struct win32_apply_ctx *ctx)
2420 {
2421         FILE_BASIC_INFORMATION info;
2422         NTSTATUS status;
2423
2424         /* Set security descriptor if present and not in NO_ACLS mode  */
2425         if (inode->i_security_id >= 0 &&
2426             !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
2427         {
2428                 const struct wim_security_data *sd;
2429                 const void *desc;
2430                 size_t desc_size;
2431
2432                 sd = wim_get_current_security_data(ctx->common.wim);
2433                 desc = sd->descriptors[inode->i_security_id];
2434                 desc_size = sd->sizes[inode->i_security_id];
2435
2436                 status = set_security_descriptor(h, desc, desc_size, ctx);
2437                 if (!NT_SUCCESS(status) &&
2438                     (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
2439                 {
2440                         winnt_error(status,
2441                                     L"Can't set security descriptor on \"%ls\"",
2442                                     current_path(ctx));
2443                         return WIMLIB_ERR_SET_SECURITY;
2444                 }
2445         }
2446
2447         /* Set attributes and timestamps  */
2448         info.CreationTime.QuadPart = inode->i_creation_time;
2449         info.LastAccessTime.QuadPart = inode->i_last_access_time;
2450         info.LastWriteTime.QuadPart = inode->i_last_write_time;
2451         info.ChangeTime.QuadPart = 0;
2452         if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) {
2453                 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2454         } else {
2455                 info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES;
2456                 if (info.FileAttributes == 0)
2457                         info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2458         }
2459
2460         status = (*func_NtSetInformationFile)(h, &ctx->iosb,
2461                                               &info, sizeof(info),
2462                                               FileBasicInformation);
2463         /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set
2464          * attributes on the root directory.  (Apparently because FAT doesn't
2465          * actually have a place to store those attributes!)  */
2466         if (!NT_SUCCESS(status)
2467             && !(status == STATUS_INVALID_PARAMETER &&
2468                  dentry_is_root(inode_first_extraction_dentry(inode))))
2469         {
2470                 winnt_error(status, L"Can't set basic metadata on \"%ls\"",
2471                             current_path(ctx));
2472                 return WIMLIB_ERR_SET_ATTRIBUTES;
2473         }
2474
2475         return 0;
2476 }
2477
2478 static int
2479 apply_metadata_to_file(const struct wim_dentry *dentry,
2480                        struct win32_apply_ctx *ctx)
2481 {
2482         const struct wim_inode *inode = dentry->d_inode;
2483         DWORD perms;
2484         HANDLE h;
2485         NTSTATUS status;
2486         int ret;
2487
2488         perms = FILE_WRITE_ATTRIBUTES | WRITE_DAC |
2489                 WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
2490
2491         build_extraction_path(dentry, ctx);
2492
2493         /* Open a handle with as many relevant permissions as possible.  */
2494         while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL,
2495                                                    0, FILE_OPEN, 0, ctx)))
2496         {
2497                 if (status == STATUS_PRIVILEGE_NOT_HELD ||
2498                     status == STATUS_ACCESS_DENIED)
2499                 {
2500                         if (perms & ACCESS_SYSTEM_SECURITY) {
2501                                 perms &= ~ACCESS_SYSTEM_SECURITY;
2502                                 continue;
2503                         }
2504                         if (perms & WRITE_DAC) {
2505                                 perms &= ~WRITE_DAC;
2506                                 continue;
2507                         }
2508                         if (perms & WRITE_OWNER) {
2509                                 perms &= ~WRITE_OWNER;
2510                                 continue;
2511                         }
2512                 }
2513                 winnt_error(status, L"Can't open \"%ls\" to set metadata",
2514                             current_path(ctx));
2515                 return WIMLIB_ERR_OPEN;
2516         }
2517
2518         ret = do_apply_metadata_to_file(h, inode, ctx);
2519
2520         (*func_NtClose)(h);
2521
2522         return ret;
2523 }
2524
2525 static int
2526 apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
2527 {
2528         const struct wim_dentry *dentry;
2529         int ret;
2530
2531         /* We go in reverse so that metadata is set on all a directory's
2532          * children before the directory itself.  This avoids any potential
2533          * problems with attributes, timestamps, or security descriptors.  */
2534         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node)
2535         {
2536                 ret = apply_metadata_to_file(dentry, ctx);
2537                 ret = check_apply_error(dentry, ctx, ret);
2538                 if (ret)
2539                         return ret;
2540                 ret = report_file_metadata_applied(&ctx->common);
2541                 if (ret)
2542                         return ret;
2543         }
2544         return 0;
2545 }
2546
2547 /* Issue warnings about problems during the extraction for which warnings were
2548  * not already issued (due to the high number of potential warnings if we issued
2549  * them per-file).  */
2550 static void
2551 do_warnings(const struct win32_apply_ctx *ctx)
2552 {
2553         if (ctx->partial_security_descriptors == 0
2554             && ctx->no_security_descriptors == 0
2555             && ctx->num_set_short_name_failures == 0
2556         #if 0
2557             && ctx->num_remove_short_name_failures == 0
2558         #endif
2559             )
2560                 return;
2561
2562         WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
2563                 ctx->common.target);
2564         if (ctx->num_set_short_name_failures) {
2565                 WARNING("- Could not set short names on %lu files or directories",
2566                         ctx->num_set_short_name_failures);
2567         }
2568 #if 0
2569         if (ctx->num_remove_short_name_failures) {
2570                 WARNING("- Could not remove short names on %lu files or directories"
2571                         "          (This is expected on Vista and earlier)",
2572                         ctx->num_remove_short_name_failures);
2573         }
2574 #endif
2575         if (ctx->partial_security_descriptors) {
2576                 WARNING("- Could only partially set the security descriptor\n"
2577                         "            on %lu files or directories.",
2578                         ctx->partial_security_descriptors);
2579         }
2580         if (ctx->no_security_descriptors) {
2581                 WARNING("- Could not set security descriptor at all\n"
2582                         "            on %lu files or directories.",
2583                         ctx->no_security_descriptors);
2584         }
2585         if (ctx->partial_security_descriptors || ctx->no_security_descriptors) {
2586                 WARNING("To fully restore all security descriptors, run the program\n"
2587                         "          with Administrator rights.");
2588         }
2589 }
2590
2591 static u64
2592 count_dentries(const struct list_head *dentry_list)
2593 {
2594         const struct list_head *cur;
2595         u64 count = 0;
2596
2597         list_for_each(cur, dentry_list)
2598                 count++;
2599
2600         return count;
2601 }
2602
2603 /* Extract files from a WIM image to a directory on Windows  */
2604 static int
2605 win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
2606 {
2607         int ret;
2608         struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
2609         u64 dentry_count;
2610
2611         ret = prepare_target(dentry_list, ctx);
2612         if (ret)
2613                 goto out;
2614
2615         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
2616                 ret = start_wimboot_extraction(dentry_list, ctx);
2617                 if (ret)
2618                         goto out;
2619         }
2620
2621         dentry_count = count_dentries(dentry_list);
2622
2623         ret = start_file_structure_phase(&ctx->common, dentry_count);
2624         if (ret)
2625                 goto out;
2626
2627         ret = create_directories(dentry_list, ctx);
2628         if (ret)
2629                 goto out;
2630
2631         ret = create_nondirectories(dentry_list, ctx);
2632         if (ret)
2633                 goto out;
2634
2635         ret = end_file_structure_phase(&ctx->common);
2636         if (ret)
2637                 goto out;
2638
2639         struct read_blob_list_callbacks cbs = {
2640                 .begin_blob        = begin_extract_blob,
2641                 .begin_blob_ctx    = ctx,
2642                 .consume_chunk     = extract_chunk,
2643                 .consume_chunk_ctx = ctx,
2644                 .end_blob          = end_extract_blob,
2645                 .end_blob_ctx      = ctx,
2646         };
2647         ret = extract_blob_list(&ctx->common, &cbs);
2648         if (ret)
2649                 goto out;
2650
2651         ret = start_file_metadata_phase(&ctx->common, dentry_count);
2652         if (ret)
2653                 goto out;
2654
2655         ret = apply_metadata(dentry_list, ctx);
2656         if (ret)
2657                 goto out;
2658
2659         ret = end_file_metadata_phase(&ctx->common);
2660         if (ret)
2661                 goto out;
2662
2663         if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
2664                 ret = end_wimboot_extraction(ctx);
2665                 if (ret)
2666                         goto out;
2667         }
2668
2669         do_warnings(ctx);
2670 out:
2671         close_target_directory(ctx);
2672         if (ctx->target_ntpath.Buffer)
2673                 HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
2674         FREE(ctx->pathbuf.Buffer);
2675         FREE(ctx->print_buffer);
2676         FREE(ctx->wimboot.wims);
2677         if (ctx->wimboot.prepopulate_pats) {
2678                 FREE(ctx->wimboot.prepopulate_pats->strings);
2679                 FREE(ctx->wimboot.prepopulate_pats);
2680         }
2681         FREE(ctx->wimboot.mem_prepopulate_pats);
2682         FREE(ctx->data_buffer);
2683         return ret;
2684 }
2685
2686 const struct apply_operations win32_apply_ops = {
2687         .name                   = "Windows",
2688         .get_supported_features = win32_get_supported_features,
2689         .extract                = win32_extract,
2690         .will_externally_back   = win32_will_externally_back,
2691         .context_size           = sizeof(struct win32_apply_ctx),
2692 };
2693
2694 #endif /* __WIN32__ */