]> wimlib.net Git - wimlib/blob - src/win32_capture.c
ntfs-3g_apply.c: Only warn when DOS names cannot be applied
[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 &&
761                     path[0] != L'/' &&
762                     path[0] != L'\\')
763                 {
764                         spath_nchars += 2;
765                         static const wchar_t _relpath_prefix[] =
766                                 {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
767                         relpath_prefix = _relpath_prefix;
768                 }
769         }
770
771         spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t);
772         spath = MALLOC(spath_buf_nbytes);
773
774         swprintf(spath, L"%ls%ls%ls%ls",
775                  relpath_prefix, path, colonchar, stream_name);
776
777         /* Make a new wim_lookup_table_entry */
778         lte = new_lookup_table_entry();
779         if (!lte) {
780                 ret = WIMLIB_ERR_NOMEM;
781                 goto out_free_spath;
782         }
783         lte->file_on_disk = spath;
784         spath = NULL;
785         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED && !is_named_stream) {
786                 u64 encrypted_size;
787                 lte->resource_location = RESOURCE_WIN32_ENCRYPTED;
788                 ret = win32_get_encrypted_file_size(path, &encrypted_size);
789                 if (ret)
790                         goto out_free_spath;
791                 lte->resource_entry.original_size = encrypted_size;
792         } else {
793                 lte->resource_location = RESOURCE_WIN32;
794                 lte->resource_entry.original_size = (u64)dat->StreamSize.QuadPart;
795         }
796
797         u32 stream_id;
798         if (is_named_stream) {
799                 stream_id = ads_entry->stream_id;
800                 ads_entry->lte = lte;
801         } else {
802                 stream_id = 0;
803                 inode->i_lte = lte;
804         }
805         lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id);
806         ret = 0;
807 out_free_spath:
808         FREE(spath);
809 out:
810         return ret;
811 out_invalid_stream_name:
812         ERROR("Invalid stream name: \"%ls:%ls\"", path, dat->cStreamName);
813         ret = WIMLIB_ERR_READ;
814         goto out;
815 }
816
817 /* Scans a Win32 file for unnamed and named data streams (not reparse point
818  * streams).
819  *
820  * @path:               Path to the file (UTF-16LE).
821  *
822  * @path_num_chars:     Number of 2-byte characters in @path.
823  *
824  * @inode:              WIM inode to save the stream into.
825  *
826  * @lookup_table:       Stream lookup table for the WIM.
827  *
828  * @file_size:          Size of unnamed data stream.  (Used only if alternate
829  *                      data streams API appears to be unavailable.)
830  *
831  * @vol_flags:          Flags that specify features of the volume being
832  *                      captured.
833  *
834  * Returns 0 on success; nonzero on failure.
835  */
836 static int
837 win32_capture_streams(const wchar_t *path,
838                       size_t path_num_chars,
839                       struct wim_inode *inode,
840                       struct wim_lookup_table *lookup_table,
841                       u64 file_size,
842                       unsigned vol_flags)
843 {
844         WIN32_FIND_STREAM_DATA dat;
845         int ret;
846         HANDLE hFind;
847         DWORD err;
848
849         DEBUG("Capturing streams from \"%ls\"", path);
850
851         if (win32func_FindFirstStreamW == NULL ||
852             !(vol_flags & FILE_NAMED_STREAMS))
853                 goto unnamed_only;
854
855         hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0);
856         if (hFind == INVALID_HANDLE_VALUE) {
857                 err = GetLastError();
858                 if (err == ERROR_CALL_NOT_IMPLEMENTED)
859                         goto unnamed_only;
860
861                 /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
862                  * points and directories */
863                 if ((inode->i_attributes &
864                     (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
865                     && err == ERROR_HANDLE_EOF)
866                 {
867                         DEBUG("ERROR_HANDLE_EOF (ok)");
868                         return 0;
869                 } else {
870                         if (err == ERROR_ACCESS_DENIED) {
871                                 WARNING("Failed to look up data streams "
872                                         "of \"%ls\": Access denied!\n%ls",
873                                         path, capture_access_denied_msg);
874                                 return 0;
875                         } else {
876                                 ERROR("Failed to look up data streams "
877                                       "of \"%ls\"", path);
878                                 win32_error(err);
879                                 return WIMLIB_ERR_READ;
880                         }
881                 }
882         }
883         do {
884                 ret = win32_capture_stream(path,
885                                            path_num_chars,
886                                            inode, lookup_table,
887                                            &dat);
888                 if (ret)
889                         goto out_find_close;
890         } while (win32func_FindNextStreamW(hFind, &dat));
891         err = GetLastError();
892         if (err != ERROR_HANDLE_EOF) {
893                 ERROR("Win32 API: Error reading data streams from \"%ls\"", path);
894                 win32_error(err);
895                 ret = WIMLIB_ERR_READ;
896         }
897 out_find_close:
898         FindClose(hFind);
899         return ret;
900 unnamed_only:
901         /* FindFirstStreamW() API is not available, or the volume does not
902          * support named streams.  Only capture the unnamed data stream. */
903         DEBUG("Only capturing unnamed data stream");
904         if (inode->i_attributes &
905              (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
906         {
907                 ret = 0;
908         } else {
909                 /* Just create our own WIN32_FIND_STREAM_DATA for an unnamed
910                  * stream to reduce the code to a call to the
911                  * already-implemented win32_capture_stream() */
912                 wcscpy(dat.cStreamName, L"::$DATA");
913                 dat.StreamSize.QuadPart = file_size;
914                 ret = win32_capture_stream(path,
915                                            path_num_chars,
916                                            inode, lookup_table,
917                                            &dat);
918         }
919         return ret;
920 }
921
922 static int
923 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
924                                   wchar_t *path,
925                                   size_t path_num_chars,
926                                   struct add_image_params *params,
927                                   struct win32_capture_state *state,
928                                   unsigned vol_flags)
929 {
930         struct wim_dentry *root = NULL;
931         struct wim_inode *inode;
932         DWORD err;
933         u64 file_size;
934         int ret;
935         u8 *rpbuf;
936         u16 rpbuflen;
937         u16 not_rpfixed;
938
939         if (exclude_path(path, path_num_chars, params->config, true)) {
940                 if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) {
941                         ERROR("Cannot exclude the root directory from capture");
942                         ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
943                         goto out;
944                 }
945                 if ((params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE)
946                     && params->progress_func)
947                 {
948                         union wimlib_progress_info info;
949                         info.scan.cur_path = path;
950                         info.scan.excluded = true;
951                         params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
952                 }
953                 ret = 0;
954                 goto out;
955         }
956
957 #if 0
958         if (path_num_chars >= 4 &&
959             !wmemcmp(path, L"\\\\?\\", 4) &&
960             path_num_chars + 1 - 4 > MAX_PATH &&
961             state->num_long_path_warnings < MAX_CAPTURE_LONG_PATH_WARNINGS)
962         {
963                 WARNING("Path \"%ls\" exceeds MAX_PATH", path);
964                 if (++state->num_long_path_warnings == MAX_CAPTURE_LONG_PATH_WARNINGS)
965                         WARNING("Suppressing further warnings about long paths.");
966         }
967 #endif
968
969         if ((params->add_flags & WIMLIB_ADD_FLAG_VERBOSE)
970             && params->progress_func)
971         {
972                 union wimlib_progress_info info;
973                 info.scan.cur_path = path;
974                 info.scan.excluded = false;
975                 params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
976         }
977
978         HANDLE hFile = win32_open_existing_file(path,
979                                                 FILE_READ_DATA | FILE_READ_ATTRIBUTES);
980         if (hFile == INVALID_HANDLE_VALUE) {
981                 err = GetLastError();
982                 ERROR("Win32 API: Failed to open \"%ls\"", path);
983                 win32_error(err);
984                 ret = WIMLIB_ERR_OPEN;
985                 goto out;
986         }
987
988         BY_HANDLE_FILE_INFORMATION file_info;
989         if (!GetFileInformationByHandle(hFile, &file_info)) {
990                 err = GetLastError();
991                 ERROR("Win32 API: Failed to get file information for \"%ls\"",
992                       path);
993                 win32_error(err);
994                 ret = WIMLIB_ERR_STAT;
995                 goto out_close_handle;
996         }
997
998         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
999                 rpbuf = alloca(REPARSE_POINT_MAX_SIZE);
1000                 ret = win32_get_reparse_data(hFile, path, params,
1001                                              rpbuf, &rpbuflen);
1002                 if (ret < 0) {
1003                         /* WIMLIB_ERR_* (inverted) */
1004                         ret = -ret;
1005                         goto out_close_handle;
1006                 } else if (ret & RP_FIXED) {
1007                         not_rpfixed = 0;
1008                 } else if (ret == RP_EXCLUDED) {
1009                         ret = 0;
1010                         goto out_close_handle;
1011                 } else {
1012                         not_rpfixed = 1;
1013                 }
1014         }
1015
1016         /* Create a WIM dentry with an associated inode, which may be shared.
1017          *
1018          * However, we need to explicitly check for directories and files with
1019          * only 1 link and refuse to hard link them.  This is because Windows
1020          * has a bug where it can return duplicate File IDs for files and
1021          * directories on the FAT filesystem. */
1022         ret = inode_table_new_dentry(&params->inode_table,
1023                                      path_basename_with_len(path, path_num_chars),
1024                                      ((u64)file_info.nFileIndexHigh << 32) |
1025                                          (u64)file_info.nFileIndexLow,
1026                                      file_info.dwVolumeSerialNumber,
1027                                      (file_info.nNumberOfLinks <= 1 ||
1028                                         (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)),
1029                                      &root);
1030         if (ret)
1031                 goto out_close_handle;
1032
1033         ret = win32_get_short_name(root, path);
1034         if (ret)
1035                 goto out_close_handle;
1036
1037         inode = root->d_inode;
1038
1039         if (inode->i_nlink > 1) /* Shared inode; nothing more to do */
1040                 goto out_close_handle;
1041
1042         inode->i_attributes = file_info.dwFileAttributes;
1043         inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
1044         inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
1045         inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
1046         inode->i_resolved = 1;
1047
1048         params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT;
1049
1050         if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)
1051             && (vol_flags & FILE_PERSISTENT_ACLS))
1052         {
1053                 ret = win32_get_security_descriptor(root, &params->sd_set,
1054                                                     path, state,
1055                                                     params->add_flags);
1056                 if (ret)
1057                         goto out_close_handle;
1058         }
1059
1060         file_size = ((u64)file_info.nFileSizeHigh << 32) |
1061                      (u64)file_info.nFileSizeLow;
1062
1063         CloseHandle(hFile);
1064
1065         /* Capture the unnamed data stream (only should be present for regular
1066          * files) and any alternate data streams. */
1067         ret = win32_capture_streams(path,
1068                                     path_num_chars,
1069                                     inode,
1070                                     params->lookup_table,
1071                                     file_size,
1072                                     vol_flags);
1073         if (ret)
1074                 goto out;
1075
1076         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1077                 /* Reparse point: set the reparse data (which we read already)
1078                  * */
1079                 inode->i_not_rpfixed = not_rpfixed;
1080                 inode->i_reparse_tag = le32_to_cpu(*(le32*)rpbuf);
1081                 ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpbuflen - 8,
1082                                                params->lookup_table);
1083         } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
1084                 /* Directory (not a reparse point) --- recurse to children */
1085                 ret = win32_recurse_directory(root,
1086                                               path,
1087                                               path_num_chars,
1088                                               params,
1089                                               state,
1090                                               vol_flags);
1091         }
1092         goto out;
1093 out_close_handle:
1094         CloseHandle(hFile);
1095 out:
1096         if (ret == 0)
1097                 *root_ret = root;
1098         else
1099                 free_dentry_tree(root, params->lookup_table);
1100         return ret;
1101 }
1102
1103 static void
1104 win32_do_capture_warnings(const struct win32_capture_state *state,
1105                           int add_flags)
1106 {
1107         if (state->num_get_sacl_priv_notheld == 0 &&
1108             state->num_get_sd_access_denied == 0)
1109                 return;
1110
1111         WARNING("");
1112         WARNING("Built dentry tree successfully, but with the following problem(s):");
1113         if (state->num_get_sacl_priv_notheld != 0) {
1114                 WARNING("Could not capture SACL (System Access Control List)\n"
1115                         "          on %lu files or directories.",
1116                         state->num_get_sacl_priv_notheld);
1117         }
1118         if (state->num_get_sd_access_denied != 0) {
1119                 WARNING("Could not capture security descriptor at all\n"
1120                         "          on %lu files or directories.",
1121                         state->num_get_sd_access_denied);
1122         }
1123         WARNING(
1124           "Try running the program as the Administrator to make sure all the\n"
1125 "          desired metadata has been captured exactly.  However, if you\n"
1126 "          do not care about capturing security descriptors correctly, then\n"
1127 "          nothing more needs to be done%ls\n",
1128         (add_flags & WIMLIB_ADD_FLAG_NO_ACLS) ? L"." :
1129          L", although you might consider\n"
1130 "          using the --no-acls option to explicitly capture no security\n"
1131 "          descriptors.\n");
1132 }
1133
1134 #define WINDOWS_NT_MAX_PATH 32768
1135
1136 /* Win32 version of capturing a directory tree */
1137 int
1138 win32_build_dentry_tree(struct wim_dentry **root_ret,
1139                         const wchar_t *root_disk_path,
1140                         struct add_image_params *params)
1141 {
1142         size_t path_nchars;
1143         wchar_t *path;
1144         int ret;
1145         struct win32_capture_state state;
1146         unsigned vol_flags;
1147         DWORD dret;
1148
1149         if (!win32func_FindFirstStreamW) {
1150                 WARNING("Running on Windows XP or earlier; "
1151                         "alternate data streams will not be captured.");
1152         }
1153
1154         path_nchars = wcslen(root_disk_path);
1155         if (path_nchars > WINDOWS_NT_MAX_PATH)
1156                 return WIMLIB_ERR_INVALID_PARAM;
1157
1158         if (GetFileAttributesW(root_disk_path) == INVALID_FILE_ATTRIBUTES &&
1159             GetLastError() == ERROR_FILE_NOT_FOUND)
1160         {
1161                 ERROR("Capture directory \"%ls\" does not exist!",
1162                       root_disk_path);
1163                 return WIMLIB_ERR_OPENDIR;
1164         }
1165
1166         ret = win32_get_file_and_vol_ids(root_disk_path,
1167                                          &params->capture_root_ino,
1168                                          &params->capture_root_dev);
1169         if (ret)
1170                 return ret;
1171
1172         win32_get_vol_flags(root_disk_path, &vol_flags);
1173
1174         /* WARNING: There is no check for overflow later when this buffer is
1175          * being used!  But it's as long as the maximum path length understood
1176          * by Windows NT (which is NOT the same as MAX_PATH). */
1177         path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
1178         if (!path)
1179                 return WIMLIB_ERR_NOMEM;
1180
1181         /* Work around defective behavior in Windows where paths longer than 260
1182          * characters are not supported by default; instead they need to be
1183          * turned into absolute paths and prefixed with "\\?\".  */
1184
1185         if (wcsncmp(root_disk_path, L"\\\\?\\", 4)) {
1186                 dret = GetFullPathName(root_disk_path, WINDOWS_NT_MAX_PATH - 4,
1187                                        &path[4], NULL);
1188
1189                 if (dret == 0 || dret >= WINDOWS_NT_MAX_PATH - 4) {
1190                         WARNING("Can't get full path name for \"%ls\"", root_disk_path);
1191                         wmemcpy(path, root_disk_path, path_nchars + 1);
1192                 } else {
1193                         wmemcpy(path, L"\\\\?\\", 4);
1194                         path_nchars = 4 + dret;
1195                 }
1196         } else {
1197                 wmemcpy(path, root_disk_path, path_nchars + 1);
1198         }
1199
1200         memset(&state, 0, sizeof(state));
1201         ret = win32_build_dentry_tree_recursive(root_ret, path,
1202                                                 path_nchars, params,
1203                                                 &state, vol_flags);
1204         FREE(path);
1205         if (ret == 0)
1206                 win32_do_capture_warnings(&state, params->add_flags);
1207         return ret;
1208 }
1209
1210 #endif /* __WIN32__ */