]> wimlib.net Git - wimlib/blob - src/win32_apply.c
1a4adf5c3736622ae8bc623e16f4203f84afc80d
[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
36 static int
37 win32_start_extract(const wchar_t *path, struct apply_ctx *ctx)
38 {
39         int ret;
40         unsigned vol_flags;
41         bool supports_SetFileShortName;
42
43         ret = win32_get_vol_flags(path, &vol_flags, &supports_SetFileShortName);
44         if (ret)
45                 return ret;
46
47         ctx->supported_features.archive_files = 1;
48         ctx->supported_features.hidden_files = 1;
49         ctx->supported_features.system_files = 1;
50
51         if (vol_flags & FILE_FILE_COMPRESSION)
52                 ctx->supported_features.compressed_files = 1;
53
54         if (vol_flags & FILE_SUPPORTS_ENCRYPTION) {
55                 ctx->supported_features.encrypted_files = 1;
56                 ctx->supported_features.encrypted_directories = 1;
57         }
58
59         ctx->supported_features.not_context_indexed_files = 1;
60
61         if (vol_flags & FILE_SUPPORTS_SPARSE_FILES)
62                 ctx->supported_features.sparse_files = 1;
63
64         if (vol_flags & FILE_NAMED_STREAMS)
65                 ctx->supported_features.named_data_streams = 1;
66
67         if (vol_flags & FILE_SUPPORTS_HARD_LINKS)
68                 ctx->supported_features.hard_links = 1;
69
70         if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS) {
71                 ctx->supported_features.reparse_points = 1;
72                 if (win32func_CreateSymbolicLinkW)
73                         ctx->supported_features.symlink_reparse_points = 1;
74         }
75
76         if (vol_flags & FILE_PERSISTENT_ACLS)
77                 ctx->supported_features.security_descriptors = 1;
78
79         if (supports_SetFileShortName)
80                 ctx->supported_features.short_names = 1;
81         return 0;
82 }
83
84 static int
85 win32_create_file(const wchar_t *path, struct apply_ctx *ctx, u64 *cookie_ret)
86 {
87         HANDLE h;
88
89         h = CreateFile(path, 0, 0, NULL, CREATE_ALWAYS,
90                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
91         if (h == INVALID_HANDLE_VALUE)
92                 goto error;
93         CloseHandle(h);
94         return 0;
95
96 error:
97         set_errno_from_GetLastError();
98         return WIMLIB_ERR_OPEN;
99 }
100
101 static int
102 win32_create_directory(const wchar_t *path, struct apply_ctx *ctx,
103                        u64 *cookie_ret)
104 {
105         if (!CreateDirectory(path, NULL))
106                 if (GetLastError() != ERROR_ALREADY_EXISTS)
107                         goto error;
108         return 0;
109
110 error:
111         set_errno_from_GetLastError();
112         return WIMLIB_ERR_MKDIR;
113 }
114
115 static int
116 win32_create_hardlink(const wchar_t *oldpath, const wchar_t *newpath,
117                       struct apply_ctx *ctx)
118 {
119         if (!CreateHardLink(newpath, oldpath, NULL)) {
120                 if (GetLastError() != ERROR_ALREADY_EXISTS)
121                         goto error;
122                 if (!DeleteFile(newpath))
123                         goto error;
124                 if (!CreateHardLink(newpath, oldpath, NULL))
125                         goto error;
126         }
127         return 0;
128
129 error:
130         set_errno_from_GetLastError();
131         return WIMLIB_ERR_LINK;
132 }
133
134 static int
135 win32_create_symlink(const wchar_t *oldpath, const wchar_t *newpath,
136                      struct apply_ctx *ctx)
137 {
138         if (!(*win32func_CreateSymbolicLinkW)(newpath, oldpath, 0)) {
139                 if (GetLastError() != ERROR_ALREADY_EXISTS)
140                         goto error;
141                 if (!DeleteFile(newpath))
142                         goto error;
143                 if (!(*win32func_CreateSymbolicLinkW)(newpath, oldpath, 0))
144                         goto error;
145         }
146         return 0;
147
148 error:
149         set_errno_from_GetLastError();
150         return WIMLIB_ERR_LINK;
151 }
152
153 static int
154 win32_extract_wim_chunk(const void *buf, size_t len, void *arg)
155 {
156         HANDLE h = (HANDLE)arg;
157         DWORD nbytes_written;
158
159         if (unlikely(!WriteFile(h, buf, len, &nbytes_written, NULL)))
160                 goto error;
161         if (unlikely(nbytes_written != len))
162                 goto error;
163         return 0;
164
165 error:
166         set_errno_from_GetLastError();
167         return WIMLIB_ERR_WRITE;
168 }
169
170 static int
171 win32_extract_stream(const wchar_t *path, const wchar_t *stream_name,
172                      size_t stream_name_nchars,
173                      struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
174 {
175         DWORD creationDisposition = OPEN_EXISTING;
176         wchar_t *stream_path = (wchar_t*)path;
177         HANDLE h;
178         int ret;
179
180         if (stream_name_nchars) {
181                 creationDisposition = CREATE_ALWAYS;
182                 stream_path = alloca(sizeof(wchar_t) *
183                                      (wcslen(path) + 1 +
184                                       wcslen(stream_name) + 1));
185                 tsprintf(stream_path, L"%ls:%ls", path, stream_name);
186         }
187
188         h = CreateFile(stream_path, FILE_WRITE_DATA, 0, NULL,
189                        creationDisposition, FILE_FLAG_BACKUP_SEMANTICS |
190                                             FILE_FLAG_OPEN_REPARSE_POINT,
191                        NULL);
192         if (h == INVALID_HANDLE_VALUE)
193                 goto error;
194
195         ret = 0;
196         if (!lte)
197                 goto out_close_handle;
198         ret = extract_wim_resource(lte, lte->size, win32_extract_wim_chunk, h);
199 out_close_handle:
200         if (!CloseHandle(h))
201                 goto error;
202         if (ret && !errno)
203                 errno = -1;
204         return ret;
205
206 error:
207         set_errno_from_GetLastError();
208         return WIMLIB_ERR_WRITE;
209 }
210
211 static int
212 win32_extract_unnamed_stream(file_spec_t file,
213                              struct wim_lookup_table_entry *lte,
214                              struct apply_ctx *ctx)
215 {
216         return win32_extract_stream(file.path, NULL, 0, lte, ctx);
217 }
218
219 static int
220 win32_extract_named_stream(file_spec_t file, const wchar_t *stream_name,
221                            size_t stream_name_nchars,
222                            struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
223 {
224         return win32_extract_stream(file.path, stream_name,
225                                     stream_name_nchars, lte, ctx);
226 }
227
228 struct win32_encrypted_extract_ctx {
229         const struct wim_lookup_table_entry *lte;
230         u64 offset;
231 };
232
233 static DWORD WINAPI
234 win32_encrypted_import_cb(unsigned char *data, void *_import_ctx,
235                           unsigned long *len_p)
236 {
237         struct win32_encrypted_extract_ctx *import_ctx = _import_ctx;
238         unsigned long len = *len_p;
239         const struct wim_lookup_table_entry *lte = import_ctx->lte;
240
241         len = min(len, lte->size - import_ctx->offset);
242
243         if (read_partial_wim_resource_into_buf(lte, len, import_ctx->offset, data))
244                 return ERROR_READ_FAULT;
245
246         import_ctx->offset += len;
247         *len_p = len;
248         return ERROR_SUCCESS;
249 }
250
251 static int
252 win32_extract_encrypted_stream(const wchar_t *path,
253                                struct wim_lookup_table_entry *lte,
254                                struct apply_ctx *ctx)
255 {
256         void *file_ctx;
257         DWORD err;
258         int ret;
259         struct win32_encrypted_extract_ctx extract_ctx;
260
261         err = OpenEncryptedFileRaw(path, CREATE_FOR_IMPORT, &file_ctx);
262         if (err != ERROR_SUCCESS) {
263                 set_errno_from_win32_error(err);
264                 ret = WIMLIB_ERR_OPEN;
265                 goto out;
266         }
267
268         extract_ctx.lte = lte;
269         extract_ctx.offset = 0;
270         err = WriteEncryptedFileRaw(win32_encrypted_import_cb, &extract_ctx,
271                                     file_ctx);
272         if (err != ERROR_SUCCESS) {
273                 set_errno_from_win32_error(err);
274                 ret = WIMLIB_ERR_WRITE;
275                 goto out_close;
276         }
277
278         ret = 0;
279 out_close:
280         CloseEncryptedFileRaw(file_ctx);
281 out:
282         return ret;
283 }
284
285 static BOOL
286 win32_set_special_file_attributes(const wchar_t *path, u32 attributes)
287 {
288         HANDLE h;
289         DWORD err;
290         USHORT compression_format = COMPRESSION_FORMAT_DEFAULT;
291         DWORD bytes_returned;
292
293         h = win32_open_existing_file(path, GENERIC_READ | GENERIC_WRITE);
294         if (h == INVALID_HANDLE_VALUE)
295                 goto error;
296
297         if (attributes & FILE_ATTRIBUTE_SPARSE_FILE)
298                 if (!DeviceIoControl(h, FSCTL_SET_SPARSE,
299                                      NULL, 0,
300                                      NULL, 0,
301                                      &bytes_returned, NULL))
302                         goto error_close_handle;
303
304         if (attributes & FILE_ATTRIBUTE_COMPRESSED)
305                 if (!DeviceIoControl(h, FSCTL_SET_COMPRESSION,
306                                      &compression_format, sizeof(USHORT),
307                                      NULL, 0,
308                                      &bytes_returned, NULL))
309                         goto error_close_handle;
310
311         if (!CloseHandle(h))
312                 goto error;
313
314         if (attributes & FILE_ATTRIBUTE_ENCRYPTED)
315                 if (!EncryptFile(path))
316                         goto error;
317
318         return TRUE;
319
320 error_close_handle:
321         err = GetLastError();
322         CloseHandle(h);
323         SetLastError(err);
324 error:
325         return FALSE;
326 }
327
328 static int
329 win32_set_file_attributes(const wchar_t *path, u32 attributes,
330                           struct apply_ctx *ctx, unsigned pass)
331 {
332         u32 special_attributes =
333                 FILE_ATTRIBUTE_REPARSE_POINT |
334                 FILE_ATTRIBUTE_DIRECTORY |
335                 FILE_ATTRIBUTE_SPARSE_FILE |
336                 FILE_ATTRIBUTE_COMPRESSED |
337                 FILE_ATTRIBUTE_ENCRYPTED;
338         u32 actual_attributes;
339
340         /* Delay setting FILE_ATTRIBUTE_READONLY on the initial pass (when files
341          * are created, but data not extracted); otherwise the system will
342          * refuse access to the file even if the process has SeRestorePrivilege.
343          */
344         if (pass == 0)
345                 attributes &= ~FILE_ATTRIBUTE_READONLY;
346
347         if (!SetFileAttributes(path, attributes & ~special_attributes))
348                 goto error;
349
350         if (pass != 0)
351                 return 0;
352
353         if (attributes & (FILE_ATTRIBUTE_SPARSE_FILE |
354                           FILE_ATTRIBUTE_ENCRYPTED |
355                           FILE_ATTRIBUTE_COMPRESSED))
356                 if (!win32_set_special_file_attributes(path, attributes))
357                         goto error;
358
359         /* If file is not supposed to be encrypted or compressed, remove
360          * defaulted encrypted or compressed attributes (from creating file in
361          * encrypted or compressed directory).  */
362         actual_attributes = GetFileAttributes(path);
363         if (actual_attributes == INVALID_FILE_ATTRIBUTES)
364                 goto error;
365
366         if ((actual_attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
367             !(attributes & FILE_ATTRIBUTE_ENCRYPTED))
368                 if (!DecryptFile(path, 0))
369                         goto error;
370         if ((actual_attributes & FILE_ATTRIBUTE_COMPRESSED) &&
371             !(attributes & FILE_ATTRIBUTE_COMPRESSED))
372         {
373                 HANDLE h;
374                 DWORD bytes_returned;
375                 USHORT compression_format = COMPRESSION_FORMAT_NONE;
376
377                 h = win32_open_existing_file(path, GENERIC_READ | GENERIC_WRITE);
378                 if (h == INVALID_HANDLE_VALUE)
379                         goto error;
380
381                 if (!DeviceIoControl(h, FSCTL_SET_COMPRESSION,
382                                      &compression_format, sizeof(USHORT),
383                                      NULL, 0,
384                                      &bytes_returned, NULL))
385                 {
386                         DWORD err = GetLastError();
387                         CloseHandle(h);
388                         SetLastError(err);
389                         goto error;
390                 }
391
392                 if (!CloseHandle(h))
393                         goto error;
394         }
395
396         return 0;
397
398 error:
399         set_errno_from_GetLastError();
400         return WIMLIB_ERR_SET_ATTRIBUTES;
401 }
402
403 static int
404 win32_set_reparse_data(const wchar_t *path, const u8 *rpbuf, u16 rpbuflen,
405                        struct apply_ctx *ctx)
406 {
407         HANDLE h;
408         DWORD err;
409         DWORD bytes_returned;
410
411         h = win32_open_existing_file(path, GENERIC_WRITE);
412         if (h == INVALID_HANDLE_VALUE)
413                 goto error;
414
415         if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
416                              (void*)rpbuf, rpbuflen,
417                              NULL, 0, &bytes_returned, NULL))
418                 goto error_close_handle;
419
420         if (!CloseHandle(h))
421                 goto error;
422
423         return 0;
424
425 error_close_handle:
426         err = GetLastError();
427         CloseHandle(h);
428         SetLastError(err);
429 error:
430         set_errno_from_GetLastError();
431         return WIMLIB_ERR_WRITE; /* XXX: need better error code */
432 }
433
434 static int
435 win32_set_short_name(const wchar_t *path, const wchar_t *short_name,
436                      size_t short_name_nchars, struct apply_ctx *ctx)
437 {
438         HANDLE h;
439         DWORD err;
440
441         h = win32_open_existing_file(path, GENERIC_WRITE | DELETE);
442         if (h == INVALID_HANDLE_VALUE)
443                 goto error;
444
445         if (short_name_nchars) {
446                 if (!SetFileShortName(h, short_name))
447                         goto error_close_handle;
448         } else if (running_on_windows_7_or_later()) {
449                 if (!SetFileShortName(h, L""))
450                         goto error_close_handle;
451         }
452
453         if (!CloseHandle(h))
454                 goto error;
455
456         return 0;
457
458 error_close_handle:
459         err = GetLastError();
460         CloseHandle(h);
461         SetLastError(err);
462 error:
463         set_errno_from_GetLastError();
464         return WIMLIB_ERR_WRITE; /* XXX: need better error code */
465 }
466
467 static DWORD
468 do_win32_set_security_descriptor(HANDLE h, const wchar_t *path,
469                                  SECURITY_INFORMATION info,
470                                  PSECURITY_DESCRIPTOR desc)
471 {
472 #ifdef WITH_NTDLL
473         if (func_NtSetSecurityObject) {
474                 return (*func_RtlNtStatusToDosError)(
475                                 (*func_NtSetSecurityObject)(h, info, desc));
476         }
477 #endif
478         if (SetFileSecurity(path, info, desc))
479                 return ERROR_SUCCESS;
480         else
481                 return GetLastError();
482 }
483
484 static int
485 win32_set_security_descriptor(const wchar_t *path, const u8 *desc,
486                               size_t desc_size, struct apply_ctx *ctx)
487 {
488         SECURITY_INFORMATION info;
489         HANDLE h;
490         DWORD err;
491         int ret;
492
493         info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
494                DACL_SECURITY_INFORMATION  | SACL_SECURITY_INFORMATION;
495         h = INVALID_HANDLE_VALUE;
496
497 #ifdef WITH_NTDLL
498         if (func_NtSetSecurityObject) {
499                 h = win32_open_existing_file(path, MAXIMUM_ALLOWED);
500                 if (h == INVALID_HANDLE_VALUE) {
501                         ERROR_WITH_ERRNO("Can't open %ls (%u)", path, GetLastError());
502                         goto error;
503                 }
504         }
505 #endif
506
507         for (;;) {
508                 err = do_win32_set_security_descriptor(h, path, info,
509                                                        (PSECURITY_DESCRIPTOR)desc);
510                 if (err == ERROR_SUCCESS)
511                         break;
512                 if ((err == ERROR_PRIVILEGE_NOT_HELD ||
513                      err == ERROR_ACCESS_DENIED) &&
514                     !(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
515                 {
516                         if (info & SACL_SECURITY_INFORMATION) {
517                                 info &= ~SACL_SECURITY_INFORMATION;
518                                 ctx->partial_security_descriptors++;
519                                 continue;
520                         }
521                         if (info & DACL_SECURITY_INFORMATION) {
522                                 info &= ~DACL_SECURITY_INFORMATION;
523                                 continue;
524                         }
525                         if (info & OWNER_SECURITY_INFORMATION) {
526                                 info &= ~OWNER_SECURITY_INFORMATION;
527                                 continue;
528                         }
529                         ctx->partial_security_descriptors--;
530                         ctx->no_security_descriptors++;
531                         break;
532                 }
533                 SetLastError(err);
534                 goto error;
535         }
536         ret = 0;
537 out_close:
538 #ifdef WITH_NTDLL
539         if (func_NtSetSecurityObject)
540                 CloseHandle(h);
541 #endif
542         return ret;
543
544 error:
545         set_errno_from_GetLastError();
546         ret = WIMLIB_ERR_SET_SECURITY;
547         goto out_close;
548 }
549
550 static int
551 win32_set_timestamps(const wchar_t *path, u64 creation_time,
552                      u64 last_write_time, u64 last_access_time,
553                      struct apply_ctx *ctx)
554 {
555         HANDLE h;
556         DWORD err;
557         FILETIME creationTime = {.dwLowDateTime = creation_time & 0xffffffff,
558                                  .dwHighDateTime = creation_time >> 32};
559         FILETIME lastAccessTime = {.dwLowDateTime = last_access_time & 0xffffffff,
560                                   .dwHighDateTime = last_access_time >> 32};
561         FILETIME lastWriteTime = {.dwLowDateTime = last_write_time & 0xffffffff,
562                                   .dwHighDateTime = last_write_time >> 32};
563
564         h = win32_open_existing_file(path, FILE_WRITE_ATTRIBUTES);
565         if (h == INVALID_HANDLE_VALUE)
566                 goto error;
567
568         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime))
569                 goto error_close_handle;
570
571         if (!CloseHandle(h))
572                 goto error;
573
574         return 0;
575
576 error_close_handle:
577         err = GetLastError();
578         CloseHandle(h);
579         SetLastError(err);
580 error:
581         set_errno_from_GetLastError();
582         return WIMLIB_ERR_SET_TIMESTAMPS;
583 }
584
585 const struct apply_operations win32_apply_ops = {
586         .name = L"Win32",
587
588         .target_is_root           = win32_path_is_root_of_drive,
589         .start_extract            = win32_start_extract,
590         .create_file              = win32_create_file,
591         .create_directory         = win32_create_directory,
592         .create_hardlink          = win32_create_hardlink,
593         .create_symlink           = win32_create_symlink,
594         .extract_unnamed_stream   = win32_extract_unnamed_stream,
595         .extract_named_stream     = win32_extract_named_stream,
596         .extract_encrypted_stream = win32_extract_encrypted_stream,
597         .set_file_attributes      = win32_set_file_attributes,
598         .set_reparse_data         = win32_set_reparse_data,
599         .set_short_name           = win32_set_short_name,
600         .set_security_descriptor  = win32_set_security_descriptor,
601         .set_timestamps           = win32_set_timestamps,
602
603         .path_prefix = L"\\\\?\\",
604         .path_prefix_nchars = 4,
605         .path_separator = L'\\',
606         .path_max = 32768,
607
608         .requires_realtarget_in_paths = 1,
609         .realpath_works_on_nonexisting_files = 1,
610         .root_directory_is_special = 1,
611         .requires_final_set_attributes_pass = 1,
612         .extract_encrypted_stream_creates_file = 1,
613         .requires_short_name_reordering = 1, /* TODO: check if this is really needed  */
614 };
615
616 #endif /* __WIN32__ */