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