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