extract.c: Omit encrypted files when unsupported
[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         ctx->supported_features.compressed_files          = !!(vol_flags & FILE_FILE_COMPRESSION);
51         ctx->supported_features.encrypted_files           = !!(vol_flags & FILE_SUPPORTS_ENCRYPTION);
52         ctx->supported_features.encrypted_directories     = !!(vol_flags & FILE_SUPPORTS_ENCRYPTION);
53         ctx->supported_features.not_context_indexed_files = 1;
54         ctx->supported_features.sparse_files              = !!(vol_flags & FILE_SUPPORTS_SPARSE_FILES);
55         ctx->supported_features.named_data_streams        = !!(vol_flags & FILE_NAMED_STREAMS);
56         ctx->supported_features.hard_links                = !!(vol_flags & FILE_SUPPORTS_HARD_LINKS);
57         ctx->supported_features.reparse_points            = !!(vol_flags & FILE_SUPPORTS_REPARSE_POINTS);
58         ctx->supported_features.security_descriptors      = !!(vol_flags & FILE_PERSISTENT_ACLS);
59         ctx->supported_features.short_names               = !!supports_SetFileShortName;
60         return 0;
61 }
62
63 static int
64 win32_create_file(const wchar_t *path, struct apply_ctx *ctx)
65 {
66         HANDLE h;
67
68         h = CreateFile(path, 0, 0, NULL, CREATE_ALWAYS,
69                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
70         if (h == INVALID_HANDLE_VALUE)
71                 goto error;
72         CloseHandle(h);
73         return 0;
74
75 error:
76         set_errno_from_GetLastError();
77         return WIMLIB_ERR_OPEN;
78 }
79
80 static int
81 win32_create_directory(const wchar_t *path, struct apply_ctx *ctx)
82 {
83         if (!CreateDirectory(path, NULL))
84                 if (GetLastError() != ERROR_ALREADY_EXISTS)
85                         goto error;
86         return 0;
87
88 error:
89         set_errno_from_GetLastError();
90         return WIMLIB_ERR_MKDIR;
91 }
92
93 static int
94 win32_create_hardlink(const wchar_t *oldpath, const wchar_t *newpath,
95                       struct apply_ctx *ctx)
96 {
97         if (!CreateHardLink(newpath, oldpath, NULL))
98                 goto error;
99         return 0;
100
101 error:
102         set_errno_from_GetLastError();
103         return WIMLIB_ERR_LINK;
104 }
105
106 static int
107 win32_extract_wim_chunk(const void *buf, size_t len, void *arg)
108 {
109         HANDLE h = (HANDLE)arg;
110         DWORD nbytes_written;
111
112         if (unlikely(!WriteFile(h, buf, len, &nbytes_written, NULL)))
113                 goto error;
114         if (unlikely(nbytes_written != len))
115                 goto error;
116         return 0;
117
118 error:
119         set_errno_from_GetLastError();
120         return WIMLIB_ERR_WRITE;
121 }
122
123 static int
124 win32_extract_stream(const wchar_t *path, const wchar_t *stream_name,
125                      size_t stream_name_nchars,
126                      struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
127 {
128         DWORD creationDisposition = OPEN_EXISTING;
129         wchar_t *stream_path = (wchar_t*)path;
130         HANDLE h;
131         int ret;
132
133         if (stream_name_nchars) {
134                 creationDisposition = CREATE_ALWAYS;
135                 stream_path = alloca(sizeof(wchar_t) *
136                                      (wcslen(path) + 1 +
137                                       wcslen(stream_name) + 1));
138                 swprintf(stream_path, L"%ls:%ls", path, stream_name);
139         }
140
141         h = CreateFile(stream_path, FILE_WRITE_DATA, 0, NULL,
142                        creationDisposition, FILE_FLAG_BACKUP_SEMANTICS |
143                                             FILE_FLAG_OPEN_REPARSE_POINT,
144                        NULL);
145         if (h == INVALID_HANDLE_VALUE)
146                 goto error;
147
148         ret = 0;
149         if (!lte)
150                 goto out_close_handle;
151         ret = extract_wim_resource(lte, wim_resource_size(lte),
152                                    win32_extract_wim_chunk, h);
153 out_close_handle:
154         if (!CloseHandle(h))
155                 goto error;
156         if (ret && !errno)
157                 errno = -1;
158         return ret;
159
160 error:
161         set_errno_from_GetLastError();
162         return WIMLIB_ERR_WRITE;
163 }
164
165 static int
166 win32_extract_unnamed_stream(file_spec_t file,
167                              struct wim_lookup_table_entry *lte,
168                              struct apply_ctx *ctx)
169 {
170         return win32_extract_stream(file.path, NULL, 0, lte, ctx);
171 }
172
173 static int
174 win32_extract_named_stream(file_spec_t file, const wchar_t *stream_name,
175                            size_t stream_name_nchars,
176                            struct wim_lookup_table_entry *lte, struct apply_ctx *ctx)
177 {
178         return win32_extract_stream(file.path, stream_name,
179                                     stream_name_nchars, lte, ctx);
180 }
181
182 struct win32_encrypted_extract_ctx {
183         const struct wim_lookup_table_entry *lte;
184         u64 offset;
185 };
186
187 static DWORD WINAPI
188 win32_encrypted_import_cb(unsigned char *data, void *_import_ctx,
189                           unsigned long *len_p)
190 {
191         struct win32_encrypted_extract_ctx *import_ctx = _import_ctx;
192         unsigned long len = *len_p;
193         const struct wim_lookup_table_entry *lte = import_ctx->lte;
194
195         len = min(len, wim_resource_size(lte) - import_ctx->offset);
196
197         if (read_partial_wim_resource_into_buf(lte, len, import_ctx->offset, data))
198                 return ERROR_READ_FAULT;
199
200         import_ctx->offset += len;
201         *len_p = len;
202         return ERROR_SUCCESS;
203 }
204
205 static int
206 win32_extract_encrypted_stream(const wchar_t *path,
207                                struct wim_lookup_table_entry *lte,
208                                struct apply_ctx *ctx)
209 {
210         void *file_ctx;
211         DWORD err;
212         int ret;
213         struct win32_encrypted_extract_ctx extract_ctx;
214
215         err = OpenEncryptedFileRaw(path, CREATE_FOR_IMPORT, &file_ctx);
216         if (err != ERROR_SUCCESS) {
217                 errno = win32_error_to_errno(err);
218                 ret = WIMLIB_ERR_OPEN;
219                 goto out;
220         }
221
222         extract_ctx.lte = lte;
223         extract_ctx.offset = 0;
224         err = WriteEncryptedFileRaw(win32_encrypted_import_cb, &extract_ctx,
225                                     file_ctx);
226         if (err != ERROR_SUCCESS) {
227                 errno = win32_error_to_errno(err);
228                 ret = WIMLIB_ERR_WRITE;
229                 goto out_close;
230         }
231
232         ret = 0;
233 out_close:
234         CloseEncryptedFileRaw(file_ctx);
235 out:
236         return ret;
237 }
238
239 static BOOL
240 win32_set_special_file_attributes(const wchar_t *path, u32 attributes)
241 {
242         HANDLE h;
243         DWORD err;
244         USHORT compression_format = COMPRESSION_FORMAT_DEFAULT;
245         DWORD bytes_returned;
246
247         h = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
248                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
249                                       FILE_FLAG_OPEN_REPARSE_POINT,
250                        NULL);
251         if (h == INVALID_HANDLE_VALUE)
252                 goto error;
253
254         if (attributes & FILE_ATTRIBUTE_SPARSE_FILE)
255                 if (!DeviceIoControl(h, FSCTL_SET_SPARSE,
256                                      NULL, 0,
257                                      NULL, 0,
258                                      &bytes_returned, NULL))
259                         goto error_close_handle;
260
261         if (attributes & FILE_ATTRIBUTE_COMPRESSED)
262                 if (!DeviceIoControl(h, FSCTL_SET_COMPRESSION,
263                                      &compression_format, sizeof(USHORT),
264                                      NULL, 0,
265                                      &bytes_returned, NULL))
266                         goto error_close_handle;
267
268         if (!CloseHandle(h))
269                 goto error;
270
271         if (attributes & FILE_ATTRIBUTE_ENCRYPTED)
272                 if (!EncryptFile(path))
273                         goto error;
274
275         return TRUE;
276
277 error_close_handle:
278         err = GetLastError();
279         CloseHandle(h);
280         SetLastError(err);
281 error:
282         return FALSE;
283 }
284
285 static int
286 win32_set_file_attributes(const wchar_t *path, u32 attributes,
287                           struct apply_ctx *ctx)
288 {
289         u32 special_attributes =
290                 FILE_ATTRIBUTE_REPARSE_POINT |
291                 FILE_ATTRIBUTE_DIRECTORY |
292                 FILE_ATTRIBUTE_SPARSE_FILE |
293                 FILE_ATTRIBUTE_COMPRESSED |
294                 FILE_ATTRIBUTE_ENCRYPTED;
295         u32 actual_attributes;
296
297         if (!SetFileAttributes(path, attributes & ~special_attributes))
298                 goto error;
299
300         if (attributes & (FILE_ATTRIBUTE_SPARSE_FILE |
301                           FILE_ATTRIBUTE_ENCRYPTED |
302                           FILE_ATTRIBUTE_COMPRESSED))
303                 if (!win32_set_special_file_attributes(path, attributes))
304                         goto error;
305
306         /* If file is not supposed to be encrypted or compressed, remove
307          * defaulted encrypted or compressed attributes (from creating file in
308          * encrypted or compressed directory).  */
309         actual_attributes = GetFileAttributes(path);
310         if (actual_attributes == INVALID_FILE_ATTRIBUTES)
311                 goto error;
312
313         if ((actual_attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
314             !(attributes & FILE_ATTRIBUTE_ENCRYPTED))
315                 if (!DecryptFile(path, 0))
316                         goto error;
317         if ((actual_attributes & FILE_ATTRIBUTE_COMPRESSED) &&
318             !(attributes & FILE_ATTRIBUTE_COMPRESSED))
319         {
320                 HANDLE h;
321                 DWORD bytes_returned;
322                 USHORT compression_format = COMPRESSION_FORMAT_NONE;
323
324                 h = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
325                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
326                                               FILE_FLAG_OPEN_REPARSE_POINT,
327                                NULL);
328                 if (h == INVALID_HANDLE_VALUE)
329                         goto error;
330
331                 if (!DeviceIoControl(h, FSCTL_SET_COMPRESSION,
332                                      &compression_format, sizeof(USHORT),
333                                      NULL, 0,
334                                      &bytes_returned, NULL))
335                 {
336                         DWORD err = GetLastError();
337                         CloseHandle(h);
338                         SetLastError(err);
339                         goto error;
340                 }
341
342                 if (!CloseHandle(h))
343                         goto error;
344         }
345
346 success:
347         return 0;
348
349 error:
350         set_errno_from_GetLastError();
351         return WIMLIB_ERR_SET_ATTRIBUTES;
352 }
353
354 static int
355 win32_set_reparse_data(const wchar_t *path, const u8 *rpbuf, u16 rpbuflen,
356                        struct apply_ctx *ctx)
357 {
358         HANDLE h;
359         DWORD err;
360         DWORD bytes_returned;
361
362         h = CreateFile(path, GENERIC_WRITE, 0, NULL,
363                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
364                                       FILE_FLAG_OPEN_REPARSE_POINT,
365                        NULL);
366         if (h == INVALID_HANDLE_VALUE)
367                 goto error;
368
369         if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT,
370                              (void*)rpbuf, rpbuflen,
371                              NULL, 0, &bytes_returned, NULL))
372                 goto error_close_handle;
373
374         if (!CloseHandle(h))
375                 goto error;
376
377         return 0;
378
379 error_close_handle:
380         err = GetLastError();
381         CloseHandle(h);
382         SetLastError(err);
383 error:
384         set_errno_from_GetLastError();
385         return WIMLIB_ERR_WRITE; /* XXX: need better error code */
386 }
387
388 static int
389 win32_set_short_name(const wchar_t *path, const wchar_t *short_name,
390                      size_t short_name_nchars, struct apply_ctx *ctx)
391 {
392         HANDLE h;
393         DWORD err;
394
395         h = CreateFile(path, GENERIC_WRITE | DELETE, 0, NULL,
396                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
397                                       FILE_FLAG_OPEN_REPARSE_POINT,
398                        NULL);
399         if (h == INVALID_HANDLE_VALUE)
400                 goto error;
401
402         if (short_name_nchars) {
403                 if (!SetFileShortName(h, short_name))
404                         goto error_close_handle;
405         } else if (running_on_windows_7_or_later()) {
406                 if (!SetFileShortName(h, L""))
407                         goto error_close_handle;
408         }
409
410         if (!CloseHandle(h))
411                 goto error;
412
413         return 0;
414
415 error_close_handle:
416         err = GetLastError();
417         CloseHandle(h);
418         SetLastError(err);
419 error:
420         set_errno_from_GetLastError();
421         return WIMLIB_ERR_WRITE; /* XXX: need better error code */
422 }
423
424 static int
425 win32_set_security_descriptor(const wchar_t *path, const u8 *desc, size_t desc_size,
426                               struct apply_ctx *ctx)
427 {
428         SECURITY_INFORMATION info;
429
430         info = OWNER_SECURITY_INFORMATION |
431                 GROUP_SECURITY_INFORMATION |
432                 DACL_SECURITY_INFORMATION  |
433                 SACL_SECURITY_INFORMATION;
434 retry:
435         if (!SetFileSecurity(path, info, (PSECURITY_DESCRIPTOR)desc)) {
436                 if (!(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) &&
437                     GetLastError() == ERROR_PRIVILEGE_NOT_HELD &&
438                     (info & SACL_SECURITY_INFORMATION))
439                 {
440                         info &= ~SACL_SECURITY_INFORMATION;
441                         goto retry;
442                 }
443                 goto error;
444         }
445         return 0;
446
447 error:
448         set_errno_from_GetLastError();
449         return WIMLIB_ERR_SET_SECURITY;
450 }
451
452 static int
453 win32_set_timestamps(const wchar_t *path, u64 creation_time,
454                      u64 last_write_time, u64 last_access_time,
455                      struct apply_ctx *ctx)
456 {
457         HANDLE h;
458         DWORD err;
459         FILETIME creationTime = {.dwLowDateTime = creation_time & 0xffffffff,
460                                  .dwHighDateTime = creation_time >> 32};
461         FILETIME lastAccessTime = {.dwLowDateTime = last_access_time & 0xffffffff,
462                                   .dwHighDateTime = last_access_time >> 32};
463         FILETIME lastWriteTime = {.dwLowDateTime = last_write_time & 0xffffffff,
464                                   .dwHighDateTime = last_write_time >> 32};
465
466         h = CreateFile(path, FILE_WRITE_ATTRIBUTES, 0, NULL,
467                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
468                                       FILE_FLAG_OPEN_REPARSE_POINT,
469                        NULL);
470         if (h == INVALID_HANDLE_VALUE)
471                 goto error;
472
473         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime))
474                 goto error_close_handle;
475
476         if (!CloseHandle(h))
477                 goto error;
478
479         return 0;
480
481 error_close_handle:
482         err = GetLastError();
483         CloseHandle(h);
484         SetLastError(err);
485 error:
486         set_errno_from_GetLastError();
487         return WIMLIB_ERR_SET_TIMESTAMPS;
488 }
489
490 const struct apply_operations win32_apply_ops = {
491         .name = L"Win32",
492
493         .target_is_root           = win32_path_is_root_of_drive,
494         .start_extract            = win32_start_extract,
495         .create_file              = win32_create_file,
496         .create_directory         = win32_create_directory,
497         .create_hardlink          = win32_create_hardlink,
498         .extract_unnamed_stream   = win32_extract_unnamed_stream,
499         .extract_named_stream     = win32_extract_named_stream,
500         .extract_encrypted_stream = win32_extract_encrypted_stream,
501         .set_file_attributes      = win32_set_file_attributes,
502         .set_reparse_data         = win32_set_reparse_data,
503         .set_short_name           = win32_set_short_name,
504         .set_security_descriptor  = win32_set_security_descriptor,
505         .set_timestamps           = win32_set_timestamps,
506
507         .path_prefix = L"\\\\?\\",
508         .path_prefix_nchars = 4,
509         .path_separator = L'\\',
510         .path_max = 32768,
511
512         .requires_realtarget_in_paths = 1,
513         .realpath_works_on_nonexisting_files = 1,
514         .root_directory_is_special = 1,
515 };
516
517 #endif /* __WIN32__ */