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