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