]> wimlib.net Git - wimlib/blob - src/win32_apply.c
wim_inode_set_symlink(): Fix typo in comment
[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 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #ifdef __WIN32__
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include <aclapi.h> /* for SetSecurityInfo() */
31
32 #include "wimlib/win32_common.h"
33
34 #include "wimlib/apply.h"
35 #include "wimlib/dentry.h"
36 #include "wimlib/endianness.h"
37 #include "wimlib/error.h"
38 #include "wimlib/lookup_table.h"
39 #include "wimlib/metadata.h"
40 #include "wimlib/reparse.h"
41 #include "wimlib/security.h"
42
43 #define MAX_CREATE_HARD_LINK_WARNINGS 5
44 #define MAX_CREATE_SOFT_LINK_WARNINGS 5
45
46 #define MAX_SET_SD_ACCESS_DENIED_WARNINGS 1
47 #define MAX_SET_SACL_PRIV_NOTHELD_WARNINGS 1
48
49 static const wchar_t *apply_access_denied_msg =
50 L"If you are not running this program as the administrator, you may\n"
51  "          need to do so, so that all data and metadata can be extracted\n"
52  "          exactly as the origignal copy.  However, if you do not care that\n"
53  "          the security descriptors are extracted correctly, you could run\n"
54  "          `wimlib-imagex apply' with the --no-acls flag instead.\n"
55  ;
56
57
58 static int
59 win32_extract_try_rpfix(u8 *rpbuf,
60                         u16 *rpbuflen_p,
61                         const wchar_t *extract_root_realpath,
62                         unsigned extract_root_realpath_nchars)
63 {
64         struct reparse_data rpdata;
65         wchar_t *target;
66         size_t target_nchars;
67         size_t stripped_nchars;
68         wchar_t *stripped_target;
69         wchar_t stripped_target_nchars;
70         int ret;
71
72         utf16lechar *new_target;
73         utf16lechar *new_print_name;
74         size_t new_target_nchars;
75         size_t new_print_name_nchars;
76         utf16lechar *p;
77
78         ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata);
79         if (ret)
80                 return ret;
81
82         if (extract_root_realpath[0] == L'\0' ||
83             extract_root_realpath[1] != L':' ||
84             extract_root_realpath[2] != L'\\')
85         {
86                 ERROR("Can't understand full path format \"%ls\".  "
87                       "Try turning reparse point fixups off...",
88                       extract_root_realpath);
89                 return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
90         }
91
92         ret = parse_substitute_name(rpdata.substitute_name,
93                                     rpdata.substitute_name_nbytes,
94                                     rpdata.rptag);
95         if (ret < 0)
96                 return 0;
97         stripped_nchars = ret;
98         target = rpdata.substitute_name;
99         target_nchars = rpdata.substitute_name_nbytes / sizeof(utf16lechar);
100         stripped_target = target + stripped_nchars;
101         stripped_target_nchars = target_nchars - stripped_nchars;
102
103         new_target = alloca((6 + extract_root_realpath_nchars +
104                              stripped_target_nchars) * sizeof(utf16lechar));
105
106         p = new_target;
107         if (stripped_nchars == 6) {
108                 /* Include \??\ prefix if it was present before */
109                 p = wmempcpy(p, L"\\??\\", 4);
110         }
111
112         /* Print name excludes the \??\ if present. */
113         new_print_name = p;
114         if (stripped_nchars != 0) {
115                 /* Get drive letter from real path to extract root, if a drive
116                  * letter was present before. */
117                 *p++ = extract_root_realpath[0];
118                 *p++ = extract_root_realpath[1];
119         }
120         /* Copy the rest of the extract root */
121         p = wmempcpy(p, extract_root_realpath + 2, extract_root_realpath_nchars - 2);
122
123         /* Append the stripped target */
124         p = wmempcpy(p, stripped_target, stripped_target_nchars);
125         new_target_nchars = p - new_target;
126         new_print_name_nchars = p - new_print_name;
127
128         if (new_target_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE ||
129             new_print_name_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE)
130         {
131                 ERROR("Path names too long to do reparse point fixup!");
132                 return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
133         }
134         rpdata.substitute_name = new_target;
135         rpdata.substitute_name_nbytes = new_target_nchars * sizeof(utf16lechar);
136         rpdata.print_name = new_print_name;
137         rpdata.print_name_nbytes = new_print_name_nchars * sizeof(utf16lechar);
138         return make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
139 }
140
141 /* Wrapper around the FSCTL_SET_REPARSE_POINT ioctl to set the reparse data on
142  * an extracted reparse point. */
143 static int
144 win32_set_reparse_data(HANDLE h,
145                        const struct wim_inode *inode,
146                        const struct wim_lookup_table_entry *lte,
147                        const wchar_t *path,
148                        struct apply_args *args)
149 {
150         int ret;
151         u8 rpbuf[REPARSE_POINT_MAX_SIZE] _aligned_attribute(8);
152         DWORD bytesReturned;
153         u16 rpbuflen;
154
155         DEBUG("Setting reparse data on \"%ls\"", path);
156
157         ret = wim_inode_get_reparse_data(inode, rpbuf, &rpbuflen);
158         if (ret)
159                 return ret;
160
161         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX &&
162             (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
163              inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT) &&
164             !inode->i_not_rpfixed)
165         {
166                 ret = win32_extract_try_rpfix(rpbuf,
167                                               &rpbuflen,
168                                               args->target_realpath,
169                                               args->target_realpath_len);
170                 if (ret)
171                         return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
172         }
173
174         /* Set the reparse data on the open file using the
175          * FSCTL_SET_REPARSE_POINT ioctl.
176          *
177          * There are contradictions in Microsoft's documentation for this:
178          *
179          * "If hDevice was opened without specifying FILE_FLAG_OVERLAPPED,
180          * lpOverlapped is ignored."
181          *
182          * --- So setting lpOverlapped to NULL is okay since it's ignored.
183          *
184          * "If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an
185          * operation returns no output data and lpOutBuffer is NULL,
186          * DeviceIoControl makes use of lpBytesReturned. After such an
187          * operation, the value of lpBytesReturned is meaningless."
188          *
189          * --- So lpOverlapped not really ignored, as it affects another
190          *  parameter.  This is the actual behavior: lpBytesReturned must be
191          *  specified, even though lpBytesReturned is documented as:
192          *
193          *  "Not used with this operation; set to NULL."
194          */
195         if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, rpbuf,
196                              rpbuflen,
197                              NULL, 0,
198                              &bytesReturned /* lpBytesReturned */,
199                              NULL /* lpOverlapped */))
200         {
201                 DWORD err = GetLastError();
202                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
203                 {
204                         args->num_soft_links_failed++;
205                         if (args->num_soft_links_failed <= MAX_CREATE_SOFT_LINK_WARNINGS) {
206                                 WARNING("Can't set reparse data on \"%ls\": Access denied!\n"
207                                         "          You may be trying to extract a symbolic "
208                                         "link without the\n"
209                                         "          SeCreateSymbolicLink privilege, which by "
210                                         "default non-Administrator\n"
211                                         "          accounts do not have.", path);
212                         }
213                         if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS) {
214                                 WARNING("Suppressing further warnings regarding failure to extract\n"
215                                         "          reparse points due to insufficient privileges...");
216                         }
217                 } else {
218                         ERROR("Failed to set reparse data on \"%ls\"", path);
219                         win32_error(err);
220                         if (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
221                             inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)
222                                 return WIMLIB_ERR_LINK;
223                         else
224                                 return WIMLIB_ERR_WRITE;
225                 }
226         }
227         return 0;
228 }
229
230 /* Wrapper around the FSCTL_SET_COMPRESSION ioctl to change the
231  * FILE_ATTRIBUTE_COMPRESSED flag of a file or directory. */
232 static int
233 win32_set_compression_state(HANDLE hFile, USHORT format, const wchar_t *path)
234 {
235         DWORD bytesReturned;
236         if (!DeviceIoControl(hFile, FSCTL_SET_COMPRESSION,
237                              &format, sizeof(USHORT),
238                              NULL, 0,
239                              &bytesReturned, NULL))
240         {
241                 /* Could be a warning only, but we only call this if the volume
242                  * supports compression.  So I'm calling this an error. */
243                 DWORD err = GetLastError();
244                 ERROR("Failed to set compression flag on \"%ls\"", path);
245                 win32_error(err);
246                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
247                         return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES_TO_EXTRACT;
248                 else
249                         return WIMLIB_ERR_WRITE;
250         }
251         return 0;
252 }
253
254 /* Wrapper around FSCTL_SET_SPARSE ioctl to set a file as sparse. */
255 static int
256 win32_set_sparse(HANDLE hFile, const wchar_t *path)
257 {
258         DWORD bytesReturned;
259         if (!DeviceIoControl(hFile, FSCTL_SET_SPARSE,
260                              NULL, 0,
261                              NULL, 0,
262                              &bytesReturned, NULL))
263         {
264                 /* Could be a warning only, but we only call this if the volume
265                  * supports sparse files.  So I'm calling this an error. */
266                 DWORD err = GetLastError();
267                 WARNING("Failed to set sparse flag on \"%ls\"", path);
268                 win32_error(err);
269                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
270                         return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES_TO_EXTRACT;
271                 else
272                         return WIMLIB_ERR_WRITE;
273         }
274         return 0;
275 }
276
277 /*
278  * Sets the security descriptor on an extracted file.
279  */
280 static int
281 win32_set_security_data(const struct wim_inode *inode,
282                         HANDLE hFile,
283                         const wchar_t *path,
284                         struct apply_args *args)
285 {
286         PSECURITY_DESCRIPTOR descriptor;
287         unsigned long n;
288         DWORD err;
289         const struct wim_security_data *sd;
290
291         SECURITY_INFORMATION securityInformation = 0;
292
293         void *owner = NULL;
294         void *group = NULL;
295         ACL *dacl = NULL;
296         ACL *sacl = NULL;
297
298         BOOL owner_defaulted;
299         BOOL group_defaulted;
300         BOOL dacl_present;
301         BOOL dacl_defaulted;
302         BOOL sacl_present;
303         BOOL sacl_defaulted;
304
305         sd = wim_const_security_data(args->wim);
306         descriptor = sd->descriptors[inode->i_security_id];
307
308         GetSecurityDescriptorOwner(descriptor, &owner, &owner_defaulted);
309         if (owner)
310                 securityInformation |= OWNER_SECURITY_INFORMATION;
311
312         GetSecurityDescriptorGroup(descriptor, &group, &group_defaulted);
313         if (group)
314                 securityInformation |= GROUP_SECURITY_INFORMATION;
315
316         GetSecurityDescriptorDacl(descriptor, &dacl_present,
317                                   &dacl, &dacl_defaulted);
318         if (dacl)
319                 securityInformation |= DACL_SECURITY_INFORMATION;
320
321         GetSecurityDescriptorSacl(descriptor, &sacl_present,
322                                   &sacl, &sacl_defaulted);
323         if (sacl)
324                 securityInformation |= SACL_SECURITY_INFORMATION;
325
326 again:
327         if (securityInformation == 0)
328                 return 0;
329         if (SetSecurityInfo(hFile, SE_FILE_OBJECT,
330                             securityInformation, owner, group, dacl, sacl))
331                 return 0;
332         err = GetLastError();
333         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
334                 goto fail;
335         switch (err) {
336         case ERROR_PRIVILEGE_NOT_HELD:
337                 if (securityInformation & SACL_SECURITY_INFORMATION) {
338                         n = args->num_set_sacl_priv_notheld++;
339                         securityInformation &= ~SACL_SECURITY_INFORMATION;
340                         sacl = NULL;
341                         if (n < MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) {
342                                 WARNING(
343 "We don't have enough privileges to set the full security\n"
344 "          descriptor on \"%ls\"!\n", path);
345                                 if (args->num_set_sd_access_denied +
346                                     args->num_set_sacl_priv_notheld == 1)
347                                 {
348                                         WARNING("%ls", apply_access_denied_msg);
349                                 }
350                                 WARNING("Re-trying with SACL omitted.\n", path);
351                         } else if (n == MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) {
352                                 WARNING(
353 "Suppressing further 'privileges not held' error messages when setting\n"
354 "          security descriptors.");
355                         }
356                         goto again;
357                 }
358                 /* Fall through */
359         case ERROR_INVALID_OWNER:
360         case ERROR_ACCESS_DENIED:
361                 n = args->num_set_sd_access_denied++;
362                 if (n < MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
363                         WARNING("Failed to set security descriptor on \"%ls\": "
364                                 "Access denied!\n", path);
365                         if (args->num_set_sd_access_denied +
366                             args->num_set_sacl_priv_notheld == 1)
367                         {
368                                 WARNING("%ls", apply_access_denied_msg);
369                         }
370                 } else if (n == MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
371                         WARNING(
372 "Suppressing further access denied error messages when setting\n"
373 "          security descriptors");
374                 }
375                 return 0;
376         default:
377 fail:
378                 ERROR("Failed to set security descriptor on \"%ls\"", path);
379                 win32_error(err);
380                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
381                         return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES_TO_EXTRACT;
382                 else
383                         return WIMLIB_ERR_WRITE;
384         }
385 }
386
387
388 static int
389 win32_extract_chunk(const void *buf, size_t len, void *arg)
390 {
391         HANDLE hStream = arg;
392
393         DWORD nbytes_written;
394         wimlib_assert(len <= 0xffffffff);
395
396         if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) ||
397             nbytes_written != len)
398         {
399                 DWORD err = GetLastError();
400                 ERROR("WriteFile(): write error");
401                 win32_error(err);
402                 return WIMLIB_ERR_WRITE;
403         }
404         return 0;
405 }
406
407 static int
408 do_win32_extract_stream(HANDLE hStream, const struct wim_lookup_table_entry *lte)
409 {
410         return extract_wim_resource(lte, wim_resource_size(lte),
411                                     win32_extract_chunk, hStream);
412 }
413
414 struct win32_encrypted_extract_ctx {
415         const struct wim_lookup_table_entry *lte;
416         u64 offset;
417 };
418
419 static DWORD WINAPI
420 win32_encrypted_import_cb(unsigned char *data, void *_ctx,
421                           unsigned long *len_p)
422 {
423         struct win32_encrypted_extract_ctx *ctx = _ctx;
424         unsigned long len = *len_p;
425         const struct wim_lookup_table_entry *lte = ctx->lte;
426
427         len = min(len, wim_resource_size(lte) - ctx->offset);
428
429         if (read_partial_wim_resource_into_buf(lte, len, ctx->offset, data))
430                 return ERROR_READ_FAULT;
431
432         ctx->offset += len;
433         *len_p = len;
434         return ERROR_SUCCESS;
435 }
436
437 /* Create an encrypted file and extract the raw encrypted data to it.
438  *
439  * @path:  Path to encrypted file to create.
440  * @lte:   WIM lookup_table entry for the raw encrypted data.
441  *
442  * This is separate from do_win32_extract_stream() because the WIM is supposed
443  * to contain the *raw* encrypted data, which needs to be extracted ("imported")
444  * using the special APIs OpenEncryptedFileRawW(), WriteEncryptedFileRaw(), and
445  * CloseEncryptedFileRaw().
446  *
447  * Returns 0 on success; nonzero on failure.
448  */
449 static int
450 do_win32_extract_encrypted_stream(const wchar_t *path,
451                                   const struct wim_lookup_table_entry *lte)
452 {
453         void *file_ctx;
454         int ret;
455
456         DEBUG("Opening file \"%ls\" to extract raw encrypted data", path);
457
458         ret = OpenEncryptedFileRawW(path, CREATE_FOR_IMPORT, &file_ctx);
459         if (ret) {
460                 ERROR("Failed to open \"%ls\" to write raw encrypted data", path);
461                 win32_error(ret);
462                 return WIMLIB_ERR_OPEN;
463         }
464
465         if (lte) {
466                 struct win32_encrypted_extract_ctx ctx;
467
468                 ctx.lte = lte;
469                 ctx.offset = 0;
470                 ret = WriteEncryptedFileRaw(win32_encrypted_import_cb, &ctx, file_ctx);
471                 if (ret == ERROR_SUCCESS) {
472                         ret = 0;
473                 } else {
474                         ret = WIMLIB_ERR_WRITE;
475                         ERROR("Failed to extract encrypted file \"%ls\"", path);
476                 }
477         }
478         CloseEncryptedFileRaw(file_ctx);
479         return ret;
480 }
481
482 static inline DWORD
483 win32_mask_attributes(DWORD i_attributes)
484 {
485         return i_attributes & ~(FILE_ATTRIBUTE_SPARSE_FILE |
486                                 FILE_ATTRIBUTE_COMPRESSED |
487                                 FILE_ATTRIBUTE_REPARSE_POINT |
488                                 FILE_ATTRIBUTE_DIRECTORY |
489                                 FILE_ATTRIBUTE_ENCRYPTED |
490                                 FILE_FLAG_DELETE_ON_CLOSE |
491                                 FILE_FLAG_NO_BUFFERING |
492                                 FILE_FLAG_OPEN_NO_RECALL |
493                                 FILE_FLAG_OVERLAPPED |
494                                 FILE_FLAG_RANDOM_ACCESS |
495                                 /*FILE_FLAG_SESSION_AWARE |*/
496                                 FILE_FLAG_SEQUENTIAL_SCAN |
497                                 FILE_FLAG_WRITE_THROUGH);
498 }
499
500 static inline DWORD
501 win32_get_create_flags_and_attributes(DWORD i_attributes)
502 {
503         /*
504          * Some attributes cannot be set by passing them to CreateFile().  In
505          * particular:
506          *
507          * FILE_ATTRIBUTE_DIRECTORY:
508          *   CreateDirectory() must be called instead of CreateFile().
509          *
510          * FILE_ATTRIBUTE_SPARSE_FILE:
511          *   Needs an ioctl.
512          *   See: win32_set_sparse().
513          *
514          * FILE_ATTRIBUTE_COMPRESSED:
515          *   Not clear from the documentation, but apparently this needs an
516          *   ioctl as well.
517          *   See: win32_set_compressed().
518          *
519          * FILE_ATTRIBUTE_REPARSE_POINT:
520          *   Needs an ioctl, with the reparse data specified.
521          *   See: win32_set_reparse_data().
522          *
523          * In addition, clear any file flags in the attributes that we don't
524          * want, but also specify FILE_FLAG_OPEN_REPARSE_POINT and
525          * FILE_FLAG_BACKUP_SEMANTICS as we are a backup application.
526          */
527         return win32_mask_attributes(i_attributes) |
528                 FILE_FLAG_OPEN_REPARSE_POINT |
529                 FILE_FLAG_BACKUP_SEMANTICS;
530 }
531
532 /* Set compression and/or sparse attributes on a stream, if supported by the
533  * volume. */
534 static int
535 win32_set_special_stream_attributes(HANDLE hFile, const struct wim_inode *inode,
536                                     struct wim_lookup_table_entry *unnamed_stream_lte,
537                                     const wchar_t *path, unsigned vol_flags)
538 {
539         int ret;
540
541         if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) {
542                 if (vol_flags & FILE_FILE_COMPRESSION) {
543                         ret = win32_set_compression_state(hFile,
544                                                           COMPRESSION_FORMAT_DEFAULT,
545                                                           path);
546                         if (ret)
547                                 return ret;
548                 } else {
549                         DEBUG("Cannot set compression attribute on \"%ls\": "
550                               "volume does not support transparent compression",
551                               path);
552                 }
553         }
554
555         if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) {
556                 if (vol_flags & FILE_SUPPORTS_SPARSE_FILES) {
557                         DEBUG("Setting sparse flag on \"%ls\"", path);
558                         ret = win32_set_sparse(hFile, path);
559                         if (ret)
560                                 return ret;
561                 } else {
562                         DEBUG("Cannot set sparse attribute on \"%ls\": "
563                               "volume does not support sparse files",
564                               path);
565                 }
566         }
567         return 0;
568 }
569
570 /* Pre-create directories; extract encrypted streams */
571 static int
572 win32_begin_extract_unnamed_stream(const struct wim_inode *inode,
573                                    const struct wim_lookup_table_entry *lte,
574                                    const wchar_t *path,
575                                    DWORD *creationDisposition_ret,
576                                    unsigned int vol_flags)
577 {
578         DWORD err;
579         int ret;
580
581         /* Directories must be created with CreateDirectoryW().  Then the call
582          * to CreateFileW() will merely open the directory that was already
583          * created rather than creating a new file. */
584         if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
585                 if (!win32_path_is_root_of_drive(path)) {
586                         if (!CreateDirectoryW(path, NULL)) {
587                                 err = GetLastError();
588                                 if (err != ERROR_ALREADY_EXISTS) {
589                                         ERROR("Failed to create directory \"%ls\"",
590                                               path);
591                                         win32_error(err);
592                                         return WIMLIB_ERR_MKDIR;
593                                 }
594                         }
595                         DEBUG("Created directory \"%ls\"", path);
596                 }
597                 *creationDisposition_ret = OPEN_EXISTING;
598         }
599         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED &&
600             vol_flags & FILE_SUPPORTS_ENCRYPTION)
601         {
602                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
603                         unsigned remaining_sharing_violations = 100;
604                         while (!EncryptFile(path)) {
605                                 if (remaining_sharing_violations &&
606                                     err == ERROR_SHARING_VIOLATION)
607                                 {
608                                         WARNING("Couldn't encrypt directory \"%ls\" "
609                                                 "due to sharing violation; re-trying "
610                                                 "after 100 ms", path);
611                                         Sleep(100);
612                                         remaining_sharing_violations--;
613                                 } else {
614                                         err = GetLastError();
615                                         ERROR("Failed to encrypt directory \"%ls\"",
616                                               path);
617                                         win32_error(err);
618                                         return WIMLIB_ERR_WRITE;
619                                 }
620                         }
621                 } else {
622                         ret = do_win32_extract_encrypted_stream(path, lte);
623                         if (ret)
624                                 return ret;
625                         DEBUG("Extracted encrypted file \"%ls\"", path);
626                 }
627                 *creationDisposition_ret = OPEN_EXISTING;
628         }
629
630         /* Set file attributes if we created the file.  Otherwise, we haven't
631          * created the file set and we will set the attributes in the call to
632          * CreateFileW().
633          *
634          * The FAT filesystem does not let you change the attributes of the root
635          * directory, so treat that as a special case and do not set attributes.
636          * */
637         if (*creationDisposition_ret == OPEN_EXISTING &&
638             !win32_path_is_root_of_drive(path))
639         {
640                 if (!SetFileAttributesW(path,
641                                         win32_mask_attributes(inode->i_attributes)))
642                 {
643                         err = GetLastError();
644                         ERROR("Failed to set attributes on \"%ls\"", path);
645                         win32_error(err);
646                         return WIMLIB_ERR_WRITE;
647                 }
648         }
649         return 0;
650 }
651
652 /* Set security descriptor and extract stream data or reparse data (skip the
653  * unnamed data stream of encrypted files, which was already extracted). */
654 static int
655 win32_finish_extract_stream(HANDLE h, const struct wim_dentry *dentry,
656                             const struct wim_lookup_table_entry *lte,
657                             const wchar_t *stream_path,
658                             const wchar_t *stream_name_utf16,
659                             struct apply_args *args)
660 {
661         int ret = 0;
662         const struct wim_inode *inode = dentry->d_inode;
663         if (stream_name_utf16 == NULL) {
664                 /* Unnamed stream. */
665
666                 /* Set security descriptor, unless the extract_flags indicate
667                  * not to or the volume does not supported it.  Note that this
668                  * is only done when the unnamed stream is being extracted, as
669                  * security descriptors are per-file and not per-stream. */
670                 if (inode->i_security_id >= 0 &&
671                     !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
672                     && (args->vol_flags & FILE_PERSISTENT_ACLS))
673                 {
674                         ret = win32_set_security_data(inode, h, stream_path, args);
675                         if (ret)
676                                 return ret;
677                 }
678
679                 /* Handle reparse points.  The data for them needs to be set
680                  * using a special ioctl.  Note that the reparse point may have
681                  * been created using CreateFileW() in the case of
682                  * non-directories or CreateDirectoryW() in the case of
683                  * directories; but the ioctl works either way.  Also, it is
684                  * only this step that actually sets the
685                  * FILE_ATTRIBUTE_REPARSE_POINT, as it is not valid to set it
686                  * using SetFileAttributesW() or CreateFileW().
687                  *
688                  * If the volume does not support reparse points we simply
689                  * ignore the reparse data.  (N.B. the code currently doesn't
690                  * actually reach this case because reparse points are skipped
691                  * entirely on such volumes.) */
692                 if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
693                         if (args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS) {
694                                 ret = win32_set_reparse_data(h, inode,
695                                                              lte, stream_path,
696                                                              args);
697                                 if (ret)
698                                         return ret;
699                         } else {
700                                 DEBUG("Cannot set reparse data on \"%ls\": volume "
701                                       "does not support reparse points", stream_path);
702                         }
703                 } else if (lte != NULL &&
704                            !(args->vol_flags & FILE_SUPPORTS_ENCRYPTION &&
705                              inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED))
706                 {
707                         /* Extract the data of the unnamed stream, unless the
708                          * lookup table entry is NULL (indicating an empty
709                          * stream for which no data needs to be extracted), or
710                          * the stream is encrypted and therefore was already
711                          * extracted as a special case. */
712                         ret = do_win32_extract_stream(h, lte);
713                         if (ret)
714                                 return ret;
715                 }
716
717                 if (dentry_has_short_name(dentry) && !dentry->dos_name_invalid)
718                         SetFileShortNameW(h, dentry->short_name);
719                 else if (running_on_windows_7_or_later())
720                         SetFileShortNameW(h, L"");
721         } else {
722                 /* Extract the data for a named data stream. */
723                 if (lte != NULL) {
724                         DEBUG("Extracting named data stream \"%ls\" (len = %"PRIu64")",
725                               stream_path, wim_resource_size(lte));
726                         ret = do_win32_extract_stream(h, lte);
727                 }
728         }
729         return ret;
730 }
731
732 static int
733 win32_decrypt_file(HANDLE open_handle, const wchar_t *path)
734 {
735         DWORD err;
736         /* We cannot call DecryptFileW() while there is an open handle to the
737          * file.  So close it first. */
738         if (!CloseHandle(open_handle)) {
739                 err = GetLastError();
740                 ERROR("Failed to close handle for \"%ls\"", path);
741                 win32_error(err);
742                 return WIMLIB_ERR_WRITE;
743         }
744         if (!DecryptFileW(path, 0 /* reserved parameter; set to 0 */)) {
745                 err = GetLastError();
746                 ERROR("Failed to decrypt file \"%ls\"", path);
747                 win32_error(err);
748                 return WIMLIB_ERR_WRITE;
749         }
750         return 0;
751 }
752
753 /*
754  * Create and extract a stream to a file, or create a directory, using the
755  * Windows API.
756  *
757  * This handles reparse points, directories, alternate data streams, encrypted
758  * files, compressed files, etc.
759  *
760  * @dentry: WIM dentry for the file or directory being extracted.
761  *
762  * @path:  Path to extract the file to.
763  *
764  * @stream_name_utf16:
765  *         Name of the stream, or NULL if the stream is unnamed.  This will
766  *         be called with a NULL stream_name_utf16 before any non-NULL
767  *         stream_name_utf16's.
768  *
769  * @lte:   WIM lookup table entry for the stream.  May be NULL to indicate
770  *         a stream of length 0.
771  *
772  * @args:  Additional apply context, including flags indicating supported
773  *         volume features.
774  *
775  * Returns 0 on success; nonzero on failure.
776  */
777 static int
778 win32_extract_stream(const struct wim_dentry *dentry,
779                      const wchar_t *path,
780                      const wchar_t *stream_name_utf16,
781                      struct wim_lookup_table_entry *lte,
782                      struct apply_args *args)
783 {
784         wchar_t *stream_path;
785         HANDLE h;
786         int ret;
787         DWORD err;
788         DWORD creationDisposition = CREATE_ALWAYS;
789         DWORD requestedAccess;
790         BY_HANDLE_FILE_INFORMATION file_info;
791         unsigned remaining_sharing_violations = 1000;
792         const struct wim_inode *inode = dentry->d_inode;
793
794         if (stream_name_utf16) {
795                 /* Named stream.  Create a buffer that contains the UTF-16LE
796                  * string [.\]path:stream_name_utf16.  This is needed to
797                  * create and open the stream using CreateFileW().  I'm not
798                  * aware of any other APIs to do this.  Note: the '$DATA' suffix
799                  * seems to be unneeded.  Additional note: a ".\" prefix needs
800                  * to be added when the path is a 1-character long relative path
801                  * to avoid ambiguity with drive letters. */
802                 size_t stream_path_nchars;
803                 size_t path_nchars;
804                 size_t stream_name_nchars;
805                 const wchar_t *prefix;
806
807                 path_nchars = wcslen(path);
808                 stream_name_nchars = wcslen(stream_name_utf16);
809                 stream_path_nchars = path_nchars + 1 + stream_name_nchars;
810                 if (path_nchars == 1 && !is_any_path_separator(path[0])) {
811                         static const wchar_t _prefix[] =
812                                 {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
813                         prefix = _prefix;
814                         stream_path_nchars += 2;
815                 } else {
816                         prefix = L"";
817                 }
818                 stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
819                 swprintf(stream_path, L"%ls%ls:%ls",
820                          prefix, path, stream_name_utf16);
821         } else {
822                 /* Unnamed stream; its path is just the path to the file itself.
823                  * */
824                 stream_path = (wchar_t*)path;
825
826                 ret = win32_begin_extract_unnamed_stream(inode, lte, path,
827                                                          &creationDisposition,
828                                                          args->vol_flags);
829                 if (ret)
830                         goto fail;
831         }
832
833         DEBUG("Opening \"%ls\"", stream_path);
834         requestedAccess = GENERIC_READ | GENERIC_WRITE |
835                           ACCESS_SYSTEM_SECURITY;
836         /* DELETE access is needed for SetFileShortNameW(), for some reason.
837          * But don't request it for the extraction root, for the following
838          * reasons:
839          *
840          * - Requesting DELETE access on the extraction root will cause a
841          *   sharing violation if the extraction root is the current working
842          *   directory (".").
843          * - The extraction root may be extracted to a different name than given
844          *   in the WIM file, in which case the DOS name, if given, would not be
845          *   meaningful.
846          * - For full-image extractions, the root dentry is supposed to be
847          *   unnamed anyway.
848          * - Microsoft's ImageX does not extract the root directory.
849          */
850         if (dentry != args->extract_root)
851                 requestedAccess |= DELETE;
852 try_open_again:
853         /* Open the stream to be extracted.  Depending on what we have set
854          * creationDisposition to, we may be creating this for the first time,
855          * or we may be opening on existing stream we already created using
856          * CreateDirectoryW() or OpenEncryptedFileRawW(). */
857         h = CreateFileW(stream_path,
858                         requestedAccess,
859                         FILE_SHARE_READ,
860                         NULL,
861                         creationDisposition,
862                         win32_get_create_flags_and_attributes(inode->i_attributes),
863                         NULL);
864         if (h == INVALID_HANDLE_VALUE) {
865                 err = GetLastError();
866                 if (err == ERROR_ACCESS_DENIED &&
867                     win32_path_is_root_of_drive(stream_path))
868                 {
869                         ret = 0;
870                         goto out;
871                 }
872                 if ((err == ERROR_PRIVILEGE_NOT_HELD ||
873                      err == ERROR_ACCESS_DENIED) &&
874                     (requestedAccess & ACCESS_SYSTEM_SECURITY))
875                 {
876                         /* Try opening the file again without privilege to
877                          * modify SACL. */
878                         requestedAccess &= ~ACCESS_SYSTEM_SECURITY;
879                         goto try_open_again;
880                 }
881                 if (err == ERROR_SHARING_VIOLATION &&
882                     (inode->i_attributes & (FILE_ATTRIBUTE_ENCRYPTED |
883                                             FILE_ATTRIBUTE_DIRECTORY)) ==
884                         (FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_DIRECTORY))
885                 {
886                         if (remaining_sharing_violations) {
887                                 --remaining_sharing_violations;
888                                 /* This can happen when restoring encrypted directories
889                                  * for some reason.  Probably a bug in EncryptFile(). */
890                                 WARNING("Couldn't open \"%ls\" due to sharing violation; "
891                                         "re-trying after 100ms", stream_path);
892                                 Sleep(100);
893                                 goto try_open_again;
894                         } else {
895                                 ERROR("Too many sharing violations; giving up...");
896                         }
897                 }
898                 if (creationDisposition == OPEN_EXISTING)
899                         ERROR("Failed to open \"%ls\"", stream_path);
900                 else
901                         ERROR("Failed to create \"%ls\"", stream_path);
902                 win32_error(err);
903                 ret = WIMLIB_ERR_OPEN;
904                 goto fail;
905         }
906
907         /* Check the attributes of the file we just opened, and remove
908          * encryption or compression if either was set by default but is not
909          * supposed to be set based on the WIM inode attributes. */
910         if (!GetFileInformationByHandle(h, &file_info)) {
911                 err = GetLastError();
912                 ERROR("Failed to get attributes of \"%ls\"", stream_path);
913                 win32_error(err);
914                 ret = WIMLIB_ERR_STAT;
915                 goto fail_close_handle;
916         }
917
918         /* Remove encryption? */
919         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED &&
920             !(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED))
921         {
922                 /* File defaulted to encrypted due to being in an encrypted
923                  * directory, but is not actually supposed to be encrypted.
924                  *
925                  * This is a workaround, because I'm not aware of any way to
926                  * directly (e.g. with CreateFileW()) create an unencrypted file
927                  * in a directory with FILE_ATTRIBUTE_ENCRYPTED set. */
928                 ret = win32_decrypt_file(h, stream_path);
929                 if (ret)
930                         goto fail; /* win32_decrypt_file() closed the handle. */
931                 creationDisposition = OPEN_EXISTING;
932                 goto try_open_again;
933         }
934
935         /* Remove compression? */
936         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED &&
937             !(inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED))
938         {
939                 /* Similar to the encrypted case, above, if the file defaulted
940                  * to compressed due to being in an compressed directory, but is
941                  * not actually supposed to be compressed, explicitly set the
942                  * compression format to COMPRESSION_FORMAT_NONE. */
943                 ret = win32_set_compression_state(h, COMPRESSION_FORMAT_NONE,
944                                                   stream_path);
945                 if (ret)
946                         goto fail_close_handle;
947         }
948
949         /* Set compression and/or sparse attributes if needed */
950         ret = win32_set_special_stream_attributes(h, inode, lte, path,
951                                                   args->vol_flags);
952
953         if (ret)
954                 goto fail_close_handle;
955
956         /* At this point we have at least created the needed stream with the
957          * appropriate attributes.  We have yet to set the appropriate security
958          * descriptor and actually extract the stream data (other than for
959          * extracted files, which were already extracted).
960          * win32_finish_extract_stream() handles these additional steps. */
961         ret = win32_finish_extract_stream(h, dentry, lte, stream_path,
962                                           stream_name_utf16, args);
963         if (ret)
964                 goto fail_close_handle;
965
966         /* Done extracting the stream.  Close the handle and return. */
967         DEBUG("Closing \"%ls\"", stream_path);
968         if (!CloseHandle(h)) {
969                 err = GetLastError();
970                 ERROR("Failed to close \"%ls\"", stream_path);
971                 win32_error(err);
972                 ret = WIMLIB_ERR_WRITE;
973                 goto fail;
974         }
975         ret = 0;
976         goto out;
977 fail_close_handle:
978         CloseHandle(h);
979 fail:
980         ERROR("Error extracting \"%ls\"", stream_path);
981 out:
982         return ret;
983 }
984
985 /*
986  * Creates a file, directory, or reparse point and extracts all streams to it
987  * (unnamed data stream and/or reparse point stream, plus any alternate data
988  * streams).  Handles sparse, compressed, and/or encrypted files.
989  *
990  * @dentry:     WIM dentry for this file or directory.
991  * @path:       UTF-16LE external path to extract the inode to.
992  * @args:       Additional extraction context.
993  *
994  * Returns 0 on success; nonzero on failure.
995  */
996 static int
997 win32_extract_streams(const struct wim_dentry *dentry,
998                       const wchar_t *path, struct apply_args *args)
999 {
1000         struct wim_lookup_table_entry *unnamed_lte;
1001         int ret;
1002         const struct wim_inode *inode = dentry->d_inode;
1003
1004         /* First extract the unnamed stream. */
1005
1006         unnamed_lte = inode_unnamed_lte_resolved(inode);
1007         ret = win32_extract_stream(dentry, path, NULL, unnamed_lte, args);
1008         if (ret)
1009                 goto out;
1010
1011         /* Extract any named streams, if supported by the volume. */
1012
1013         if (!(args->vol_flags & FILE_NAMED_STREAMS))
1014                 goto out;
1015         for (u16 i = 0; i < inode->i_num_ads; i++) {
1016                 const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i];
1017
1018                 /* Skip the unnamed stream if it's in the ADS entries (we
1019                  * already extracted it...) */
1020                 if (ads_entry->stream_name_nbytes == 0)
1021                         continue;
1022
1023                 /* Skip special UNIX data entries (see documentation for
1024                  * WIMLIB_ADD_FLAG_UNIX_DATA) */
1025                 if (ads_entry->stream_name_nbytes == WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES
1026                     && !memcmp(ads_entry->stream_name,
1027                                WIMLIB_UNIX_DATA_TAG_UTF16LE,
1028                                WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES))
1029                         continue;
1030
1031                 /* Extract the named stream */
1032                 ret = win32_extract_stream(dentry,
1033                                            path,
1034                                            ads_entry->stream_name,
1035                                            ads_entry->lte,
1036                                            args);
1037                 if (ret)
1038                         break;
1039         }
1040 out:
1041         return ret;
1042 }
1043
1044 static int
1045 dentry_clear_inode_visited(struct wim_dentry *dentry, void *_ignore)
1046 {
1047         dentry->d_inode->i_visited = 0;
1048         return 0;
1049 }
1050
1051 static int
1052 dentry_get_features(struct wim_dentry *dentry, void *_features_p)
1053 {
1054         DWORD features = 0;
1055         DWORD *features_p = _features_p;
1056         struct wim_inode *inode = dentry->d_inode;
1057
1058         if (inode->i_visited) {
1059                 features |= FILE_SUPPORTS_HARD_LINKS;
1060         } else {
1061                 inode->i_visited = 1;
1062                 if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE)
1063                         features |= FILE_SUPPORTS_SPARSE_FILES;
1064                 if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
1065                         features |= FILE_SUPPORTS_REPARSE_POINTS;
1066                 for (unsigned i = 0; i < inode->i_num_ads; i++)
1067                         if (inode->i_ads_entries[i].stream_name_nbytes)
1068                                 features |= FILE_NAMED_STREAMS;
1069                 if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
1070                         features |= FILE_SUPPORTS_ENCRYPTION;
1071                 if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED)
1072                         features |= FILE_FILE_COMPRESSION;
1073                 if (inode->i_security_id != -1)
1074                         features |= FILE_PERSISTENT_ACLS;
1075         }
1076         *features_p |= features;
1077         return 0;
1078 }
1079
1080 /* If not done already, load the supported feature flags for the volume onto
1081  * which the image is being extracted, and warn the user about any missing
1082  * features that could be important. */
1083 static int
1084 win32_check_vol_flags(const wchar_t *output_path,
1085                       struct wim_dentry *root, struct apply_args *args)
1086 {
1087         DWORD dentry_features = 0;
1088         DWORD missing_features;
1089
1090         if (args->have_vol_flags)
1091                 return 0;
1092
1093         for_dentry_in_tree(root, dentry_clear_inode_visited, NULL);
1094         for_dentry_in_tree(root, dentry_get_features, &dentry_features);
1095
1096         win32_get_vol_flags(output_path, &args->vol_flags);
1097         args->have_vol_flags = true;
1098
1099         missing_features = dentry_features & ~args->vol_flags;
1100
1101         /* Warn the user about data that may not be extracted. */
1102         if (missing_features & FILE_SUPPORTS_SPARSE_FILES)
1103                 WARNING("Volume does not support sparse files!\n"
1104                         "          Sparse files will be extracted as non-sparse.");
1105         if (missing_features & FILE_SUPPORTS_REPARSE_POINTS)
1106                 WARNING("Volume does not support reparse points!\n"
1107                         "          Reparse point data will not be extracted.");
1108         if (missing_features & FILE_NAMED_STREAMS) {
1109                 WARNING("Volume does not support named data streams!\n"
1110                         "          Named data streams will not be extracted.");
1111         }
1112         if (missing_features & FILE_SUPPORTS_ENCRYPTION) {
1113                 WARNING("Volume does not support encryption!\n"
1114                         "          Encrypted files will be extracted as raw data.");
1115         }
1116         if (missing_features & FILE_FILE_COMPRESSION) {
1117                 WARNING("Volume does not support transparent compression!\n"
1118                         "          Compressed files will be extracted as non-compressed.");
1119         }
1120         if (missing_features & FILE_PERSISTENT_ACLS) {
1121                 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) {
1122                         ERROR("Strict ACLs requested, but the volume does not "
1123                               "support ACLs!");
1124                         return WIMLIB_ERR_VOLUME_LACKS_FEATURES;
1125                 } else {
1126                         WARNING("Volume does not support persistent ACLS!\n"
1127                                 "          File permissions will not be extracted.");
1128                 }
1129         }
1130         if (running_on_windows_7_or_later() &&
1131             (missing_features & FILE_SUPPORTS_HARD_LINKS))
1132         {
1133                 WARNING("Volume does not support hard links!\n"
1134                         "          Hard links will be extracted as duplicate files.");
1135         }
1136         return 0;
1137 }
1138
1139 /*
1140  * Try extracting a hard link.
1141  *
1142  * @output_path:  Path to link to be extracted.
1143  *
1144  * @inode:        WIM inode that the link is to; inode->i_extracted_file
1145  *                the path to a name of the file that has already been
1146  *                extracted (we use this to create the hard link).
1147  *
1148  * @args:         Additional apply context, used here to keep track of
1149  *                the number of times creating a hard link failed due to
1150  *                ERROR_INVALID_FUNCTION.  This error should indicate that hard
1151  *                links are not supported by the volume, and we would like to
1152  *                warn the user a few times, but not too many times.
1153  *
1154  * Returns 0 if the hard link was successfully extracted.  Returns
1155  * WIMLIB_ERR_LINK (> 0) if an error occurred, other than hard links possibly
1156  * being unsupported by the volume.  Returns a negative value if creating the
1157  * hard link failed due to ERROR_INVALID_FUNCTION.
1158  */
1159 static int
1160 win32_try_hard_link(const wchar_t *output_path, const struct wim_inode *inode,
1161                     struct apply_args *args)
1162 {
1163         DWORD err;
1164
1165         /* There is a volume flag for this (FILE_SUPPORTS_HARD_LINKS),
1166          * but it's only available on Windows 7 and later.
1167          *
1168          * Otherwise, CreateHardLinkW() will apparently return
1169          * ERROR_INVALID_FUNCTION if the volume does not support hard links. */
1170
1171         DEBUG("Creating hard link \"%ls => %ls\"",
1172               output_path, inode->i_extracted_file);
1173
1174         if (running_on_windows_7_or_later() &&
1175             !(args->vol_flags & FILE_SUPPORTS_HARD_LINKS))
1176                 goto hard_links_unsupported;
1177
1178         if (CreateHardLinkW(output_path, inode->i_extracted_file, NULL))
1179                 return 0;
1180
1181         err = GetLastError();
1182         if (err != ERROR_INVALID_FUNCTION) {
1183                 ERROR("Can't create hard link \"%ls => %ls\"",
1184                       output_path, inode->i_extracted_file);
1185                 win32_error(err);
1186                 return WIMLIB_ERR_LINK;
1187         }
1188 hard_links_unsupported:
1189         args->num_hard_links_failed++;
1190         if (args->num_hard_links_failed <= MAX_CREATE_HARD_LINK_WARNINGS) {
1191                 if (running_on_windows_7_or_later())
1192                 {
1193                         WARNING("Extracting duplicate copy of \"%ls\" "
1194                                 "rather than hard link", output_path);
1195                 } else {
1196                         WARNING("Can't create hard link \"%ls\" => \"%ls\":\n"
1197                                 "          Volume does not support hard links!\n"
1198                                 "          Falling back to extracting a copy of the file.",
1199                                 output_path, inode->i_extracted_file);
1200                 }
1201         }
1202         if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS)
1203                 WARNING("Suppressing further hard linking warnings...");
1204         return -1;
1205 }
1206
1207 /* Extract a file, directory, reparse point, or hard link to an
1208  * already-extracted file using the Win32 API */
1209 int
1210 win32_do_apply_dentry(const wchar_t *output_path,
1211                       size_t output_path_num_chars,
1212                       struct wim_dentry *dentry,
1213                       struct apply_args *args)
1214 {
1215         int ret;
1216         struct wim_inode *inode = dentry->d_inode;
1217
1218         ret = win32_check_vol_flags(output_path, dentry, args);
1219         if (ret)
1220                 return ret;
1221         if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
1222                 /* Linked file, with another name already extracted.  Create a
1223                  * hard link. */
1224                 ret = win32_try_hard_link(output_path, inode, args);
1225                 if (ret >= 0)
1226                         return ret;
1227                 /* Negative return value from win32_try_hard_link() indicates
1228                  * that hard links are probably not supported by the volume.
1229                  * Fall back to extracting a copy of the file. */
1230         }
1231
1232         /* If this is a reparse point and the volume does not support reparse
1233          * points, just skip it completely. */
1234         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1235             !(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
1236         {
1237                 WARNING("Not extracting reparse point \"%ls\"", output_path);
1238                 dentry->not_extracted = 1;
1239         } else {
1240                 /* Create the file, directory, or reparse point, and extract the
1241                  * data streams. */
1242                 ret = win32_extract_streams(dentry, output_path, args);
1243                 if (ret)
1244                         return ret;
1245         }
1246         if (inode->i_extracted_file == NULL) {
1247                 const struct wim_lookup_table_entry *lte;
1248
1249                 /* Tally bytes extracted, including all alternate data streams,
1250                  * unless we extracted a hard link (or, at least extracted a
1251                  * name that was supposed to be a hard link) */
1252                 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
1253                         lte = inode_stream_lte_resolved(inode, i);
1254                         if (lte)
1255                                 args->progress.extract.completed_bytes +=
1256                                                         wim_resource_size(lte);
1257                 }
1258                 if (inode->i_nlink > 1) {
1259                         /* Save extracted path for a later call to
1260                          * CreateHardLinkW() if this inode has multiple links.
1261                          * */
1262                         inode->i_extracted_file = WCSDUP(output_path);
1263                         if (!inode->i_extracted_file)
1264                                 return WIMLIB_ERR_NOMEM;
1265                 }
1266         }
1267         return 0;
1268 }
1269
1270 /* Set timestamps on an extracted file using the Win32 API */
1271 int
1272 win32_do_apply_dentry_timestamps(const wchar_t *path,
1273                                  size_t path_num_chars,
1274                                  struct wim_dentry *dentry,
1275                                  struct apply_args *args)
1276 {
1277         DWORD err;
1278         HANDLE h;
1279         const struct wim_inode *inode = dentry->d_inode;
1280
1281         /* Windows doesn't let you change the timestamps of the root directory
1282          * (at least on FAT, which is dumb but expected since FAT doesn't store
1283          * any metadata about the root directory...) */
1284         if (win32_path_is_root_of_drive(path))
1285                 return 0;
1286
1287         DEBUG("Opening \"%ls\" to set timestamps", path);
1288         h = win32_open_existing_file(path, FILE_WRITE_ATTRIBUTES);
1289         if (h == INVALID_HANDLE_VALUE) {
1290                 err = GetLastError();
1291                 goto fail;
1292         }
1293
1294         FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff,
1295                                  .dwHighDateTime = inode->i_creation_time >> 32};
1296         FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff,
1297                                   .dwHighDateTime = inode->i_last_access_time >> 32};
1298         FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff,
1299                                   .dwHighDateTime = inode->i_last_write_time >> 32};
1300
1301         DEBUG("Calling SetFileTime() on \"%ls\"", path);
1302         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) {
1303                 err = GetLastError();
1304                 CloseHandle(h);
1305                 goto fail;
1306         }
1307         DEBUG("Closing \"%ls\"", path);
1308         if (!CloseHandle(h)) {
1309                 err = GetLastError();
1310                 goto fail;
1311         }
1312         goto out;
1313 fail:
1314         /* Only warn if setting timestamps failed; still return 0. */
1315         WARNING("Can't set timestamps on \"%ls\"", path);
1316         win32_error(err);
1317 out:
1318         return 0;
1319 }
1320
1321 #endif /* __WIN32__ */