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