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