]> wimlib.net Git - wimlib/blob - src/win32_apply.c
87582d60d93b3649e76e7e02e7289f6d3c65dd74
[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 "wimlib/win32_common.h"
31
32 #include "wimlib/apply.h"
33 #include "wimlib/error.h"
34 #include "wimlib/lookup_table.h"
35 #include "wimlib/xml.h"
36 #include "wimlib/wim.h"
37 #include "wimlib/wimboot.h"
38
39 static void
40 ctx_save_data_source_id(struct apply_ctx *ctx, u64 data_source_id)
41 {
42         ctx->private[0] = data_source_id & 0xFFFFFFFF;
43         ctx->private[1] = data_source_id >> 32;
44 }
45
46 static u64
47 ctx_get_data_source_id(const struct apply_ctx *ctx)
48 {
49         return (u32)ctx->private[0] | ((u64)(u32)ctx->private[1] << 32);
50 }
51
52 static int
53 win32_start_extract(const wchar_t *path, struct apply_ctx *ctx)
54 {
55         int ret;
56         unsigned vol_flags;
57         bool supports_SetFileShortName;
58
59         ret = win32_get_vol_flags(path, &vol_flags, &supports_SetFileShortName);
60         if (ret)
61                 return ret;
62
63         ctx->supported_features.archive_files = 1;
64         ctx->supported_features.hidden_files = 1;
65         ctx->supported_features.system_files = 1;
66
67         if (vol_flags & FILE_FILE_COMPRESSION)
68                 ctx->supported_features.compressed_files = 1;
69
70         if (vol_flags & FILE_SUPPORTS_ENCRYPTION) {
71                 ctx->supported_features.encrypted_files = 1;
72                 ctx->supported_features.encrypted_directories = 1;
73         }
74
75         ctx->supported_features.not_context_indexed_files = 1;
76
77         if (vol_flags & FILE_SUPPORTS_SPARSE_FILES)
78                 ctx->supported_features.sparse_files = 1;
79
80         if (vol_flags & FILE_NAMED_STREAMS)
81                 ctx->supported_features.named_data_streams = 1;
82
83         if (vol_flags & FILE_SUPPORTS_HARD_LINKS)
84                 ctx->supported_features.hard_links = 1;
85
86         if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS) {
87                 ctx->supported_features.reparse_points = 1;
88                 if (win32func_CreateSymbolicLinkW)
89                         ctx->supported_features.symlink_reparse_points = 1;
90         }
91
92         if (vol_flags & FILE_PERSISTENT_ACLS)
93                 ctx->supported_features.security_descriptors = 1;
94
95         if (supports_SetFileShortName)
96                 ctx->supported_features.short_names = 1;
97
98         if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
99
100                 u64 data_source_id;
101
102                 if (!wim_info_get_wimboot(ctx->wim->wim_info,
103                                           ctx->wim->current_image))
104                         WARNING("Image is not marked as WIMBoot compatible!");
105
106                 ret = wimboot_alloc_data_source_id(ctx->wim->filename,
107                                                    ctx->wim->current_image,
108                                                    path, &data_source_id);
109                 if (ret)
110                         return ret;
111
112                 ctx_save_data_source_id(ctx, data_source_id);
113         }
114
115         return 0;
116 }
117
118 /* Delete a non-directory file, working around Windows quirks.  */
119 static BOOL
120 win32_delete_file_wrapper(const wchar_t *path)
121 {
122         DWORD err;
123         DWORD attrib;
124
125         if (DeleteFile(path))
126                 return TRUE;
127
128         err = GetLastError();
129         attrib = GetFileAttributes(path);
130         if ((attrib != INVALID_FILE_ATTRIBUTES) &&
131             (attrib & FILE_ATTRIBUTE_READONLY))
132         {
133                 /* Try again with FILE_ATTRIBUTE_READONLY cleared.  */
134                 attrib &= ~FILE_ATTRIBUTE_READONLY;
135                 if (SetFileAttributes(path, attrib)) {
136                         if (DeleteFile(path))
137                                 return TRUE;
138                         else
139                                 err = GetLastError();
140                 }
141         }
142
143         SetLastError(err);
144         return FALSE;
145 }
146
147
148 /* Create a normal file, overwriting one already present.  */
149 static int
150 win32_create_file(const wchar_t *path, struct apply_ctx *ctx, u64 *cookie_ret)
151 {
152         HANDLE h;
153
154         /* Notes:
155          *
156          * WRITE_OWNER and WRITE_DAC privileges are required for some reason,
157          * even through we're creating a new file.
158          *
159          * FILE_FLAG_OPEN_REPARSE_POINT is required to prevent an existing
160          * reparse point from redirecting the creation of the new file
161          * (potentially to an arbitrary location).
162          *
163          * CREATE_ALWAYS could be used instead of CREATE_NEW.  However, there
164          * are quirks that would need to be handled (e.g. having to set
165          * FILE_ATTRIBUTE_HIDDEN and/or FILE_ATTRIBUTE_SYSTEM if the existing
166          * file had them specified, and/or having to clear
167          * FILE_ATTRIBUTE_READONLY on the existing file).  It's simpler to just
168          * call win32_delete_file_wrapper() to delete the existing file in such
169          * a way that already handles the FILE_ATTRIBUTE_READONLY quirk.
170          */
171 retry:
172         h = CreateFile(path, WRITE_OWNER | WRITE_DAC, 0, NULL, CREATE_NEW,
173                        FILE_FLAG_BACKUP_SEMANTICS |
174                                 FILE_FLAG_OPEN_REPARSE_POINT, NULL);
175         if (h == INVALID_HANDLE_VALUE) {
176                 DWORD err = GetLastError();
177
178                 if (err == ERROR_FILE_EXISTS && win32_delete_file_wrapper(path))
179                         goto retry;
180                 set_errno_from_win32_error(err);
181                 return WIMLIB_ERR_OPEN;
182         }
183         CloseHandle(h);
184         return 0;
185 }
186
187 static int
188 win32_create_directory(const wchar_t *path, struct apply_ctx *ctx,
189                        u64 *cookie_ret)
190 {
191         if (!CreateDirectory(path, NULL))
192                 if (GetLastError() != ERROR_ALREADY_EXISTS)
193                         goto error;
194         return 0;
195
196 error:
197         set_errno_from_GetLastError();
198         return WIMLIB_ERR_MKDIR;
199 }
200
201 static int
202 win32_create_hardlink(const wchar_t *oldpath, const wchar_t *newpath,
203                       struct apply_ctx *ctx)
204 {
205         if (!CreateHardLink(newpath, oldpath, NULL)) {
206                 if (GetLastError() != ERROR_ALREADY_EXISTS)
207                         goto error;
208                 if (!win32_delete_file_wrapper(newpath))
209                         goto error;
210                 if (!CreateHardLink(newpath, oldpath, NULL))
211                         goto error;
212         }
213         return 0;
214
215 error:
216         set_errno_from_GetLastError();
217         return WIMLIB_ERR_LINK;
218 }
219
220 static int
221 win32_create_symlink(const wchar_t *oldpath, const wchar_t *newpath,
222                      struct apply_ctx *ctx)
223 {
224         if (!(*win32func_CreateSymbolicLinkW)(newpath, oldpath, 0)) {
225                 if (GetLastError() != ERROR_ALREADY_EXISTS)
226                         goto error;
227                 if (!win32_delete_file_wrapper(newpath))
228                         goto error;
229                 if (!(*win32func_CreateSymbolicLinkW)(newpath, oldpath, 0))
230                         goto error;
231         }
232         return 0;
233
234 error:
235         set_errno_from_GetLastError();
236         return WIMLIB_ERR_LINK;
237 }
238
239 static int
240 win32_extract_wim_chunk(const void *buf, size_t len, void *arg)
241 {
242         HANDLE h = (HANDLE)arg;
243         DWORD nbytes_written;
244
245         if (unlikely(!WriteFile(h, buf, len, &nbytes_written, NULL)))
246                 goto error;
247         if (unlikely(nbytes_written != len))
248                 goto error;
249         return 0;
250
251 error:
252         set_errno_from_GetLastError();
253         return WIMLIB_ERR_WRITE;
254 }
255
256 static int
257 win32_extract_stream(const wchar_t *path, const wchar_t *stream_name,
258                      size_t stream_name_nchars,
259                      struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
260 {
261         DWORD creationDisposition = OPEN_EXISTING;
262         wchar_t *stream_path = (wchar_t*)path;
263         HANDLE h;
264         int ret;
265
266         if (stream_name_nchars) {
267                 creationDisposition = CREATE_ALWAYS;
268                 stream_path = alloca(sizeof(wchar_t) *
269                                      (wcslen(path) + 1 +
270                                       wcslen(stream_name) + 1));
271                 tsprintf(stream_path, L"%ls:%ls", path, stream_name);
272         }
273
274         h = CreateFile(stream_path, FILE_WRITE_DATA, 0, NULL,
275                        creationDisposition, FILE_FLAG_BACKUP_SEMANTICS |
276                                             FILE_FLAG_OPEN_REPARSE_POINT,
277                        NULL);
278         if (h == INVALID_HANDLE_VALUE)
279                 goto error;
280
281         ret = 0;
282         if (!lte)
283                 goto out_close_handle;
284         ret = extract_stream(lte, lte->size, win32_extract_wim_chunk, h);
285 out_close_handle:
286         if (!CloseHandle(h))
287                 goto error;
288         if (ret && !errno)
289                 errno = -1;
290         return ret;
291
292 error:
293         set_errno_from_GetLastError();
294         return WIMLIB_ERR_WRITE;
295 }
296
297 static int
298 win32_extract_unnamed_stream(file_spec_t file,
299                              struct wim_lookup_table_entry *lte,
300                              struct apply_ctx *ctx)
301 {
302         if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT
303             && lte
304             && lte->resource_location == RESOURCE_IN_WIM
305             && lte->rspec->wim == ctx->wim)
306         {
307                 return wimboot_set_pointer(file.path,
308                                            ctx_get_data_source_id(ctx),
309                                            lte->hash);
310         }
311
312         return win32_extract_stream(file.path, NULL, 0, lte, ctx);
313 }
314
315 static int
316 win32_extract_named_stream(file_spec_t file, const wchar_t *stream_name,
317                            size_t stream_name_nchars,
318                            struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
319 {
320         return win32_extract_stream(file.path, stream_name,
321                                     stream_name_nchars, lte, ctx);
322 }
323
324 struct win32_encrypted_extract_ctx {
325         const struct wim_lookup_table_entry *lte;
326         u64 offset;
327 };
328
329 static DWORD WINAPI
330 win32_encrypted_import_cb(unsigned char *data, void *_import_ctx,
331                           unsigned long *len_p)
332 {
333         struct win32_encrypted_extract_ctx *import_ctx = _import_ctx;
334         unsigned long len = *len_p;
335         const struct wim_lookup_table_entry *lte = import_ctx->lte;
336
337         len = min(len, lte->size - import_ctx->offset);
338
339         if (read_partial_wim_stream_into_buf(lte, len, import_ctx->offset, data))
340                 return ERROR_READ_FAULT;
341
342         import_ctx->offset += len;
343         *len_p = len;
344         return ERROR_SUCCESS;
345 }
346
347 static int
348 win32_extract_encrypted_stream(const wchar_t *path,
349                                struct wim_lookup_table_entry *lte,
350                                struct apply_ctx *ctx)
351 {
352         void *file_ctx;
353         DWORD err;
354         int ret;
355         struct win32_encrypted_extract_ctx extract_ctx;
356
357         err = OpenEncryptedFileRaw(path, CREATE_FOR_IMPORT, &file_ctx);
358         if (err != ERROR_SUCCESS) {
359                 set_errno_from_win32_error(err);
360                 ret = WIMLIB_ERR_OPEN;
361                 goto out;
362         }
363
364         extract_ctx.lte = lte;
365         extract_ctx.offset = 0;
366         err = WriteEncryptedFileRaw(win32_encrypted_import_cb, &extract_ctx,
367                                     file_ctx);
368         if (err != ERROR_SUCCESS) {
369                 set_errno_from_win32_error(err);
370                 ret = WIMLIB_ERR_WRITE;
371                 goto out_close;
372         }
373
374         ret = 0;
375 out_close:
376         CloseEncryptedFileRaw(file_ctx);
377 out:
378         return ret;
379 }
380
381 static BOOL
382 win32_set_special_file_attributes(const wchar_t *path, u32 attributes)
383 {
384         HANDLE h;
385         DWORD err;
386         USHORT compression_format = COMPRESSION_FORMAT_DEFAULT;
387         DWORD bytes_returned;
388
389         h = win32_open_existing_file(path, GENERIC_READ | GENERIC_WRITE);
390         if (h == INVALID_HANDLE_VALUE)
391                 goto error;
392
393         if (attributes & FILE_ATTRIBUTE_SPARSE_FILE)
394                 if (!DeviceIoControl(h, FSCTL_SET_SPARSE,
395                                      NULL, 0,
396                                      NULL, 0,
397                                      &bytes_returned, NULL))
398                         goto error_close_handle;
399
400         if (attributes & FILE_ATTRIBUTE_COMPRESSED)
401                 if (!DeviceIoControl(h, FSCTL_SET_COMPRESSION,
402                                      &compression_format, sizeof(USHORT),
403                                      NULL, 0,
404                                      &bytes_returned, NULL))
405                         goto error_close_handle;
406
407         if (!CloseHandle(h))
408                 goto error;
409
410         if (attributes & FILE_ATTRIBUTE_ENCRYPTED)
411                 if (!EncryptFile(path))
412                         goto error;
413
414         return TRUE;
415
416 error_close_handle:
417         err = GetLastError();
418         CloseHandle(h);
419         SetLastError(err);
420 error:
421         return FALSE;
422 }
423
424 static int
425 win32_set_file_attributes(const wchar_t *path, u32 attributes,
426                           struct apply_ctx *ctx, unsigned pass)
427 {
428         u32 special_attributes =
429                 FILE_ATTRIBUTE_REPARSE_POINT |
430                 FILE_ATTRIBUTE_DIRECTORY |
431                 FILE_ATTRIBUTE_SPARSE_FILE |
432                 FILE_ATTRIBUTE_COMPRESSED |
433                 FILE_ATTRIBUTE_ENCRYPTED;
434         u32 actual_attributes;
435
436         /* Delay setting FILE_ATTRIBUTE_READONLY on the initial pass (when files
437          * are created, but data not extracted); otherwise the system will
438          * refuse access to the file even if the process has SeRestorePrivilege.
439          */
440         if (pass == 0)
441                 attributes &= ~FILE_ATTRIBUTE_READONLY;
442
443         if (!SetFileAttributes(path, attributes & ~special_attributes))
444                 goto error;
445
446         if (pass != 0)
447                 return 0;
448
449         if (attributes & (FILE_ATTRIBUTE_SPARSE_FILE |
450                           FILE_ATTRIBUTE_ENCRYPTED |
451                           FILE_ATTRIBUTE_COMPRESSED))
452                 if (!win32_set_special_file_attributes(path, attributes))
453                         goto error;
454
455         /* If file is not supposed to be encrypted or compressed, remove
456          * defaulted encrypted or compressed attributes (from creating file in
457          * encrypted or compressed directory).  */
458         actual_attributes = GetFileAttributes(path);
459         if (actual_attributes == INVALID_FILE_ATTRIBUTES)
460                 goto error;
461
462         if ((actual_attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
463             !(attributes & FILE_ATTRIBUTE_ENCRYPTED))
464                 if (!DecryptFile(path, 0))
465                         goto error;
466         if ((actual_attributes & FILE_ATTRIBUTE_COMPRESSED) &&
467             !(attributes & FILE_ATTRIBUTE_COMPRESSED))
468         {
469                 HANDLE h;
470                 DWORD bytes_returned;
471                 USHORT compression_format = COMPRESSION_FORMAT_NONE;
472
473                 h = win32_open_existing_file(path, GENERIC_READ | GENERIC_WRITE);
474                 if (h == INVALID_HANDLE_VALUE)
475                         goto error;
476
477                 if (!DeviceIoControl(h, FSCTL_SET_COMPRESSION,
478                                      &compression_format, sizeof(USHORT),
479                                      NULL, 0,
480                                      &bytes_returned, NULL))
481                 {
482                         DWORD err = GetLastError();
483                         CloseHandle(h);
484                         SetLastError(err);
485                         goto error;
486                 }
487
488                 if (!CloseHandle(h))
489                         goto error;
490         }
491
492         return 0;
493
494 error:
495         set_errno_from_GetLastError();
496         return WIMLIB_ERR_SET_ATTRIBUTES;
497 }
498
499 static int
500 win32_set_reparse_data(const wchar_t *path, const u8 *rpbuf, u16 rpbuflen,
501                        struct apply_ctx *ctx)
502 {
503         HANDLE h;
504         DWORD err;
505         DWORD bytes_returned;
506
507         h = win32_open_existing_file(path, GENERIC_WRITE);
508         if (h == INVALID_HANDLE_VALUE)
509                 goto error;
510
511         if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
512                              (void*)rpbuf, rpbuflen,
513                              NULL, 0, &bytes_returned, NULL))
514                 goto error_close_handle;
515
516         if (!CloseHandle(h))
517                 goto error;
518
519         return 0;
520
521 error_close_handle:
522         err = GetLastError();
523         CloseHandle(h);
524         SetLastError(err);
525 error:
526         set_errno_from_GetLastError();
527         return WIMLIB_ERR_WRITE; /* XXX: need better error code */
528 }
529
530 static int
531 win32_set_short_name(const wchar_t *path, const wchar_t *short_name,
532                      size_t short_name_nchars, struct apply_ctx *ctx)
533 {
534         HANDLE h;
535         DWORD err;
536
537         h = win32_open_existing_file(path, GENERIC_WRITE | DELETE);
538         if (h == INVALID_HANDLE_VALUE)
539                 goto error;
540
541         if (short_name_nchars) {
542                 if (!SetFileShortName(h, short_name))
543                         goto error_close_handle;
544         } else if (running_on_windows_7_or_later()) {
545                 if (!SetFileShortName(h, L""))
546                         goto error_close_handle;
547         }
548
549         if (!CloseHandle(h))
550                 goto error;
551
552         return 0;
553
554 error_close_handle:
555         err = GetLastError();
556         CloseHandle(h);
557         SetLastError(err);
558 error:
559         set_errno_from_GetLastError();
560         return WIMLIB_ERR_WRITE; /* XXX: need better error code */
561 }
562
563 static DWORD
564 do_win32_set_security_descriptor(HANDLE h, const wchar_t *path,
565                                  SECURITY_INFORMATION info,
566                                  PSECURITY_DESCRIPTOR desc)
567 {
568 #ifdef WITH_NTDLL
569         if (func_NtSetSecurityObject) {
570                 return (*func_RtlNtStatusToDosError)(
571                                 (*func_NtSetSecurityObject)(h, info, desc));
572         }
573 #endif
574         if (SetFileSecurity(path, info, desc))
575                 return ERROR_SUCCESS;
576         else
577                 return GetLastError();
578 }
579
580 /*
581  * Set an arbitrary security descriptor on an arbitrary file (or directory),
582  * working around bugs and design flaws in the Windows operating system.
583  *
584  * On success, return 0.  On failure, return WIMLIB_ERR_SET_SECURITY and set
585  * errno.  Note: if WIMLIB_EXTRACT_FLAG_STRICT_ACLS is not set in
586  * ctx->extract_flags, this function succeeds iff any part of the security
587  * descriptor was successfully set.
588  */
589 static int
590 win32_set_security_descriptor(const wchar_t *path, const u8 *desc,
591                               size_t desc_size, struct apply_ctx *ctx)
592 {
593         SECURITY_INFORMATION info;
594         HANDLE h;
595         int ret;
596
597         /* We really just want to set entire the security descriptor as-is, but
598          * all available APIs require specifying the specific parts of the
599          * descriptor being set.  Start out by requesting all parts be set.  If
600          * permissions problems are encountered, fall back to omitting some
601          * parts (first the SACL, then the DACL, then the owner), unless the
602          * WIMLIB_EXTRACT_FLAG_STRICT_ACLS flag has been enabled.  */
603         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
604                DACL_SECURITY_INFORMATION  | SACL_SECURITY_INFORMATION;
605
606         h = INVALID_HANDLE_VALUE;
607
608         /* Prefer NtSetSecurityObject() to SetFileSecurity().  SetFileSecurity()
609          * itself necessarily uses NtSetSecurityObject() as the latter is the
610          * underlying system call for setting security information, but
611          * SetFileSecurity() opens the handle with NtCreateFile() without
612          * FILE_OPEN_FILE_BACKUP_INTENT.  Hence, access checks are done and due
613          * to the Windows security model, even a process running as the
614          * Administrator can have access denied.  (Of course, this not mentioned
615          * in the MS "documentation".)  */
616
617 #ifdef WITH_NTDLL
618         if (func_NtSetSecurityObject) {
619                 DWORD dwDesiredAccess;
620
621                 /* Open a handle for NtSetSecurityObject() with as many relevant
622                  * access rights as possible.
623                  *
624                  * We don't know which rights will be actually granted.  It
625                  * could be less than what is needed to actually assign the full
626                  * security descriptor, especially if the process is running as
627                  * a non-Administrator.  However, by default we just do the best
628                  * we can, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS has been
629                  * enabled.  The MAXIMUM_ALLOWED access right is seemingly
630                  * designed for this use case; however, it does not work
631                  * properly in all cases: it can cause CreateFile() to fail with
632                  * ERROR_ACCESS_DENIED, even though by definition
633                  * MAXIMUM_ALLOWED access only requests access rights that are
634                  * *not* denied.  (Needless to say, MS does not document this
635                  * bug.)  */
636
637                 dwDesiredAccess = WRITE_DAC |
638                                   WRITE_OWNER |
639                                   ACCESS_SYSTEM_SECURITY;
640                 for (;;) {
641                         DWORD err;
642
643                         h = win32_open_existing_file(path, dwDesiredAccess);
644                         if (h != INVALID_HANDLE_VALUE)
645                                 break;
646                         err = GetLastError();
647                         if (err == ERROR_ACCESS_DENIED ||
648                             err == ERROR_PRIVILEGE_NOT_HELD)
649                         {
650                                 /* Don't increment partial_security_descriptors
651                                  * here or check WIMLIB_EXTRACT_FLAG_STRICT_ACLS
652                                  * here.  It will be done later if needed; here
653                                  * we are just trying to get as many relevant
654                                  * access rights as possible.  */
655                                 if (dwDesiredAccess & ACCESS_SYSTEM_SECURITY) {
656                                         dwDesiredAccess &= ~ACCESS_SYSTEM_SECURITY;
657                                         continue;
658                                 }
659                                 if (dwDesiredAccess & WRITE_DAC) {
660                                         dwDesiredAccess &= ~WRITE_DAC;
661                                         continue;
662                                 }
663                                 if (dwDesiredAccess & WRITE_OWNER) {
664                                         dwDesiredAccess &= ~WRITE_OWNER;
665                                         continue;
666                                 }
667                         }
668                         /* Other error, or couldn't open the file even with no
669                          * access rights specified.  Something else must be
670                          * wrong.  */
671                         set_errno_from_win32_error(err);
672                         return WIMLIB_ERR_SET_SECURITY;
673                 }
674         }
675 #endif
676
677         /* Try setting the security descriptor.  */
678         for (;;) {
679                 DWORD err;
680
681                 err = do_win32_set_security_descriptor(h, path, info,
682                                                        (PSECURITY_DESCRIPTOR)desc);
683                 if (err == ERROR_SUCCESS) {
684                         ret = 0;
685                         break;
686                 }
687
688                 /* Failed to set the requested parts of the security descriptor.
689                  * If the error was permissions-related, try to set fewer parts
690                  * of the security descriptor, unless
691                  * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled.  */
692                 if ((err == ERROR_PRIVILEGE_NOT_HELD ||
693                      err == ERROR_ACCESS_DENIED) &&
694                     !(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
695                 {
696                         if (info & SACL_SECURITY_INFORMATION) {
697                                 info &= ~SACL_SECURITY_INFORMATION;
698                                 ctx->partial_security_descriptors++;
699                                 continue;
700                         }
701                         if (info & DACL_SECURITY_INFORMATION) {
702                                 info &= ~DACL_SECURITY_INFORMATION;
703                                 continue;
704                         }
705                         if (info & OWNER_SECURITY_INFORMATION) {
706                                 info &= ~OWNER_SECURITY_INFORMATION;
707                                 continue;
708                         }
709                         /* Nothing left except GROUP, and if we removed it we
710                          * wouldn't have anything at all.  */
711                 }
712                 /* No part of the security descriptor could be set, or
713                  * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full
714                  * security descriptor could not be set.  */
715                 if (!(info & SACL_SECURITY_INFORMATION))
716                         ctx->partial_security_descriptors--;
717                 set_errno_from_win32_error(err);
718                 ret = WIMLIB_ERR_SET_SECURITY;
719                 break;
720         }
721
722         /* Close handle opened for NtSetSecurityObject().  */
723 #ifdef WITH_NTDLL
724         if (func_NtSetSecurityObject)
725                 CloseHandle(h);
726 #endif
727         return ret;
728 }
729
730 static int
731 win32_set_timestamps(const wchar_t *path, u64 creation_time,
732                      u64 last_write_time, u64 last_access_time,
733                      struct apply_ctx *ctx)
734 {
735         HANDLE h;
736         DWORD err;
737         FILETIME creationTime = {.dwLowDateTime = creation_time & 0xffffffff,
738                                  .dwHighDateTime = creation_time >> 32};
739         FILETIME lastAccessTime = {.dwLowDateTime = last_access_time & 0xffffffff,
740                                   .dwHighDateTime = last_access_time >> 32};
741         FILETIME lastWriteTime = {.dwLowDateTime = last_write_time & 0xffffffff,
742                                   .dwHighDateTime = last_write_time >> 32};
743
744         h = win32_open_existing_file(path, FILE_WRITE_ATTRIBUTES);
745         if (h == INVALID_HANDLE_VALUE)
746                 goto error;
747
748         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime))
749                 goto error_close_handle;
750
751         if (!CloseHandle(h))
752                 goto error;
753
754         return 0;
755
756 error_close_handle:
757         err = GetLastError();
758         CloseHandle(h);
759         SetLastError(err);
760 error:
761         set_errno_from_GetLastError();
762         return WIMLIB_ERR_SET_TIMESTAMPS;
763 }
764
765 const struct apply_operations win32_apply_ops = {
766         .name = L"Win32",
767
768         .target_is_root           = win32_path_is_root_of_drive,
769         .start_extract            = win32_start_extract,
770         .create_file              = win32_create_file,
771         .create_directory         = win32_create_directory,
772         .create_hardlink          = win32_create_hardlink,
773         .create_symlink           = win32_create_symlink,
774         .extract_unnamed_stream   = win32_extract_unnamed_stream,
775         .extract_named_stream     = win32_extract_named_stream,
776         .extract_encrypted_stream = win32_extract_encrypted_stream,
777         .set_file_attributes      = win32_set_file_attributes,
778         .set_reparse_data         = win32_set_reparse_data,
779         .set_short_name           = win32_set_short_name,
780         .set_security_descriptor  = win32_set_security_descriptor,
781         .set_timestamps           = win32_set_timestamps,
782
783         .path_prefix = L"\\\\?\\",
784         .path_prefix_nchars = 4,
785         .path_separator = L'\\',
786         .path_max = 32768,
787
788         .requires_realtarget_in_paths = 1,
789         .realpath_works_on_nonexisting_files = 1,
790         .root_directory_is_special = 1,
791         .requires_final_set_attributes_pass = 1,
792         .extract_encrypted_stream_creates_file = 1,
793         .requires_short_name_reordering = 1, /* TODO: check if this is really needed  */
794 };
795
796 #endif /* __WIN32__ */