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