]> wimlib.net Git - wimlib/blob - src/win32_capture.c
bbcfbe3d84d3f9000cb16e6ced8177e47d8d4ff9
[wimlib] / src / win32_capture.c
1 /*
2  * win32_capture.c - Windows-specific code for capturing files into 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/capture.h"
33 #include "wimlib/endianness.h"
34 #include "wimlib/error.h"
35 #include "wimlib/lookup_table.h"
36 #include "wimlib/paths.h"
37 #include "wimlib/reparse.h"
38
39 #define MAX_GET_SD_ACCESS_DENIED_WARNINGS 1
40 #define MAX_GET_SACL_PRIV_NOTHELD_WARNINGS 1
41 #define MAX_CAPTURE_LONG_PATH_WARNINGS 5
42
43 struct win32_capture_state {
44         unsigned long num_get_sd_access_denied;
45         unsigned long num_get_sacl_priv_notheld;
46         unsigned long num_long_path_warnings;
47 };
48
49
50 static const wchar_t *capture_access_denied_msg =
51 L"         If you are not running this program as the administrator, you may\n"
52  "         need to do so, so that all data and metadata can be backed up.\n"
53  "         Otherwise, there may be no way to access the desired data or\n"
54  "         metadata without taking ownership of the file or directory.\n"
55  ;
56
57 int
58 read_win32_file_prefix(const struct wim_lookup_table_entry *lte,
59                        u64 size,
60                        consume_data_callback_t cb,
61                        void *ctx_or_buf,
62                        int _ignored_flags)
63 {
64         int ret = 0;
65         void *out_buf;
66         DWORD err;
67         u64 bytes_remaining;
68
69         HANDLE hFile = win32_open_file_data_only(lte->file_on_disk);
70         if (hFile == INVALID_HANDLE_VALUE) {
71                 err = GetLastError();
72                 ERROR("Failed to open \"%ls\"", lte->file_on_disk);
73                 win32_error(err);
74                 return WIMLIB_ERR_OPEN;
75         }
76
77         if (cb)
78                 out_buf = alloca(WIM_CHUNK_SIZE);
79         else
80                 out_buf = ctx_or_buf;
81
82         bytes_remaining = size;
83         while (bytes_remaining) {
84                 DWORD bytesToRead, bytesRead;
85
86                 bytesToRead = min(WIM_CHUNK_SIZE, bytes_remaining);
87                 if (!ReadFile(hFile, out_buf, bytesToRead, &bytesRead, NULL) ||
88                     bytesRead != bytesToRead)
89                 {
90                         err = GetLastError();
91                         ERROR("Failed to read data from \"%ls\"", lte->file_on_disk);
92                         win32_error(err);
93                         ret = WIMLIB_ERR_READ;
94                         break;
95                 }
96                 bytes_remaining -= bytesRead;
97                 if (cb) {
98                         ret = (*cb)(out_buf, bytesRead, ctx_or_buf);
99                         if (ret)
100                                 break;
101                 } else {
102                         out_buf += bytesRead;
103                 }
104         }
105         CloseHandle(hFile);
106         return ret;
107 }
108
109 struct win32_encrypted_read_ctx {
110         consume_data_callback_t read_prefix_cb;
111         void *read_prefix_ctx_or_buf;
112         int wimlib_err_code;
113         void *buf;
114         size_t buf_filled;
115         u64 bytes_remaining;
116 };
117
118 static DWORD WINAPI
119 win32_encrypted_export_cb(unsigned char *_data, void *_ctx, unsigned long len)
120 {
121         const void *data = _data;
122         struct win32_encrypted_read_ctx *ctx = _ctx;
123         int ret;
124
125         DEBUG("len = %lu", len);
126         if (ctx->read_prefix_cb) {
127                 /* The length of the buffer passed to the ReadEncryptedFileRaw()
128                  * export callback is undocumented, so we assume it may be of
129                  * arbitrary size. */
130                 size_t bytes_to_buffer = min(ctx->bytes_remaining - ctx->buf_filled,
131                                              len);
132                 while (bytes_to_buffer) {
133                         size_t bytes_to_copy_to_buf =
134                                 min(bytes_to_buffer, WIM_CHUNK_SIZE - ctx->buf_filled);
135
136                         memcpy(ctx->buf + ctx->buf_filled, data,
137                                bytes_to_copy_to_buf);
138                         ctx->buf_filled += bytes_to_copy_to_buf;
139                         data += bytes_to_copy_to_buf;
140                         bytes_to_buffer -= bytes_to_copy_to_buf;
141
142                         if (ctx->buf_filled == WIM_CHUNK_SIZE ||
143                             ctx->buf_filled == ctx->bytes_remaining)
144                         {
145                                 ret = (*ctx->read_prefix_cb)(ctx->buf,
146                                                              ctx->buf_filled,
147                                                              ctx->read_prefix_ctx_or_buf);
148                                 if (ret) {
149                                         ctx->wimlib_err_code = ret;
150                                         /* Shouldn't matter what error code is returned
151                                          * here, as long as it isn't ERROR_SUCCESS. */
152                                         return ERROR_READ_FAULT;
153                                 }
154                                 ctx->bytes_remaining -= ctx->buf_filled;
155                                 ctx->buf_filled = 0;
156                         }
157                 }
158         } else {
159                 size_t len_to_copy = min(len, ctx->bytes_remaining);
160                 ctx->read_prefix_ctx_or_buf = mempcpy(ctx->read_prefix_ctx_or_buf,
161                                                       data,
162                                                       len_to_copy);
163                 ctx->bytes_remaining -= len_to_copy;
164         }
165         return ERROR_SUCCESS;
166 }
167
168 int
169 read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
170                                  u64 size,
171                                  consume_data_callback_t cb,
172                                  void *ctx_or_buf,
173                                  int _ignored_flags)
174 {
175         struct win32_encrypted_read_ctx export_ctx;
176         DWORD err;
177         void *file_ctx;
178         int ret;
179
180         DEBUG("Reading %"PRIu64" bytes from encryted file \"%ls\"",
181               size, lte->file_on_disk);
182
183         export_ctx.read_prefix_cb = cb;
184         export_ctx.read_prefix_ctx_or_buf = ctx_or_buf;
185         export_ctx.wimlib_err_code = 0;
186         if (cb) {
187                 export_ctx.buf = MALLOC(WIM_CHUNK_SIZE);
188                 if (!export_ctx.buf)
189                         return WIMLIB_ERR_NOMEM;
190         } else {
191                 export_ctx.buf = NULL;
192         }
193         export_ctx.buf_filled = 0;
194         export_ctx.bytes_remaining = size;
195
196         err = OpenEncryptedFileRawW(lte->file_on_disk, 0, &file_ctx);
197         if (err != ERROR_SUCCESS) {
198                 ERROR("Failed to open encrypted file \"%ls\" for raw read",
199                       lte->file_on_disk);
200                 win32_error(err);
201                 ret = WIMLIB_ERR_OPEN;
202                 goto out_free_buf;
203         }
204         err = ReadEncryptedFileRaw(win32_encrypted_export_cb,
205                                    &export_ctx, file_ctx);
206         if (err != ERROR_SUCCESS) {
207                 ERROR("Failed to read encrypted file \"%ls\"",
208                       lte->file_on_disk);
209                 win32_error(err);
210                 ret = export_ctx.wimlib_err_code;
211                 if (ret == 0)
212                         ret = WIMLIB_ERR_READ;
213         } else if (export_ctx.bytes_remaining != 0) {
214                 ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from "
215                       "encryted file \"%ls\"",
216                       size - export_ctx.bytes_remaining, size,
217                       lte->file_on_disk);
218                 ret = WIMLIB_ERR_READ;
219         } else {
220                 ret = 0;
221         }
222         CloseEncryptedFileRaw(file_ctx);
223 out_free_buf:
224         FREE(export_ctx.buf);
225         return ret;
226 }
227
228
229 static u64
230 FILETIME_to_u64(const FILETIME *ft)
231 {
232         return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime;
233 }
234
235 static int
236 win32_get_short_name(struct wim_dentry *dentry, const wchar_t *path)
237 {
238         WIN32_FIND_DATAW dat;
239         HANDLE hFind;
240         int ret = 0;
241
242         /* If we can't read the short filename for some reason, we just ignore
243          * the error and assume the file has no short name.  I don't think this
244          * should be an issue, since the short names are essentially obsolete
245          * anyway. */
246         hFind = FindFirstFileW(path, &dat);
247         if (hFind != INVALID_HANDLE_VALUE) {
248                 if (dat.cAlternateFileName[0] != L'\0') {
249                         DEBUG("\"%ls\": short name \"%ls\"", path, dat.cAlternateFileName);
250                         size_t short_name_nbytes = wcslen(dat.cAlternateFileName) *
251                                                    sizeof(wchar_t);
252                         size_t n = short_name_nbytes + sizeof(wchar_t);
253                         dentry->short_name = MALLOC(n);
254                         if (dentry->short_name) {
255                                 memcpy(dentry->short_name, dat.cAlternateFileName, n);
256                                 dentry->short_name_nbytes = short_name_nbytes;
257                         } else {
258                                 ret = WIMLIB_ERR_NOMEM;
259                         }
260                 }
261                 FindClose(hFind);
262         }
263         return ret;
264 }
265
266 static int
267 win32_get_security_descriptor(struct wim_dentry *dentry,
268                               struct wim_sd_set *sd_set,
269                               const wchar_t *path,
270                               struct win32_capture_state *state,
271                               int add_flags)
272 {
273         SECURITY_INFORMATION requestedInformation;
274         DWORD lenNeeded = 0;
275         BOOL status;
276         DWORD err;
277         unsigned long n;
278
279         requestedInformation = DACL_SECURITY_INFORMATION |
280                                SACL_SECURITY_INFORMATION |
281                                OWNER_SECURITY_INFORMATION |
282                                GROUP_SECURITY_INFORMATION;
283 again:
284         /* Request length of security descriptor */
285         status = GetFileSecurityW(path, requestedInformation,
286                                   NULL, 0, &lenNeeded);
287         err = GetLastError();
288         if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
289                 DWORD len = lenNeeded;
290                 char buf[len];
291                 if (GetFileSecurityW(path, requestedInformation,
292                                      (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
293                 {
294                         int security_id = sd_set_add_sd(sd_set, buf, len);
295                         if (security_id < 0)
296                                 return WIMLIB_ERR_NOMEM;
297                         else {
298                                 dentry->d_inode->i_security_id = security_id;
299                                 return 0;
300                         }
301                 } else {
302                         err = GetLastError();
303                 }
304         }
305
306         if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS)
307                 goto fail;
308
309         switch (err) {
310         case ERROR_PRIVILEGE_NOT_HELD:
311                 if (requestedInformation & SACL_SECURITY_INFORMATION) {
312                         n = state->num_get_sacl_priv_notheld++;
313                         requestedInformation &= ~SACL_SECURITY_INFORMATION;
314                         if (n < MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
315                                 WARNING(
316 "We don't have enough privileges to read the full security\n"
317 "          descriptor of \"%ls\"!\n"
318 "          Re-trying with SACL omitted.\n", path);
319                         } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
320                                 WARNING(
321 "Suppressing further privileges not held error messages when reading\n"
322 "          security descriptors.");
323                         }
324                         goto again;
325                 }
326                 /* Fall through */
327         case ERROR_ACCESS_DENIED:
328                 n = state->num_get_sd_access_denied++;
329                 if (n < MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
330                         WARNING("Failed to read security descriptor of \"%ls\": "
331                                 "Access denied!\n%ls", path, capture_access_denied_msg);
332                 } else if (n == MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
333                         WARNING("Suppressing further access denied errors messages i"
334                                 "when reading security descriptors");
335                 }
336                 return 0;
337         default:
338 fail:
339                 ERROR("Failed to read security descriptor of \"%ls\"", path);
340                 win32_error(err);
341                 return WIMLIB_ERR_READ;
342         }
343 }
344
345 static int
346 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
347                                   wchar_t *path,
348                                   size_t path_num_chars,
349                                   struct add_image_params *params,
350                                   struct win32_capture_state *state,
351                                   unsigned vol_flags);
352
353 /* Reads the directory entries of directory using a Win32 API and recursively
354  * calls win32_build_dentry_tree() on them. */
355 static int
356 win32_recurse_directory(struct wim_dentry *root,
357                         wchar_t *dir_path,
358                         size_t dir_path_num_chars,
359                         struct add_image_params *params,
360                         struct win32_capture_state *state,
361                         unsigned vol_flags)
362 {
363         WIN32_FIND_DATAW dat;
364         HANDLE hFind;
365         DWORD err;
366         int ret;
367
368         DEBUG("Recurse to directory \"%ls\"", dir_path);
369
370         /* Begin reading the directory by calling FindFirstFileW.  Unlike UNIX
371          * opendir(), FindFirstFileW has file globbing built into it.  But this
372          * isn't what we actually want, so just add a dummy glob to get all
373          * entries. */
374         dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR;
375         dir_path[dir_path_num_chars + 1] = L'*';
376         dir_path[dir_path_num_chars + 2] = L'\0';
377         hFind = FindFirstFileW(dir_path, &dat);
378         dir_path[dir_path_num_chars] = L'\0';
379
380         if (hFind == INVALID_HANDLE_VALUE) {
381                 err = GetLastError();
382                 if (err == ERROR_FILE_NOT_FOUND) {
383                         return 0;
384                 } else {
385                         ERROR("Failed to read directory \"%ls\"", dir_path);
386                         win32_error(err);
387                         return WIMLIB_ERR_READ;
388                 }
389         }
390         ret = 0;
391         do {
392                 /* Skip . and .. entries */
393                 if (dat.cFileName[0] == L'.' &&
394                     (dat.cFileName[1] == L'\0' ||
395                      (dat.cFileName[1] == L'.' &&
396                       dat.cFileName[2] == L'\0')))
397                         continue;
398                 size_t filename_len = wcslen(dat.cFileName);
399
400                 dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR;
401                 wmemcpy(dir_path + dir_path_num_chars + 1,
402                         dat.cFileName,
403                         filename_len + 1);
404
405                 struct wim_dentry *child;
406                 size_t path_len = dir_path_num_chars + 1 + filename_len;
407                 ret = win32_build_dentry_tree_recursive(&child,
408                                                         dir_path,
409                                                         path_len,
410                                                         params,
411                                                         state,
412                                                         vol_flags);
413                 dir_path[dir_path_num_chars] = L'\0';
414                 if (ret)
415                         goto out_find_close;
416                 if (child)
417                         dentry_add_child(root, child);
418         } while (FindNextFileW(hFind, &dat));
419         err = GetLastError();
420         if (err != ERROR_NO_MORE_FILES) {
421                 ERROR("Failed to read directory \"%ls\"", dir_path);
422                 win32_error(err);
423                 if (ret == 0)
424                         ret = WIMLIB_ERR_READ;
425         }
426 out_find_close:
427         FindClose(hFind);
428         return ret;
429 }
430
431 /* Reparse point fixup status code */
432 enum rp_status {
433         /* Reparse point corresponded to an absolute symbolic link or junction
434          * point that pointed outside the directory tree being captured, and
435          * therefore was excluded. */
436         RP_EXCLUDED       = 0x0,
437
438         /* Reparse point was not fixed as it was either a relative symbolic
439          * link, a mount point, or something else we could not understand. */
440         RP_NOT_FIXED      = 0x1,
441
442         /* Reparse point corresponded to an absolute symbolic link or junction
443          * point that pointed inside the directory tree being captured, where
444          * the target was specified by a "full" \??\ prefixed path, and
445          * therefore was fixed to be relative to the root of the directory tree
446          * being captured. */
447         RP_FIXED_FULLPATH = 0x2,
448
449         /* Same as RP_FIXED_FULLPATH, except the absolute link target did not
450          * have the \??\ prefix.  It may have begun with a drive letter though.
451          * */
452         RP_FIXED_ABSPATH  = 0x4,
453
454         /* Either RP_FIXED_FULLPATH or RP_FIXED_ABSPATH. */
455         RP_FIXED          = RP_FIXED_FULLPATH | RP_FIXED_ABSPATH,
456 };
457
458 /* Given the "substitute name" target of a Windows reparse point, try doing a
459  * fixup where we change it to be absolute relative to the root of the directory
460  * tree being captured.
461  *
462  * Note that this is only executed when WIMLIB_ADD_FLAG_RPFIX has been
463  * set.
464  *
465  * @capture_root_ino and @capture_root_dev indicate the inode number and device
466  * of the root of the directory tree being captured.  They are meant to identify
467  * this directory (as an alternative to its actual path, which could potentially
468  * be reached via multiple destinations due to other symbolic links).  This may
469  * not work properly on FAT, which doesn't seem to supply proper inode numbers
470  * or file IDs.  However, FAT doesn't support reparse points so this function
471  * wouldn't even be called anyway.
472  */
473 static enum rp_status
474 win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
475                                  u64 capture_root_ino, u64 capture_root_dev,
476                                  u32 rptag)
477 {
478         u16 target_nchars = *target_nbytes_p / 2;
479         size_t stripped_chars;
480         wchar_t *orig_target;
481         int ret;
482
483         ret = parse_substitute_name(target, *target_nbytes_p, rptag);
484         if (ret < 0)
485                 return RP_NOT_FIXED;
486         stripped_chars = ret;
487         if (stripped_chars)
488                 stripped_chars -= 2;
489         target[target_nchars] = L'\0';
490         orig_target = target;
491         target = capture_fixup_absolute_symlink(target + stripped_chars,
492                                                 capture_root_ino, capture_root_dev);
493         if (!target)
494                 return RP_EXCLUDED;
495         target_nchars = wcslen(target);
496         wmemmove(orig_target + stripped_chars, target, target_nchars + 1);
497         *target_nbytes_p = (target_nchars + stripped_chars) * sizeof(wchar_t);
498         DEBUG("Fixed reparse point (new target: \"%ls\")", orig_target);
499         if (stripped_chars)
500                 return RP_FIXED_FULLPATH;
501         else
502                 return RP_FIXED_ABSPATH;
503 }
504
505 /* Returns: `enum rp_status' value on success; negative WIMLIB_ERR_* value on
506  * failure. */
507 static int
508 win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
509                         u64 capture_root_ino, u64 capture_root_dev,
510                         const wchar_t *path)
511 {
512         struct reparse_data rpdata;
513         int ret;
514         enum rp_status rp_status;
515
516         ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata);
517         if (ret)
518                 return -ret;
519
520         rp_status = win32_capture_maybe_rpfix_target(rpdata.substitute_name,
521                                                      &rpdata.substitute_name_nbytes,
522                                                      capture_root_ino,
523                                                      capture_root_dev,
524                                                      le32_to_cpu(*(le32*)rpbuf));
525         if (rp_status & RP_FIXED) {
526                 wimlib_assert(rpdata.substitute_name_nbytes % 2 == 0);
527                 utf16lechar substitute_name_copy[rpdata.substitute_name_nbytes / 2];
528                 wmemcpy(substitute_name_copy, rpdata.substitute_name,
529                         rpdata.substitute_name_nbytes / 2);
530                 rpdata.substitute_name = substitute_name_copy;
531                 rpdata.print_name = substitute_name_copy;
532                 rpdata.print_name_nbytes = rpdata.substitute_name_nbytes;
533                 if (rp_status == RP_FIXED_FULLPATH) {
534                         /* "full path", meaning \??\ prefixed.  We should not
535                          * include this prefix in the print name, as it is
536                          * apparently meant for the filesystem driver only. */
537                         rpdata.print_name += 4;
538                         rpdata.print_name_nbytes -= 8;
539                 }
540                 ret = make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
541                 if (ret == 0)
542                         ret = rp_status;
543                 else
544                         ret = -ret;
545         } else {
546                 if (rp_status == RP_EXCLUDED) {
547                         size_t print_name_nchars = rpdata.print_name_nbytes / 2;
548                         wchar_t print_name0[print_name_nchars + 1];
549                         print_name0[print_name_nchars] = L'\0';
550                         wmemcpy(print_name0, rpdata.print_name, print_name_nchars);
551                         WARNING("Ignoring %ls pointing out of capture directory:\n"
552                                 "          \"%ls\" -> \"%ls\"\n"
553                                 "          (Use --norpfix to capture all symbolic links "
554                                 "and junction points as-is)",
555                                 (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK) ?
556                                         L"absolute symbolic link" : L"junction point",
557                                 path, print_name0);
558                 }
559                 ret = rp_status;
560         }
561         return ret;
562 }
563
564 /*
565  * Loads the reparse point data from a reparse point into memory, optionally
566  * fixing the targets of absolute symbolic links and junction points to be
567  * relative to the root of capture.
568  *
569  * @hFile:  Open handle to the reparse point.
570  * @path:   Path to the reparse point.  Used for error messages only.
571  * @params: Additional parameters, including whether to do reparse point fixups
572  *          or not.
573  * @rpbuf:  Buffer of length at least REPARSE_POINT_MAX_SIZE bytes into which
574  *          the reparse point buffer will be loaded.
575  * @rpbuflen_ret:  On success, the length of the reparse point buffer in bytes
576  *                 is written to this location.
577  *
578  * Returns:
579  *      On success, returns an `enum rp_status' value that indicates if and/or
580  *      how the reparse point fixup was done.
581  *
582  *      On failure, returns a negative value that is a negated WIMLIB_ERR_*
583  *      code.
584  */
585 static int
586 win32_get_reparse_data(HANDLE hFile, const wchar_t *path,
587                        struct add_image_params *params,
588                        u8 *rpbuf, u16 *rpbuflen_ret)
589 {
590         DWORD bytesReturned;
591         u32 reparse_tag;
592         int ret;
593         u16 rpbuflen;
594
595         DEBUG("Loading reparse data from \"%ls\"", path);
596         if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT,
597                              NULL, /* "Not used with this operation; set to NULL" */
598                              0, /* "Not used with this operation; set to 0" */
599                              rpbuf, /* "A pointer to a buffer that
600                                                    receives the reparse point data */
601                              REPARSE_POINT_MAX_SIZE, /* "The size of the output
602                                                         buffer, in bytes */
603                              &bytesReturned,
604                              NULL))
605         {
606                 DWORD err = GetLastError();
607                 ERROR("Failed to get reparse data of \"%ls\"", path);
608                 win32_error(err);
609                 return -WIMLIB_ERR_READ;
610         }
611         if (bytesReturned < 8 || bytesReturned > REPARSE_POINT_MAX_SIZE) {
612                 ERROR("Reparse data on \"%ls\" is invalid", path);
613                 return -WIMLIB_ERR_INVALID_REPARSE_DATA;
614         }
615
616         rpbuflen = bytesReturned;
617         reparse_tag = le32_to_cpu(*(le32*)rpbuf);
618         if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
619             (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
620              reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
621         {
622                 /* Try doing reparse point fixup */
623                 ret = win32_capture_try_rpfix(rpbuf,
624                                               &rpbuflen,
625                                               params->capture_root_ino,
626                                               params->capture_root_dev,
627                                               path);
628         } else {
629                 ret = RP_NOT_FIXED;
630         }
631         *rpbuflen_ret = rpbuflen;
632         return ret;
633 }
634
635 static DWORD WINAPI
636 win32_tally_encrypted_size_cb(unsigned char *_data, void *_ctx,
637                               unsigned long len)
638 {
639         *(u64*)_ctx += len;
640         return ERROR_SUCCESS;
641 }
642
643 static int
644 win32_get_encrypted_file_size(const wchar_t *path, u64 *size_ret)
645 {
646         DWORD err;
647         void *file_ctx;
648         int ret;
649
650         *size_ret = 0;
651         err = OpenEncryptedFileRawW(path, 0, &file_ctx);
652         if (err != ERROR_SUCCESS) {
653                 ERROR("Failed to open encrypted file \"%ls\" for raw read", path);
654                 win32_error(err);
655                 return WIMLIB_ERR_OPEN;
656         }
657         err = ReadEncryptedFileRaw(win32_tally_encrypted_size_cb,
658                                    size_ret, file_ctx);
659         if (err != ERROR_SUCCESS) {
660                 ERROR("Failed to read raw encrypted data from \"%ls\"", path);
661                 win32_error(err);
662                 ret = WIMLIB_ERR_READ;
663         } else {
664                 ret = 0;
665         }
666         CloseEncryptedFileRaw(file_ctx);
667         return ret;
668 }
669
670 /* Scans an unnamed or named stream of a Win32 file (not a reparse point
671  * stream); calculates its SHA1 message digest and either creates a `struct
672  * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
673  * wim_lookup_table_entry' for an identical stream.
674  *
675  * @path:               Path to the file (UTF-16LE).
676  *
677  * @path_num_chars:     Number of 2-byte characters in @path.
678  *
679  * @inode:              WIM inode to save the stream into.
680  *
681  * @lookup_table:       Stream lookup table for the WIM.
682  *
683  * @dat:                A `WIN32_FIND_STREAM_DATA' structure that specifies the
684  *                      stream name.
685  *
686  * Returns 0 on success; nonzero on failure.
687  */
688 static int
689 win32_capture_stream(const wchar_t *path,
690                      size_t path_num_chars,
691                      struct wim_inode *inode,
692                      struct wim_lookup_table *lookup_table,
693                      WIN32_FIND_STREAM_DATA *dat)
694 {
695         struct wim_ads_entry *ads_entry;
696         struct wim_lookup_table_entry *lte;
697         int ret;
698         wchar_t *stream_name, *colon;
699         size_t stream_name_nchars;
700         bool is_named_stream;
701         wchar_t *spath;
702         size_t spath_nchars;
703         size_t spath_buf_nbytes;
704         const wchar_t *relpath_prefix;
705         const wchar_t *colonchar;
706
707         DEBUG("Capture \"%ls\" stream \"%ls\"", path, dat->cStreamName);
708
709         /* The stream name should be returned as :NAME:TYPE */
710         stream_name = dat->cStreamName;
711         if (*stream_name != L':')
712                 goto out_invalid_stream_name;
713         stream_name += 1;
714         colon = wcschr(stream_name, L':');
715         if (colon == NULL)
716                 goto out_invalid_stream_name;
717
718         if (wcscmp(colon + 1, L"$DATA")) {
719                 /* Not a DATA stream */
720                 ret = 0;
721                 goto out;
722         }
723
724         *colon = '\0';
725
726         stream_name_nchars = colon - stream_name;
727         is_named_stream = (stream_name_nchars != 0);
728
729         if (is_named_stream) {
730                 /* Allocate an ADS entry for the named stream. */
731                 ads_entry = inode_add_ads_utf16le(inode, stream_name,
732                                                   stream_name_nchars * sizeof(wchar_t));
733                 if (!ads_entry) {
734                         ret = WIMLIB_ERR_NOMEM;
735                         goto out;
736                 }
737         }
738
739         /* If zero length stream, no lookup table entry needed. */
740         if ((u64)dat->StreamSize.QuadPart == 0) {
741                 ret = 0;
742                 goto out;
743         }
744
745         /* Create a UTF-16LE string @spath that gives the filename, then a
746          * colon, then the stream name.  Or, if it's an unnamed stream, just the
747          * filename.  It is MALLOC()'ed so that it can be saved in the
748          * wim_lookup_table_entry if needed.
749          *
750          * As yet another special case, relative paths need to be changed to
751          * begin with an explicit "./" so that, for example, a file t:ads, where
752          * :ads is the part we added, is not interpreted as a file on the t:
753          * drive. */
754         spath_nchars = path_num_chars;
755         relpath_prefix = L"";
756         colonchar = L"";
757         if (is_named_stream) {
758                 spath_nchars += 1 + stream_name_nchars;
759                 colonchar = L":";
760                 if (path_num_chars == 1 && !is_any_path_separator(path[0])) {
761                         spath_nchars += 2;
762                         static const wchar_t _relpath_prefix[] =
763                                 {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
764                         relpath_prefix = _relpath_prefix;
765                 }
766         }
767
768         spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t);
769         spath = MALLOC(spath_buf_nbytes);
770
771         swprintf(spath, L"%ls%ls%ls%ls",
772                  relpath_prefix, path, colonchar, stream_name);
773
774         /* Make a new wim_lookup_table_entry */
775         lte = new_lookup_table_entry();
776         if (!lte) {
777                 ret = WIMLIB_ERR_NOMEM;
778                 goto out_free_spath;
779         }
780         lte->file_on_disk = spath;
781         spath = NULL;
782         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED && !is_named_stream) {
783                 u64 encrypted_size;
784                 lte->resource_location = RESOURCE_WIN32_ENCRYPTED;
785                 ret = win32_get_encrypted_file_size(path, &encrypted_size);
786                 if (ret)
787                         goto out_free_spath;
788                 lte->resource_entry.original_size = encrypted_size;
789         } else {
790                 lte->resource_location = RESOURCE_IN_FILE_ON_DISK;
791                 lte->resource_entry.original_size = (u64)dat->StreamSize.QuadPart;
792         }
793
794         u32 stream_id;
795         if (is_named_stream) {
796                 stream_id = ads_entry->stream_id;
797                 ads_entry->lte = lte;
798         } else {
799                 stream_id = 0;
800                 inode->i_lte = lte;
801         }
802         lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id);
803         ret = 0;
804 out_free_spath:
805         FREE(spath);
806 out:
807         return ret;
808 out_invalid_stream_name:
809         ERROR("Invalid stream name: \"%ls:%ls\"", path, dat->cStreamName);
810         ret = WIMLIB_ERR_READ;
811         goto out;
812 }
813
814 /* Scans a Win32 file for unnamed and named data streams (not reparse point
815  * streams).
816  *
817  * @path:               Path to the file (UTF-16LE).
818  *
819  * @path_num_chars:     Number of 2-byte characters in @path.
820  *
821  * @inode:              WIM inode to save the stream into.
822  *
823  * @lookup_table:       Stream lookup table for the WIM.
824  *
825  * @file_size:          Size of unnamed data stream.  (Used only if alternate
826  *                      data streams API appears to be unavailable.)
827  *
828  * @vol_flags:          Flags that specify features of the volume being
829  *                      captured.
830  *
831  * Returns 0 on success; nonzero on failure.
832  */
833 static int
834 win32_capture_streams(const wchar_t *path,
835                       size_t path_num_chars,
836                       struct wim_inode *inode,
837                       struct wim_lookup_table *lookup_table,
838                       u64 file_size,
839                       unsigned vol_flags)
840 {
841         WIN32_FIND_STREAM_DATA dat;
842         int ret;
843         HANDLE hFind;
844         DWORD err;
845
846         DEBUG("Capturing streams from \"%ls\"", path);
847
848         if (win32func_FindFirstStreamW == NULL ||
849             !(vol_flags & FILE_NAMED_STREAMS))
850                 goto unnamed_only;
851
852         hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0);
853         if (hFind == INVALID_HANDLE_VALUE) {
854                 err = GetLastError();
855                 if (err == ERROR_CALL_NOT_IMPLEMENTED)
856                         goto unnamed_only;
857
858                 /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
859                  * points and directories */
860                 if ((inode->i_attributes &
861                     (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
862                     && err == ERROR_HANDLE_EOF)
863                 {
864                         DEBUG("ERROR_HANDLE_EOF (ok)");
865                         return 0;
866                 } else {
867                         if (err == ERROR_ACCESS_DENIED) {
868                                 WARNING("Failed to look up data streams "
869                                         "of \"%ls\": Access denied!\n%ls",
870                                         path, capture_access_denied_msg);
871                                 return 0;
872                         } else {
873                                 ERROR("Failed to look up data streams "
874                                       "of \"%ls\"", path);
875                                 win32_error(err);
876                                 return WIMLIB_ERR_READ;
877                         }
878                 }
879         }
880         do {
881                 ret = win32_capture_stream(path,
882                                            path_num_chars,
883                                            inode, lookup_table,
884                                            &dat);
885                 if (ret)
886                         goto out_find_close;
887         } while (win32func_FindNextStreamW(hFind, &dat));
888         err = GetLastError();
889         if (err != ERROR_HANDLE_EOF) {
890                 ERROR("Win32 API: Error reading data streams from \"%ls\"", path);
891                 win32_error(err);
892                 ret = WIMLIB_ERR_READ;
893         }
894 out_find_close:
895         FindClose(hFind);
896         return ret;
897 unnamed_only:
898         /* FindFirstStreamW() API is not available, or the volume does not
899          * support named streams.  Only capture the unnamed data stream. */
900         DEBUG("Only capturing unnamed data stream");
901         if (inode->i_attributes &
902              (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
903         {
904                 ret = 0;
905         } else {
906                 /* Just create our own WIN32_FIND_STREAM_DATA for an unnamed
907                  * stream to reduce the code to a call to the
908                  * already-implemented win32_capture_stream() */
909                 wcscpy(dat.cStreamName, L"::$DATA");
910                 dat.StreamSize.QuadPart = file_size;
911                 ret = win32_capture_stream(path,
912                                            path_num_chars,
913                                            inode, lookup_table,
914                                            &dat);
915         }
916         return ret;
917 }
918
919 static int
920 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
921                                   wchar_t *path,
922                                   size_t path_num_chars,
923                                   struct add_image_params *params,
924                                   struct win32_capture_state *state,
925                                   unsigned vol_flags)
926 {
927         struct wim_dentry *root = NULL;
928         struct wim_inode *inode;
929         DWORD err;
930         u64 file_size;
931         int ret;
932         u8 *rpbuf;
933         u16 rpbuflen;
934         u16 not_rpfixed;
935
936         params->progress.scan.cur_path = path;
937
938         if (exclude_path(path, path_num_chars, params->config, true)) {
939                 if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) {
940                         ERROR("Cannot exclude the root directory from capture");
941                         ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
942                         goto out;
943                 }
944                 do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED);
945                 ret = 0;
946                 goto out;
947         }
948
949 #if 0
950         if (path_num_chars >= 4 &&
951             !wmemcmp(path, L"\\\\?\\", 4) &&
952             path_num_chars + 1 - 4 > MAX_PATH &&
953             state->num_long_path_warnings < MAX_CAPTURE_LONG_PATH_WARNINGS)
954         {
955                 WARNING("Path \"%ls\" exceeds MAX_PATH", path);
956                 if (++state->num_long_path_warnings == MAX_CAPTURE_LONG_PATH_WARNINGS)
957                         WARNING("Suppressing further warnings about long paths.");
958         }
959 #endif
960
961         do_capture_progress(params, WIMLIB_SCAN_DENTRY_OK);
962
963         HANDLE hFile = win32_open_existing_file(path,
964                                                 FILE_READ_DATA | FILE_READ_ATTRIBUTES);
965         if (hFile == INVALID_HANDLE_VALUE) {
966                 err = GetLastError();
967                 ERROR("Win32 API: Failed to open \"%ls\"", path);
968                 win32_error(err);
969                 ret = WIMLIB_ERR_OPEN;
970                 goto out;
971         }
972
973         BY_HANDLE_FILE_INFORMATION file_info;
974         if (!GetFileInformationByHandle(hFile, &file_info)) {
975                 err = GetLastError();
976                 ERROR("Win32 API: Failed to get file information for \"%ls\"",
977                       path);
978                 win32_error(err);
979                 ret = WIMLIB_ERR_STAT;
980                 goto out_close_handle;
981         }
982
983         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
984                 rpbuf = alloca(REPARSE_POINT_MAX_SIZE);
985                 ret = win32_get_reparse_data(hFile, path, params,
986                                              rpbuf, &rpbuflen);
987                 if (ret < 0) {
988                         /* WIMLIB_ERR_* (inverted) */
989                         ret = -ret;
990                         goto out_close_handle;
991                 } else if (ret & RP_FIXED) {
992                         not_rpfixed = 0;
993                 } else if (ret == RP_EXCLUDED) {
994                         ret = 0;
995                         goto out_close_handle;
996                 } else {
997                         not_rpfixed = 1;
998                 }
999         }
1000
1001         /* Create a WIM dentry with an associated inode, which may be shared.
1002          *
1003          * However, we need to explicitly check for directories and files with
1004          * only 1 link and refuse to hard link them.  This is because Windows
1005          * has a bug where it can return duplicate File IDs for files and
1006          * directories on the FAT filesystem. */
1007         ret = inode_table_new_dentry(&params->inode_table,
1008                                      path_basename_with_len(path, path_num_chars),
1009                                      ((u64)file_info.nFileIndexHigh << 32) |
1010                                          (u64)file_info.nFileIndexLow,
1011                                      file_info.dwVolumeSerialNumber,
1012                                      (file_info.nNumberOfLinks <= 1 ||
1013                                         (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)),
1014                                      &root);
1015         if (ret)
1016                 goto out_close_handle;
1017
1018         ret = win32_get_short_name(root, path);
1019         if (ret)
1020                 goto out_close_handle;
1021
1022         inode = root->d_inode;
1023
1024         if (inode->i_nlink > 1) /* Shared inode; nothing more to do */
1025                 goto out_close_handle;
1026
1027         inode->i_attributes = file_info.dwFileAttributes;
1028         inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
1029         inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
1030         inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
1031         inode->i_resolved = 1;
1032
1033         params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT;
1034
1035         if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)
1036             && (vol_flags & FILE_PERSISTENT_ACLS))
1037         {
1038                 ret = win32_get_security_descriptor(root, &params->sd_set,
1039                                                     path, state,
1040                                                     params->add_flags);
1041                 if (ret)
1042                         goto out_close_handle;
1043         }
1044
1045         file_size = ((u64)file_info.nFileSizeHigh << 32) |
1046                      (u64)file_info.nFileSizeLow;
1047
1048         CloseHandle(hFile);
1049
1050         /* Capture the unnamed data stream (only should be present for regular
1051          * files) and any alternate data streams. */
1052         ret = win32_capture_streams(path,
1053                                     path_num_chars,
1054                                     inode,
1055                                     params->lookup_table,
1056                                     file_size,
1057                                     vol_flags);
1058         if (ret)
1059                 goto out;
1060
1061         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1062                 /* Reparse point: set the reparse data (which we read already)
1063                  * */
1064                 inode->i_not_rpfixed = not_rpfixed;
1065                 inode->i_reparse_tag = le32_to_cpu(*(le32*)rpbuf);
1066                 ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpbuflen - 8,
1067                                                params->lookup_table);
1068         } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
1069                 /* Directory (not a reparse point) --- recurse to children */
1070                 ret = win32_recurse_directory(root,
1071                                               path,
1072                                               path_num_chars,
1073                                               params,
1074                                               state,
1075                                               vol_flags);
1076         }
1077         goto out;
1078 out_close_handle:
1079         CloseHandle(hFile);
1080 out:
1081         if (ret == 0)
1082                 *root_ret = root;
1083         else
1084                 free_dentry_tree(root, params->lookup_table);
1085         return ret;
1086 }
1087
1088 static void
1089 win32_do_capture_warnings(const struct win32_capture_state *state,
1090                           int add_flags)
1091 {
1092         if (state->num_get_sacl_priv_notheld == 0 &&
1093             state->num_get_sd_access_denied == 0)
1094                 return;
1095
1096         WARNING("");
1097         WARNING("Built dentry tree successfully, but with the following problem(s):");
1098         if (state->num_get_sacl_priv_notheld != 0) {
1099                 WARNING("Could not capture SACL (System Access Control List)\n"
1100                         "          on %lu files or directories.",
1101                         state->num_get_sacl_priv_notheld);
1102         }
1103         if (state->num_get_sd_access_denied != 0) {
1104                 WARNING("Could not capture security descriptor at all\n"
1105                         "          on %lu files or directories.",
1106                         state->num_get_sd_access_denied);
1107         }
1108         WARNING(
1109           "Try running the program as the Administrator to make sure all the\n"
1110 "          desired metadata has been captured exactly.  However, if you\n"
1111 "          do not care about capturing security descriptors correctly, then\n"
1112 "          nothing more needs to be done%ls\n",
1113         (add_flags & WIMLIB_ADD_FLAG_NO_ACLS) ? L"." :
1114          L", although you might consider\n"
1115 "          using the --no-acls option to explicitly capture no security\n"
1116 "          descriptors.\n");
1117 }
1118
1119 #define WINDOWS_NT_MAX_PATH 32768
1120
1121 /* Win32 version of capturing a directory tree */
1122 int
1123 win32_build_dentry_tree(struct wim_dentry **root_ret,
1124                         const wchar_t *root_disk_path,
1125                         struct add_image_params *params)
1126 {
1127         size_t path_nchars;
1128         wchar_t *path;
1129         int ret;
1130         struct win32_capture_state state;
1131         unsigned vol_flags;
1132         DWORD dret;
1133         bool need_prefix_free = false;
1134
1135         if (!win32func_FindFirstStreamW) {
1136                 WARNING("Running on Windows XP or earlier; "
1137                         "alternate data streams will not be captured.");
1138         }
1139
1140         path_nchars = wcslen(root_disk_path);
1141         if (path_nchars > WINDOWS_NT_MAX_PATH)
1142                 return WIMLIB_ERR_INVALID_PARAM;
1143
1144         if (GetFileAttributesW(root_disk_path) == INVALID_FILE_ATTRIBUTES &&
1145             GetLastError() == ERROR_FILE_NOT_FOUND)
1146         {
1147                 ERROR("Capture directory \"%ls\" does not exist!",
1148                       root_disk_path);
1149                 return WIMLIB_ERR_OPENDIR;
1150         }
1151
1152         ret = win32_get_file_and_vol_ids(root_disk_path,
1153                                          &params->capture_root_ino,
1154                                          &params->capture_root_dev);
1155         if (ret)
1156                 return ret;
1157
1158         win32_get_vol_flags(root_disk_path, &vol_flags, NULL);
1159
1160         /* WARNING: There is no check for overflow later when this buffer is
1161          * being used!  But it's as long as the maximum path length understood
1162          * by Windows NT (which is NOT the same as MAX_PATH). */
1163         path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
1164         if (!path)
1165                 return WIMLIB_ERR_NOMEM;
1166
1167         /* Work around defective behavior in Windows where paths longer than 260
1168          * characters are not supported by default; instead they need to be
1169          * turned into absolute paths and prefixed with "\\?\".  */
1170
1171         if (wcsncmp(root_disk_path, L"\\\\?\\", 4)) {
1172                 dret = GetFullPathName(root_disk_path, WINDOWS_NT_MAX_PATH - 4,
1173                                        &path[4], NULL);
1174
1175                 if (dret == 0 || dret >= WINDOWS_NT_MAX_PATH - 4) {
1176                         WARNING("Can't get full path name for \"%ls\"", root_disk_path);
1177                         wmemcpy(path, root_disk_path, path_nchars + 1);
1178                 } else {
1179                         wmemcpy(path, L"\\\\?\\", 4);
1180                         path_nchars = 4 + dret;
1181                         /* Update pattern prefix */
1182                         if (params->config != NULL)
1183                         {
1184                                 params->config->_prefix = TSTRDUP(path);
1185                                 params->config->_prefix_num_tchars = path_nchars;
1186                                 if (params->config->_prefix == NULL)
1187                                 {
1188                                         ret = WIMLIB_ERR_NOMEM;
1189                                         goto out_free_path;
1190                                 }
1191                                 need_prefix_free = true;
1192                         }
1193                 }
1194         } else {
1195                 wmemcpy(path, root_disk_path, path_nchars + 1);
1196         }
1197
1198         memset(&state, 0, sizeof(state));
1199         ret = win32_build_dentry_tree_recursive(root_ret, path,
1200                                                 path_nchars, params,
1201                                                 &state, vol_flags);
1202         if (need_prefix_free)
1203                 FREE(params->config->_prefix);
1204 out_free_path:
1205         FREE(path);
1206         if (ret == 0)
1207                 win32_do_capture_warnings(&state, params->add_flags);
1208         return ret;
1209 }
1210
1211 #endif /* __WIN32__ */