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