]> wimlib.net Git - wimlib/blob - src/win32_capture.c
Allow configurable case sensitivity
[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 *cb_ctx)
62 {
63         int ret = 0;
64         u64 bytes_remaining;
65         u8 buf[BUFFER_SIZE];
66
67         HANDLE hFile = win32_open_existing_file(lte->file_on_disk,
68                                                 FILE_READ_DATA);
69         if (hFile == INVALID_HANDLE_VALUE) {
70                 set_errno_from_GetLastError();
71                 ERROR_WITH_ERRNO("Failed to open \"%ls\"", lte->file_on_disk);
72                 return WIMLIB_ERR_OPEN;
73         }
74
75         bytes_remaining = size;
76         while (bytes_remaining) {
77                 DWORD bytesToRead, bytesRead;
78
79                 bytesToRead = min(sizeof(buf), bytes_remaining);
80                 if (!ReadFile(hFile, buf, bytesToRead, &bytesRead, NULL) ||
81                     bytesRead != bytesToRead)
82                 {
83                         set_errno_from_GetLastError();
84                         ERROR_WITH_ERRNO("Failed to read data from \"%ls\"",
85                                          lte->file_on_disk);
86                         ret = WIMLIB_ERR_READ;
87                         break;
88                 }
89                 bytes_remaining -= bytesRead;
90                 ret = (*cb)(buf, bytesRead, cb_ctx);
91                 if (ret)
92                         break;
93         }
94 out_close_handle:
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;
102         int wimlib_err_code;
103         u64 bytes_remaining;
104 };
105
106 static DWORD WINAPI
107 win32_encrypted_export_cb(unsigned char *data, void *_ctx, unsigned long len)
108 {
109         struct win32_encrypted_read_ctx *ctx = _ctx;
110         int ret;
111         size_t bytes_to_consume = min(len, ctx->bytes_remaining);
112
113         ret = (*ctx->read_prefix_cb)(data, bytes_to_consume, ctx->read_prefix_ctx);
114         if (ret) {
115                 ctx->wimlib_err_code = ret;
116                 /* Shouldn't matter what error code is returned here, as long as
117                  * it isn't ERROR_SUCCESS.  */
118                 return ERROR_READ_FAULT;
119         }
120         ctx->bytes_remaining -= bytes_to_consume;
121         return ERROR_SUCCESS;
122 }
123
124 int
125 read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
126                                  u64 size,
127                                  consume_data_callback_t cb,
128                                  void *cb_ctx)
129 {
130         struct win32_encrypted_read_ctx export_ctx;
131         DWORD err;
132         void *file_ctx;
133         int ret;
134
135         DEBUG("Reading %"PRIu64" bytes from encryted file \"%ls\"",
136               size, lte->file_on_disk);
137
138         export_ctx.read_prefix_cb = cb;
139         export_ctx.read_prefix_ctx = cb_ctx;
140         export_ctx.wimlib_err_code = 0;
141         export_ctx.bytes_remaining = size;
142
143         err = OpenEncryptedFileRaw(lte->file_on_disk, 0, &file_ctx);
144         if (err != ERROR_SUCCESS) {
145                 set_errno_from_win32_error(err);
146                 ERROR_WITH_ERRNO("Failed to open encrypted file \"%ls\" "
147                                  "for raw read", lte->file_on_disk);
148                 return WIMLIB_ERR_OPEN;
149         }
150         err = ReadEncryptedFileRaw(win32_encrypted_export_cb,
151                                    &export_ctx, file_ctx);
152         if (err != ERROR_SUCCESS) {
153                 set_errno_from_win32_error(err);
154                 ERROR_WITH_ERRNO("Failed to read encrypted file \"%ls\"",
155                                  lte->file_on_disk);
156                 ret = export_ctx.wimlib_err_code;
157                 if (ret == 0)
158                         ret = WIMLIB_ERR_READ;
159         } else if (export_ctx.bytes_remaining != 0) {
160                 ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from "
161                       "encryted file \"%ls\"",
162                       size - export_ctx.bytes_remaining, size,
163                       lte->file_on_disk);
164                 ret = WIMLIB_ERR_READ;
165         } else {
166                 ret = 0;
167         }
168         CloseEncryptedFileRaw(file_ctx);
169         return ret;
170 }
171
172
173 static u64
174 FILETIME_to_u64(const FILETIME *ft)
175 {
176         return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime;
177 }
178
179 /* Load the short name of a file into a WIM dentry.
180  *
181  * If we can't read the short filename for some reason, we just ignore the error
182  * and assume the file has no short name.  This shouldn't be an issue, since the
183  * short names are essentially obsolete anyway.
184  */
185 static int
186 win32_get_short_name(HANDLE hFile, const wchar_t *path, struct wim_dentry *dentry)
187 {
188
189         /* It's not any harder to just make the NtQueryInformationFile() system
190          * call ourselves, and it saves a dumb call to FindFirstFile() which of
191          * course has to create its own handle.  */
192 #ifdef WITH_NTDLL
193         if (func_NtQueryInformationFile) {
194                 NTSTATUS status;
195                 IO_STATUS_BLOCK io_status;
196                 u8 buf[128] _aligned_attribute(8);
197                 const FILE_NAME_INFORMATION *info;
198
199                 status = (*func_NtQueryInformationFile)(hFile, &io_status, buf, sizeof(buf),
200                                                         FileAlternateNameInformation);
201                 info = (const FILE_NAME_INFORMATION*)buf;
202                 if (status == STATUS_SUCCESS && info->FileNameLength != 0) {
203                         dentry->short_name = MALLOC(info->FileNameLength + 2);
204                         if (!dentry->short_name)
205                                 return WIMLIB_ERR_NOMEM;
206                         memcpy(dentry->short_name, info->FileName,
207                                info->FileNameLength);
208                         dentry->short_name[info->FileNameLength / 2] = L'\0';
209                         dentry->short_name_nbytes = info->FileNameLength;
210                 }
211                 return 0;
212         }
213 #endif
214
215         WIN32_FIND_DATAW dat;
216         HANDLE hFind;
217         int ret = 0;
218
219         hFind = FindFirstFile(path, &dat);
220         if (hFind != INVALID_HANDLE_VALUE) {
221                 if (dat.cAlternateFileName[0] != L'\0') {
222                         DEBUG("\"%ls\": short name \"%ls\"", path, dat.cAlternateFileName);
223                         size_t short_name_nbytes = wcslen(dat.cAlternateFileName) *
224                                                    sizeof(wchar_t);
225                         size_t n = short_name_nbytes + sizeof(wchar_t);
226                         dentry->short_name = MALLOC(n);
227                         if (dentry->short_name) {
228                                 memcpy(dentry->short_name, dat.cAlternateFileName, n);
229                                 dentry->short_name_nbytes = short_name_nbytes;
230                         } else {
231                                 ret = WIMLIB_ERR_NOMEM;
232                         }
233                 }
234                 FindClose(hFind);
235         }
236         return ret;
237 }
238
239 /*
240  * win32_query_security_descriptor() - Query a file's security descriptor
241  *
242  * We need the file's security descriptor in SECURITY_DESCRIPTOR_RELATIVE
243  * format, and we currently have a handle opened with as many relevant
244  * permissions as possible.  At this point, on Windows there are a number of
245  * options for reading a file's security descriptor:
246  *
247  * GetFileSecurity():  This takes in a path and returns the
248  * SECURITY_DESCRIPTOR_RELATIVE.  Problem: this uses an internal handle, not
249  * ours, and the handle created internally doesn't specify
250  * FILE_FLAG_BACKUP_SEMANTICS.  Therefore there can be access denied errors on
251  * some files and directories, even when running as the Administrator.
252  *
253  * GetSecurityInfo():  This takes in a handle and returns the security
254  * descriptor split into a bunch of different parts.  This should work, but it's
255  * dumb because we have to put the security descriptor back together again.
256  *
257  * BackupRead():  This can read the security descriptor, but this is a
258  * difficult-to-use API, probably only works as the Administrator, and the
259  * format of the returned data is not well documented.
260  *
261  * NtQuerySecurityObject():  This is exactly what we need, as it takes in a
262  * handle and returns the security descriptor in SECURITY_DESCRIPTOR_RELATIVE
263  * format.  Only problem is that it's a ntdll function and therefore not
264  * officially part of the Win32 API.  Oh well.
265  */
266 static DWORD
267 win32_query_security_descriptor(HANDLE hFile, const wchar_t *path,
268                                 SECURITY_INFORMATION requestedInformation,
269                                 SECURITY_DESCRIPTOR *buf,
270                                 DWORD bufsize, DWORD *lengthNeeded)
271 {
272 #ifdef WITH_NTDLL
273         if (func_NtQuerySecurityObject) {
274                 NTSTATUS status;
275
276                 status = (*func_NtQuerySecurityObject)(hFile,
277                                                        requestedInformation, buf,
278                                                        bufsize, lengthNeeded);
279                 /* Since it queries an already-open handle, NtQuerySecurityObject()
280                  * apparently returns STATUS_ACCESS_DENIED rather than
281                  * STATUS_PRIVILEGE_NOT_HELD.  */
282                 if (status == STATUS_ACCESS_DENIED)
283                         return ERROR_PRIVILEGE_NOT_HELD;
284                 else
285                         return (*func_RtlNtStatusToDosError)(status);
286         }
287 #endif
288         if (GetFileSecurity(path, requestedInformation, buf,
289                             bufsize, lengthNeeded))
290                 return ERROR_SUCCESS;
291         else
292                 return GetLastError();
293 }
294
295 static int
296 win32_get_security_descriptor(HANDLE hFile,
297                               const wchar_t *path,
298                               struct wim_inode *inode,
299                               struct wim_sd_set *sd_set,
300                               struct win32_capture_state *state,
301                               int add_flags)
302 {
303         SECURITY_INFORMATION requestedInformation;
304         u8 _buf[4096];
305         u8 *buf;
306         size_t bufsize;
307         DWORD lenNeeded;
308         DWORD err;
309         int ret;
310
311         requestedInformation = DACL_SECURITY_INFORMATION |
312                                SACL_SECURITY_INFORMATION |
313                                OWNER_SECURITY_INFORMATION |
314                                GROUP_SECURITY_INFORMATION;
315         buf = _buf;
316         bufsize = sizeof(_buf);
317         for (;;) {
318                 err = win32_query_security_descriptor(hFile, path,
319                                                       requestedInformation,
320                                                       (SECURITY_DESCRIPTOR*)buf,
321                                                       bufsize, &lenNeeded);
322                 switch (err) {
323                 case ERROR_SUCCESS:
324                         goto have_descriptor;
325                 case ERROR_INSUFFICIENT_BUFFER:
326                         wimlib_assert(buf == _buf);
327                         buf = MALLOC(lenNeeded);
328                         if (!buf)
329                                 return WIMLIB_ERR_NOMEM;
330                         bufsize = lenNeeded;
331                         break;
332                 case ERROR_PRIVILEGE_NOT_HELD:
333                         if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS)
334                                 goto fail;
335                         if (requestedInformation & SACL_SECURITY_INFORMATION) {
336                                 state->num_get_sacl_priv_notheld++;
337                                 requestedInformation &= ~SACL_SECURITY_INFORMATION;
338                                 break;
339                         }
340                         /* Fall through */
341                 case ERROR_ACCESS_DENIED:
342                         if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS)
343                                 goto fail;
344                         state->num_get_sd_access_denied++;
345                         ret = 0;
346                         goto out_free_buf;
347                 default:
348                 fail:
349                         set_errno_from_win32_error(err);
350                         ERROR_WITH_ERRNO("Failed to read security descriptor of \"%ls\"", path);
351                         ret = WIMLIB_ERR_READ;
352                         goto out_free_buf;
353                 }
354         }
355
356 have_descriptor:
357         inode->i_security_id = sd_set_add_sd(sd_set, buf, lenNeeded);
358         if (inode->i_security_id < 0)
359                 ret = WIMLIB_ERR_NOMEM;
360         else
361                 ret = 0;
362 out_free_buf:
363         if (buf != _buf)
364                 FREE(buf);
365         return ret;
366 }
367
368 static int
369 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
370                                   wchar_t *path,
371                                   size_t path_num_chars,
372                                   struct add_image_params *params,
373                                   struct win32_capture_state *state,
374                                   unsigned vol_flags);
375
376 /* Reads the directory entries of directory and recursively calls
377  * win32_build_dentry_tree() on them.  */
378 static int
379 win32_recurse_directory(HANDLE hDir,
380                         wchar_t *dir_path,
381                         size_t dir_path_num_chars,
382                         struct wim_dentry *root,
383                         struct add_image_params *params,
384                         struct win32_capture_state *state,
385                         unsigned vol_flags)
386 {
387         int ret;
388
389         DEBUG("Recurse to directory \"%ls\"", dir_path);
390
391         /* Using NtQueryDirectoryFile() we can re-use the same open handle,
392          * which we opened with FILE_FLAG_BACKUP_SEMANTICS (probably not the
393          * case for the FindFirstFile() API; it's not documented).  */
394 #ifdef WITH_NTDLL
395         if (!func_NtQueryDirectoryFile)
396                 goto use_FindFirstFile;
397
398         NTSTATUS status;
399         IO_STATUS_BLOCK io_status;
400         const size_t bufsize = 8192;
401         u8 *buf;
402         BOOL restartScan = TRUE;
403         const FILE_NAMES_INFORMATION *info;
404
405         buf = MALLOC(bufsize);
406         if (!buf)
407                 return WIMLIB_ERR_NOMEM;
408         for (;;) {
409                 status = (*func_NtQueryDirectoryFile)(hDir, NULL, NULL, NULL,
410                                                       &io_status, buf, bufsize,
411                                                       FileNamesInformation,
412                                                       FALSE, NULL, restartScan);
413                 restartScan = FALSE;
414                 if (status != STATUS_SUCCESS) {
415                         if (status == STATUS_NO_MORE_FILES ||
416                             status == STATUS_NO_MORE_ENTRIES ||
417                             status == STATUS_NO_MORE_MATCHES) {
418                                 ret = 0;
419                         } else if (status == STATUS_NOT_IMPLEMENTED ||
420                                    status == STATUS_NOT_SUPPORTED ||
421                                    status == STATUS_INVALID_INFO_CLASS) {
422                                 FREE(buf);
423                                 goto use_FindFirstFile;
424                         } else {
425                                 set_errno_from_nt_status(status);
426                                 ERROR_WITH_ERRNO("Failed to read directory "
427                                                  "\"%ls\"", dir_path);
428                                 ret = WIMLIB_ERR_READ;
429                         }
430                         goto out_free_buf;
431                 }
432                 wimlib_assert(io_status.Information != 0);
433                 info = (const FILE_NAMES_INFORMATION*)buf;
434                 for (;;) {
435                         if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') &&
436                             !(info->FileNameLength == 4 && info->FileName[0] == L'.' &&
437                                                            info->FileName[1] == L'.'))
438                         {
439                                 wchar_t *p;
440                                 struct wim_dentry *child;
441
442                                 p = dir_path + dir_path_num_chars;
443                                 *p++ = L'\\';
444                                 p = wmempcpy(p, info->FileName,
445                                              info->FileNameLength / 2);
446                                 *p = '\0';
447
448                                 ret = win32_build_dentry_tree_recursive(
449                                                                 &child,
450                                                                 dir_path,
451                                                                 p - dir_path,
452                                                                 params,
453                                                                 state,
454                                                                 vol_flags);
455
456                                 dir_path[dir_path_num_chars] = L'\0';
457
458                                 if (ret)
459                                         goto out_free_buf;
460                                 if (child)
461                                         dentry_add_child(root, child);
462                         }
463                         if (info->NextEntryOffset == 0)
464                                 break;
465                         info = (const FILE_NAMES_INFORMATION*)
466                                         ((const u8*)info + info->NextEntryOffset);
467                 }
468         }
469 out_free_buf:
470         FREE(buf);
471         return ret;
472 #endif
473
474 use_FindFirstFile:
475         ;
476         WIN32_FIND_DATAW dat;
477         HANDLE hFind;
478         DWORD err;
479
480         /* Begin reading the directory by calling FindFirstFileW.  Unlike UNIX
481          * opendir(), FindFirstFileW has file globbing built into it.  But this
482          * isn't what we actually want, so just add a dummy glob to get all
483          * entries. */
484         dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR;
485         dir_path[dir_path_num_chars + 1] = L'*';
486         dir_path[dir_path_num_chars + 2] = L'\0';
487         hFind = FindFirstFile(dir_path, &dat);
488         dir_path[dir_path_num_chars] = L'\0';
489
490         if (hFind == INVALID_HANDLE_VALUE) {
491                 err = GetLastError();
492                 if (err == ERROR_FILE_NOT_FOUND) {
493                         return 0;
494                 } else {
495                         set_errno_from_win32_error(err);
496                         ERROR_WITH_ERRNO("Failed to read directory \"%ls\"",
497                                          dir_path);
498                         return WIMLIB_ERR_READ;
499                 }
500         }
501         ret = 0;
502         do {
503                 /* Skip . and .. entries */
504                 if (dat.cFileName[0] == L'.' &&
505                     (dat.cFileName[1] == L'\0' ||
506                      (dat.cFileName[1] == L'.' &&
507                       dat.cFileName[2] == L'\0')))
508                         continue;
509                 size_t filename_len = wcslen(dat.cFileName);
510
511                 dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR;
512                 wmemcpy(dir_path + dir_path_num_chars + 1,
513                         dat.cFileName,
514                         filename_len + 1);
515
516                 struct wim_dentry *child;
517                 size_t path_len = dir_path_num_chars + 1 + filename_len;
518                 ret = win32_build_dentry_tree_recursive(&child,
519                                                         dir_path,
520                                                         path_len,
521                                                         params,
522                                                         state,
523                                                         vol_flags);
524                 dir_path[dir_path_num_chars] = L'\0';
525                 if (ret)
526                         goto out_find_close;
527                 if (child)
528                         dentry_add_child(root, child);
529         } while (FindNextFile(hFind, &dat));
530         err = GetLastError();
531         if (err != ERROR_NO_MORE_FILES) {
532                 set_errno_from_win32_error(err);
533                 ERROR_WITH_ERRNO("Failed to read directory \"%ls\"", dir_path);
534                 if (ret == 0)
535                         ret = WIMLIB_ERR_READ;
536         }
537 out_find_close:
538         FindClose(hFind);
539         return ret;
540 }
541
542 /* Reparse point fixup status code */
543 enum rp_status {
544         /* Reparse point corresponded to an absolute symbolic link or junction
545          * point that pointed outside the directory tree being captured, and
546          * therefore was excluded. */
547         RP_EXCLUDED       = 0x0,
548
549         /* Reparse point was not fixed as it was either a relative symbolic
550          * link, a mount point, or something else we could not understand. */
551         RP_NOT_FIXED      = 0x1,
552
553         /* Reparse point corresponded to an absolute symbolic link or junction
554          * point that pointed inside the directory tree being captured, where
555          * the target was specified by a "full" \??\ prefixed path, and
556          * therefore was fixed to be relative to the root of the directory tree
557          * being captured. */
558         RP_FIXED_FULLPATH = 0x2,
559
560         /* Same as RP_FIXED_FULLPATH, except the absolute link target did not
561          * have the \??\ prefix.  It may have begun with a drive letter though.
562          * */
563         RP_FIXED_ABSPATH  = 0x4,
564
565         /* Either RP_FIXED_FULLPATH or RP_FIXED_ABSPATH. */
566         RP_FIXED          = RP_FIXED_FULLPATH | RP_FIXED_ABSPATH,
567 };
568
569 /* Given the "substitute name" target of a Windows reparse point, try doing a
570  * fixup where we change it to be absolute relative to the root of the directory
571  * tree being captured.
572  *
573  * Note that this is only executed when WIMLIB_ADD_FLAG_RPFIX has been
574  * set.
575  *
576  * @capture_root_ino and @capture_root_dev indicate the inode number and device
577  * of the root of the directory tree being captured.  They are meant to identify
578  * this directory (as an alternative to its actual path, which could potentially
579  * be reached via multiple destinations due to other symbolic links).  This may
580  * not work properly on FAT, which doesn't seem to supply proper inode numbers
581  * or file IDs.  However, FAT doesn't support reparse points so this function
582  * wouldn't even be called anyway.
583  */
584 static enum rp_status
585 win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
586                                  u64 capture_root_ino, u64 capture_root_dev,
587                                  u32 rptag)
588 {
589         u16 target_nchars = *target_nbytes_p / 2;
590         size_t stripped_chars;
591         wchar_t *orig_target;
592         int ret;
593
594         ret = parse_substitute_name(target, *target_nbytes_p, rptag);
595         if (ret < 0)
596                 return RP_NOT_FIXED;
597         stripped_chars = ret;
598         if (stripped_chars)
599                 stripped_chars -= 2;
600         target[target_nchars] = L'\0';
601         orig_target = target;
602         target = capture_fixup_absolute_symlink(target + stripped_chars,
603                                                 capture_root_ino, capture_root_dev);
604         if (!target)
605                 return RP_EXCLUDED;
606         target_nchars = wcslen(target);
607         wmemmove(orig_target + stripped_chars, target, target_nchars + 1);
608         *target_nbytes_p = (target_nchars + stripped_chars) * sizeof(wchar_t);
609         DEBUG("Fixed reparse point (new target: \"%ls\")", orig_target);
610         if (stripped_chars)
611                 return RP_FIXED_FULLPATH;
612         else
613                 return RP_FIXED_ABSPATH;
614 }
615
616 /* Returns: `enum rp_status' value on success; negative WIMLIB_ERR_* value on
617  * failure. */
618 static int
619 win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
620                         u64 capture_root_ino, u64 capture_root_dev,
621                         const wchar_t *path)
622 {
623         struct reparse_data rpdata;
624         int ret;
625         enum rp_status rp_status;
626
627         ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata);
628         if (ret)
629                 return -ret;
630
631         rp_status = win32_capture_maybe_rpfix_target(rpdata.substitute_name,
632                                                      &rpdata.substitute_name_nbytes,
633                                                      capture_root_ino,
634                                                      capture_root_dev,
635                                                      le32_to_cpu(*(le32*)rpbuf));
636         if (rp_status & RP_FIXED) {
637                 wimlib_assert(rpdata.substitute_name_nbytes % 2 == 0);
638                 utf16lechar substitute_name_copy[rpdata.substitute_name_nbytes / 2];
639                 wmemcpy(substitute_name_copy, rpdata.substitute_name,
640                         rpdata.substitute_name_nbytes / 2);
641                 rpdata.substitute_name = substitute_name_copy;
642                 rpdata.print_name = substitute_name_copy;
643                 rpdata.print_name_nbytes = rpdata.substitute_name_nbytes;
644                 if (rp_status == RP_FIXED_FULLPATH) {
645                         /* "full path", meaning \??\ prefixed.  We should not
646                          * include this prefix in the print name, as it is
647                          * apparently meant for the filesystem driver only. */
648                         rpdata.print_name += 4;
649                         rpdata.print_name_nbytes -= 8;
650                 }
651                 ret = make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
652                 if (ret == 0)
653                         ret = rp_status;
654                 else
655                         ret = -ret;
656         } else {
657                 if (rp_status == RP_EXCLUDED) {
658                         size_t print_name_nchars = rpdata.print_name_nbytes / 2;
659                         wchar_t print_name0[print_name_nchars + 1];
660                         print_name0[print_name_nchars] = L'\0';
661                         wmemcpy(print_name0, rpdata.print_name, print_name_nchars);
662                         WARNING("Ignoring %ls pointing out of capture directory:\n"
663                                 "          \"%ls\" -> \"%ls\"\n"
664                                 "          (Use --norpfix to capture all symbolic links "
665                                 "and junction points as-is)",
666                                 (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK) ?
667                                         L"absolute symbolic link" : L"junction point",
668                                 path, print_name0);
669                 }
670                 ret = rp_status;
671         }
672         return ret;
673 }
674
675 /*
676  * Loads the reparse point data from a reparse point into memory, optionally
677  * fixing the targets of absolute symbolic links and junction points to be
678  * relative to the root of capture.
679  *
680  * @hFile:  Open handle to the reparse point.
681  * @path:   Path to the reparse point.  Used for error messages only.
682  * @params: Additional parameters, including whether to do reparse point fixups
683  *          or not.
684  * @rpbuf:  Buffer of length at least REPARSE_POINT_MAX_SIZE bytes into which
685  *          the reparse point buffer will be loaded.
686  * @rpbuflen_ret:  On success, the length of the reparse point buffer in bytes
687  *                 is written to this location.
688  *
689  * Returns:
690  *      On success, returns an `enum rp_status' value that indicates if and/or
691  *      how the reparse point fixup was done.
692  *
693  *      On failure, returns a negative value that is a negated WIMLIB_ERR_*
694  *      code.
695  */
696 static int
697 win32_get_reparse_data(HANDLE hFile, const wchar_t *path,
698                        struct add_image_params *params,
699                        u8 *rpbuf, u16 *rpbuflen_ret)
700 {
701         DWORD bytesReturned;
702         u32 reparse_tag;
703         int ret;
704         u16 rpbuflen;
705
706         DEBUG("Loading reparse data from \"%ls\"", path);
707         if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT,
708                              NULL, /* "Not used with this operation; set to NULL" */
709                              0, /* "Not used with this operation; set to 0" */
710                              rpbuf, /* "A pointer to a buffer that
711                                                    receives the reparse point data */
712                              REPARSE_POINT_MAX_SIZE, /* "The size of the output
713                                                         buffer, in bytes */
714                              &bytesReturned,
715                              NULL))
716         {
717                 set_errno_from_GetLastError();
718                 ERROR_WITH_ERRNO("Failed to get reparse data of \"%ls\"", path);
719                 return -WIMLIB_ERR_READ;
720         }
721         if (bytesReturned < 8 || bytesReturned > REPARSE_POINT_MAX_SIZE) {
722                 ERROR("Reparse data on \"%ls\" is invalid", path);
723                 return -WIMLIB_ERR_INVALID_REPARSE_DATA;
724         }
725
726         rpbuflen = bytesReturned;
727         reparse_tag = le32_to_cpu(*(le32*)rpbuf);
728         if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
729             (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
730              reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
731         {
732                 /* Try doing reparse point fixup */
733                 ret = win32_capture_try_rpfix(rpbuf,
734                                               &rpbuflen,
735                                               params->capture_root_ino,
736                                               params->capture_root_dev,
737                                               path);
738         } else {
739                 ret = RP_NOT_FIXED;
740         }
741         *rpbuflen_ret = rpbuflen;
742         return ret;
743 }
744
745 static DWORD WINAPI
746 win32_tally_encrypted_size_cb(unsigned char *_data, void *_size_ret,
747                               unsigned long len)
748 {
749         *(u64*)_size_ret += len;
750         return ERROR_SUCCESS;
751 }
752
753 static int
754 win32_get_encrypted_file_size(const wchar_t *path, u64 *size_ret)
755 {
756         DWORD err;
757         void *file_ctx;
758         int ret;
759
760         err = OpenEncryptedFileRaw(path, 0, &file_ctx);
761         if (err != ERROR_SUCCESS) {
762                 set_errno_from_win32_error(err);
763                 ERROR_WITH_ERRNO("Failed to open encrypted file \"%ls\" "
764                                  "for raw read", path);
765                 return WIMLIB_ERR_OPEN;
766         }
767         *size_ret = 0;
768         err = ReadEncryptedFileRaw(win32_tally_encrypted_size_cb,
769                                    size_ret, file_ctx);
770         if (err != ERROR_SUCCESS) {
771                 set_errno_from_win32_error(err);
772                 ERROR_WITH_ERRNO("Failed to read raw encrypted data from "
773                                  "\"%ls\"", path);
774                 ret = WIMLIB_ERR_READ;
775         } else {
776                 ret = 0;
777         }
778         CloseEncryptedFileRaw(file_ctx);
779         return ret;
780 }
781
782 /* Scans an unnamed or named stream of a Win32 file (not a reparse point
783  * stream); calculates its SHA1 message digest and either creates a `struct
784  * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
785  * wim_lookup_table_entry' for an identical stream.
786  *
787  * @path:               Path to the file (UTF-16LE).
788  *
789  * @path_num_chars:     Number of 2-byte characters in @path.
790  *
791  * @inode:              WIM inode to save the stream into.
792  *
793  * @lookup_table:       Stream lookup table for the WIM.
794  *
795  * @dat:                A `WIN32_FIND_STREAM_DATA' structure that specifies the
796  *                      stream name.
797  *
798  * Returns 0 on success; nonzero on failure.
799  */
800 static int
801 win32_capture_stream(const wchar_t *path,
802                      size_t path_num_chars,
803                      struct wim_inode *inode,
804                      struct wim_lookup_table *lookup_table,
805                      WIN32_FIND_STREAM_DATA *dat)
806 {
807         struct wim_ads_entry *ads_entry;
808         struct wim_lookup_table_entry *lte;
809         int ret;
810         wchar_t *stream_name, *colon;
811         size_t stream_name_nchars;
812         bool is_named_stream;
813         wchar_t *spath;
814         size_t spath_nchars;
815         size_t spath_buf_nbytes;
816         const wchar_t *relpath_prefix;
817         const wchar_t *colonchar;
818
819         DEBUG("Capture \"%ls\" stream \"%ls\"", path, dat->cStreamName);
820
821         /* The stream name should be returned as :NAME:TYPE */
822         stream_name = dat->cStreamName;
823         if (*stream_name != L':')
824                 goto out_invalid_stream_name;
825         stream_name += 1;
826         colon = wcschr(stream_name, L':');
827         if (colon == NULL)
828                 goto out_invalid_stream_name;
829
830         if (wcscmp(colon + 1, L"$DATA")) {
831                 /* Not a DATA stream */
832                 ret = 0;
833                 goto out;
834         }
835
836         *colon = '\0';
837
838         stream_name_nchars = colon - stream_name;
839         is_named_stream = (stream_name_nchars != 0);
840
841         if (is_named_stream) {
842                 /* Allocate an ADS entry for the named stream. */
843                 ads_entry = inode_add_ads_utf16le(inode, stream_name,
844                                                   stream_name_nchars * sizeof(wchar_t));
845                 if (!ads_entry) {
846                         ret = WIMLIB_ERR_NOMEM;
847                         goto out;
848                 }
849         }
850
851         /* If zero length stream, no lookup table entry needed. */
852         if ((u64)dat->StreamSize.QuadPart == 0) {
853                 ret = 0;
854                 goto out;
855         }
856
857         /* Create a UTF-16LE string @spath that gives the filename, then a
858          * colon, then the stream name.  Or, if it's an unnamed stream, just the
859          * filename.  It is MALLOC()'ed so that it can be saved in the
860          * wim_lookup_table_entry if needed.
861          *
862          * As yet another special case, relative paths need to be changed to
863          * begin with an explicit "./" so that, for example, a file t:ads, where
864          * :ads is the part we added, is not interpreted as a file on the t:
865          * drive. */
866         spath_nchars = path_num_chars;
867         relpath_prefix = L"";
868         colonchar = L"";
869         if (is_named_stream) {
870                 spath_nchars += 1 + stream_name_nchars;
871                 colonchar = L":";
872                 if (path_num_chars == 1 && !is_any_path_separator(path[0])) {
873                         spath_nchars += 2;
874                         static const wchar_t _relpath_prefix[] =
875                                 {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
876                         relpath_prefix = _relpath_prefix;
877                 }
878         }
879
880         spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t);
881         spath = MALLOC(spath_buf_nbytes);
882
883         tsprintf(spath, L"%ls%ls%ls%ls",
884                  relpath_prefix, path, colonchar, stream_name);
885
886         /* Make a new wim_lookup_table_entry */
887         lte = new_lookup_table_entry();
888         if (!lte) {
889                 ret = WIMLIB_ERR_NOMEM;
890                 goto out_free_spath;
891         }
892         lte->file_on_disk = spath;
893         spath = NULL;
894         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED && !is_named_stream) {
895                 u64 encrypted_size;
896                 lte->resource_location = RESOURCE_WIN32_ENCRYPTED;
897                 ret = win32_get_encrypted_file_size(path, &encrypted_size);
898                 if (ret)
899                         goto out_free_spath;
900                 lte->size = encrypted_size;
901         } else {
902                 lte->resource_location = RESOURCE_IN_FILE_ON_DISK;
903                 lte->size = (u64)dat->StreamSize.QuadPart;
904         }
905
906         u32 stream_id;
907         if (is_named_stream) {
908                 stream_id = ads_entry->stream_id;
909                 ads_entry->lte = lte;
910         } else {
911                 stream_id = 0;
912                 inode->i_lte = lte;
913         }
914         lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id);
915         ret = 0;
916 out_free_spath:
917         FREE(spath);
918 out:
919         return ret;
920 out_invalid_stream_name:
921         ERROR("Invalid stream name: \"%ls:%ls\"", path, dat->cStreamName);
922         ret = WIMLIB_ERR_READ;
923         goto out;
924 }
925
926 /* Load information about the streams of an open file into a WIM inode.
927  *
928  * By default, we use the NtQueryInformationFile() system call instead of
929  * FindFirstStream() and FindNextStream().  This is done for two reasons:
930  *
931  * - FindFirstStream() opens its own handle to the file or directory and
932  *   apparently does so without specifying FILE_FLAG_BACKUP_SEMANTICS, thereby
933  *   causing access denied errors on certain files (even when running as the
934  *   Administrator).
935  * - FindFirstStream() and FindNextStream() is only available on Windows Vista
936  *   and later, whereas the stream support in NtQueryInformationFile() was
937  *   already present in Windows XP.
938  */
939 static int
940 win32_capture_streams(HANDLE *hFile_p,
941                       const wchar_t *path,
942                       size_t path_num_chars,
943                       struct wim_inode *inode,
944                       struct wim_lookup_table *lookup_table,
945                       u64 file_size,
946                       unsigned vol_flags)
947 {
948         WIN32_FIND_STREAM_DATA dat;
949         int ret;
950 #ifdef WITH_NTDLL
951         u8 _buf[8192] _aligned_attribute(8);
952         u8 *buf;
953         size_t bufsize;
954         IO_STATUS_BLOCK io_status;
955         NTSTATUS status;
956         const FILE_STREAM_INFORMATION *info;
957 #endif
958         HANDLE hFind;
959         DWORD err;
960
961         DEBUG("Capturing streams from \"%ls\"", path);
962
963         if (!(vol_flags & FILE_NAMED_STREAMS))
964                 goto unnamed_only;
965
966 #ifdef WITH_NTDLL
967         if (!func_NtQueryInformationFile)
968                 goto use_FindFirstStream;
969
970         buf = _buf;
971         bufsize = sizeof(_buf);
972
973         /* Get a buffer containing the stream information.  */
974         for (;;) {
975                 status = (*func_NtQueryInformationFile)(*hFile_p, &io_status,
976                                                         buf, bufsize,
977                                                         FileStreamInformation);
978                 if (status == STATUS_SUCCESS) {
979                         break;
980                 } else if (status == STATUS_BUFFER_OVERFLOW) {
981                         u8 *newbuf;
982
983                         bufsize *= 2;
984                         if (buf == _buf)
985                                 newbuf = MALLOC(bufsize);
986                         else
987                                 newbuf = REALLOC(buf, bufsize);
988
989                         if (!newbuf) {
990                                 ret = WIMLIB_ERR_NOMEM;
991                                 goto out_free_buf;
992                         }
993                         buf = newbuf;
994                 } else if (status == STATUS_NOT_IMPLEMENTED ||
995                            status == STATUS_NOT_SUPPORTED ||
996                            status == STATUS_INVALID_INFO_CLASS) {
997                         goto use_FindFirstStream;
998                 } else {
999                         set_errno_from_nt_status(status);
1000                         ERROR_WITH_ERRNO("Failed to read streams of %ls", path);
1001                         ret = WIMLIB_ERR_READ;
1002                         goto out_free_buf;
1003                 }
1004         }
1005
1006         if (io_status.Information == 0) {
1007                 /* No stream information.  */
1008                 ret = 0;
1009                 goto out_free_buf;
1010         }
1011
1012         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
1013                 /* OpenEncryptedFileRaw() seems to fail with
1014                  * ERROR_SHARING_VIOLATION if there are any handles opened to
1015                  * the file.  */
1016                 CloseHandle(*hFile_p);
1017                 *hFile_p = INVALID_HANDLE_VALUE;
1018         }
1019
1020         /* Parse one or more stream information structures.  */
1021         info = (const FILE_STREAM_INFORMATION*)buf;
1022         for (;;) {
1023                 if (info->StreamNameLength <= sizeof(dat.cStreamName) - 2) {
1024                         dat.StreamSize = info->StreamSize;
1025                         memcpy(dat.cStreamName, info->StreamName, info->StreamNameLength);
1026                         dat.cStreamName[info->StreamNameLength / 2] = L'\0';
1027
1028                         /* Capture the stream.  */
1029                         ret = win32_capture_stream(path, path_num_chars, inode,
1030                                                    lookup_table, &dat);
1031                         if (ret)
1032                                 goto out_free_buf;
1033                 }
1034                 if (info->NextEntryOffset == 0) {
1035                         /* No more stream information.  */
1036                         ret = 0;
1037                         break;
1038                 }
1039                 /* Advance to next stream information.  */
1040                 info = (const FILE_STREAM_INFORMATION*)
1041                                 ((const u8*)info + info->NextEntryOffset);
1042         }
1043 out_free_buf:
1044         /* Free buffer if allocated on heap.  */
1045         if (buf != _buf)
1046                 FREE(buf);
1047         return ret;
1048 #endif /* WITH_NTDLL */
1049
1050 use_FindFirstStream:
1051         if (win32func_FindFirstStreamW == NULL)
1052                 goto unnamed_only;
1053         hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0);
1054         if (hFind == INVALID_HANDLE_VALUE) {
1055                 err = GetLastError();
1056                 if (err == ERROR_CALL_NOT_IMPLEMENTED ||
1057                     err == ERROR_NOT_SUPPORTED ||
1058                     err == ERROR_INVALID_FUNCTION ||
1059                     err == ERROR_INVALID_PARAMETER)
1060                         goto unnamed_only;
1061
1062                 /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
1063                  * points and directories */
1064                 if ((inode->i_attributes &
1065                     (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
1066                     && err == ERROR_HANDLE_EOF)
1067                 {
1068                         DEBUG("ERROR_HANDLE_EOF (ok)");
1069                         return 0;
1070                 } else {
1071                         if (err == ERROR_ACCESS_DENIED) {
1072                                 WARNING("Failed to look up data streams "
1073                                         "of \"%ls\": Access denied!\n%ls",
1074                                         path, capture_access_denied_msg);
1075                                 return 0;
1076                         } else {
1077                                 set_errno_from_win32_error(err);
1078                                 ERROR_WITH_ERRNO("Failed to look up data streams "
1079                                                  "of \"%ls\"", path);
1080                                 return WIMLIB_ERR_READ;
1081                         }
1082                 }
1083         }
1084         do {
1085                 ret = win32_capture_stream(path,
1086                                            path_num_chars,
1087                                            inode, lookup_table,
1088                                            &dat);
1089                 if (ret)
1090                         goto out_find_close;
1091         } while (win32func_FindNextStreamW(hFind, &dat));
1092         err = GetLastError();
1093         if (err != ERROR_HANDLE_EOF) {
1094                 set_errno_from_win32_error(err);
1095                 ERROR_WITH_ERRNO("Error reading data streams from "
1096                                  "\"%ls\"", path);
1097                 ret = WIMLIB_ERR_READ;
1098         }
1099 out_find_close:
1100         FindClose(hFind);
1101         return ret;
1102
1103 unnamed_only:
1104         /* FindFirstStream() API is not available, or the volume does not
1105          * support named streams.  Only capture the unnamed data stream. */
1106         DEBUG("Only capturing unnamed data stream");
1107         if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
1108                                    FILE_ATTRIBUTE_REPARSE_POINT))
1109                 return 0;
1110
1111         wcscpy(dat.cStreamName, L"::$DATA");
1112         dat.StreamSize.QuadPart = file_size;
1113         return win32_capture_stream(path, path_num_chars,
1114                                     inode, lookup_table, &dat);
1115 }
1116
1117 static int
1118 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
1119                                   wchar_t *path,
1120                                   size_t path_num_chars,
1121                                   struct add_image_params *params,
1122                                   struct win32_capture_state *state,
1123                                   unsigned vol_flags)
1124 {
1125         struct wim_dentry *root = NULL;
1126         struct wim_inode *inode = NULL;
1127         DWORD err;
1128         u64 file_size;
1129         int ret;
1130         u8 *rpbuf;
1131         u16 rpbuflen;
1132         u16 not_rpfixed;
1133         HANDLE hFile = INVALID_HANDLE_VALUE;
1134         DWORD desiredAccess;
1135
1136
1137         if (exclude_path(path, path_num_chars, params->config, true)) {
1138                 if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) {
1139                         ERROR("Cannot exclude the root directory from capture");
1140                         ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
1141                         goto out;
1142                 }
1143                 ret = 0;
1144                 goto out_progress;
1145         }
1146
1147 #if 0
1148         if (path_num_chars >= 4 &&
1149             !wmemcmp(path, L"\\\\?\\", 4) &&
1150             path_num_chars + 1 - 4 > MAX_PATH &&
1151             state->num_long_path_warnings < MAX_CAPTURE_LONG_PATH_WARNINGS)
1152         {
1153                 WARNING("Path \"%ls\" exceeds MAX_PATH", path);
1154                 if (++state->num_long_path_warnings == MAX_CAPTURE_LONG_PATH_WARNINGS)
1155                         WARNING("Suppressing further warnings about long paths.");
1156         }
1157 #endif
1158
1159         desiredAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES |
1160                         READ_CONTROL | ACCESS_SYSTEM_SECURITY;
1161 again:
1162         hFile = win32_open_existing_file(path, desiredAccess);
1163         if (hFile == INVALID_HANDLE_VALUE) {
1164                 err = GetLastError();
1165                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD) {
1166                         if (desiredAccess & ACCESS_SYSTEM_SECURITY) {
1167                                 desiredAccess &= ~ACCESS_SYSTEM_SECURITY;
1168                                 goto again;
1169                         }
1170                         if (desiredAccess & READ_CONTROL) {
1171                                 desiredAccess &= ~READ_CONTROL;
1172                                 goto again;
1173                         }
1174                 }
1175                 set_errno_from_GetLastError();
1176                 ERROR_WITH_ERRNO("Failed to open \"%ls\" for reading", path);
1177                 ret = WIMLIB_ERR_OPEN;
1178                 goto out;
1179         }
1180
1181         BY_HANDLE_FILE_INFORMATION file_info;
1182         if (!GetFileInformationByHandle(hFile, &file_info)) {
1183                 set_errno_from_GetLastError();
1184                 ERROR_WITH_ERRNO("Failed to get file information for \"%ls\"",
1185                                  path);
1186                 ret = WIMLIB_ERR_STAT;
1187                 goto out;
1188         }
1189
1190         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1191                 rpbuf = alloca(REPARSE_POINT_MAX_SIZE);
1192                 ret = win32_get_reparse_data(hFile, path, params,
1193                                              rpbuf, &rpbuflen);
1194                 if (ret < 0) {
1195                         /* WIMLIB_ERR_* (inverted) */
1196                         ret = -ret;
1197                         goto out;
1198                 } else if (ret & RP_FIXED) {
1199                         not_rpfixed = 0;
1200                 } else if (ret == RP_EXCLUDED) {
1201                         ret = 0;
1202                         goto out_progress;
1203                 } else {
1204                         not_rpfixed = 1;
1205                 }
1206         }
1207
1208         /* Create a WIM dentry with an associated inode, which may be shared.
1209          *
1210          * However, we need to explicitly check for directories and files with
1211          * only 1 link and refuse to hard link them.  This is because Windows
1212          * has a bug where it can return duplicate File IDs for files and
1213          * directories on the FAT filesystem. */
1214         ret = inode_table_new_dentry(&params->inode_table,
1215                                      path_basename_with_len(path, path_num_chars),
1216                                      ((u64)file_info.nFileIndexHigh << 32) |
1217                                          (u64)file_info.nFileIndexLow,
1218                                      file_info.dwVolumeSerialNumber,
1219                                      (file_info.nNumberOfLinks <= 1 ||
1220                                         (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)),
1221                                      &root);
1222         if (ret)
1223                 goto out;
1224
1225         ret = win32_get_short_name(hFile, path, root);
1226         if (ret)
1227                 goto out;
1228
1229         inode = root->d_inode;
1230
1231         if (inode->i_nlink > 1) {
1232                 /* Shared inode; nothing more to do */
1233                 goto out_progress;
1234         }
1235
1236         inode->i_attributes = file_info.dwFileAttributes;
1237         inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
1238         inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
1239         inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
1240         inode->i_resolved = 1;
1241
1242         params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT;
1243
1244         if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)
1245             && (vol_flags & FILE_PERSISTENT_ACLS))
1246         {
1247                 ret = win32_get_security_descriptor(hFile, path, inode,
1248                                                     &params->sd_set, state,
1249                                                     params->add_flags);
1250                 if (ret)
1251                         goto out;
1252         }
1253
1254         file_size = ((u64)file_info.nFileSizeHigh << 32) |
1255                      (u64)file_info.nFileSizeLow;
1256
1257
1258         /* Capture the unnamed data stream (only should be present for regular
1259          * files) and any alternate data streams. */
1260         ret = win32_capture_streams(&hFile,
1261                                     path,
1262                                     path_num_chars,
1263                                     inode,
1264                                     params->lookup_table,
1265                                     file_size,
1266                                     vol_flags);
1267         if (ret)
1268                 goto out;
1269
1270         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1271                 /* Reparse point: set the reparse data (which we read already)
1272                  * */
1273                 inode->i_not_rpfixed = not_rpfixed;
1274                 inode->i_reparse_tag = le32_to_cpu(*(le32*)rpbuf);
1275                 ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpbuflen - 8,
1276                                                params->lookup_table);
1277         } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
1278                 /* Directory (not a reparse point) --- recurse to children */
1279
1280                 if (hFile == INVALID_HANDLE_VALUE) {
1281                         /* Re-open handle that was closed to read raw encrypted
1282                          * data.  */
1283                         hFile = win32_open_existing_file(path, FILE_READ_DATA);
1284                         if (hFile == INVALID_HANDLE_VALUE) {
1285                                 set_errno_from_GetLastError();
1286                                 ERROR_WITH_ERRNO("Failed to reopen \"%ls\"",
1287                                                  path);
1288                                 ret = WIMLIB_ERR_OPEN;
1289                                 goto out;
1290                         }
1291                 }
1292                 ret = win32_recurse_directory(hFile,
1293                                               path,
1294                                               path_num_chars,
1295                                               root,
1296                                               params,
1297                                               state,
1298                                               vol_flags);
1299         }
1300         if (ret)
1301                 goto out;
1302
1303         path[path_num_chars] = '\0';
1304 out_progress:
1305         params->progress.scan.cur_path = path;
1306         if (root == NULL)
1307                 do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
1308         else
1309                 do_capture_progress(params, WIMLIB_SCAN_DENTRY_OK, inode);
1310 out:
1311         if (hFile != INVALID_HANDLE_VALUE)
1312                 CloseHandle(hFile);
1313         if (ret == 0)
1314                 *root_ret = root;
1315         else
1316                 free_dentry_tree(root, params->lookup_table);
1317         return ret;
1318 }
1319
1320 static void
1321 win32_do_capture_warnings(const wchar_t *path,
1322                           const struct win32_capture_state *state,
1323                           int add_flags)
1324 {
1325         if (state->num_get_sacl_priv_notheld == 0 &&
1326             state->num_get_sd_access_denied == 0)
1327                 return;
1328
1329         WARNING("Scan of \"%ls\" complete, but with one or more warnings:", path);
1330         if (state->num_get_sacl_priv_notheld != 0) {
1331                 WARNING("- Could not capture SACL (System Access Control List)\n"
1332                         "            on %lu files or directories.",
1333                         state->num_get_sacl_priv_notheld);
1334         }
1335         if (state->num_get_sd_access_denied != 0) {
1336                 WARNING("- Could not capture security descriptor at all\n"
1337                         "            on %lu files or directories.",
1338                         state->num_get_sd_access_denied);
1339         }
1340         WARNING("To fully capture all security descriptors, run the program\n"
1341                 "          with Administrator rights.");
1342 }
1343
1344 #define WINDOWS_NT_MAX_PATH 32768
1345
1346 /* Win32 version of capturing a directory tree */
1347 int
1348 win32_build_dentry_tree(struct wim_dentry **root_ret,
1349                         const wchar_t *root_disk_path,
1350                         struct add_image_params *params)
1351 {
1352         size_t path_nchars;
1353         wchar_t *path;
1354         int ret;
1355         struct win32_capture_state state;
1356         unsigned vol_flags;
1357         DWORD dret;
1358         bool need_prefix_free = false;
1359
1360         if (!win32func_FindFirstStreamW
1361 #ifdef WITH_NTDLL
1362             && !func_NtQueryInformationFile
1363 #endif
1364            )
1365         {
1366                 WARNING("Running on Windows XP or earlier; "
1367                         "alternate data streams will not be captured.");
1368         }
1369
1370         path_nchars = wcslen(root_disk_path);
1371         if (path_nchars > WINDOWS_NT_MAX_PATH)
1372                 return WIMLIB_ERR_INVALID_PARAM;
1373
1374         ret = win32_get_file_and_vol_ids(root_disk_path,
1375                                          &params->capture_root_ino,
1376                                          &params->capture_root_dev);
1377         if (ret) {
1378                 ERROR_WITH_ERRNO("Can't open %ls", root_disk_path);
1379                 return ret;
1380         }
1381
1382         win32_get_vol_flags(root_disk_path, &vol_flags, NULL);
1383
1384         /* WARNING: There is no check for overflow later when this buffer is
1385          * being used!  But it's as long as the maximum path length understood
1386          * by Windows NT (which is NOT the same as MAX_PATH). */
1387         path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
1388         if (!path)
1389                 return WIMLIB_ERR_NOMEM;
1390
1391         /* Work around defective behavior in Windows where paths longer than 260
1392          * characters are not supported by default; instead they need to be
1393          * turned into absolute paths and prefixed with "\\?\".  */
1394
1395         if (wcsncmp(root_disk_path, L"\\\\?\\", 4)) {
1396                 dret = GetFullPathName(root_disk_path, WINDOWS_NT_MAX_PATH - 4,
1397                                        &path[4], NULL);
1398
1399                 if (dret == 0 || dret >= WINDOWS_NT_MAX_PATH - 4) {
1400                         WARNING("Can't get full path name for \"%ls\"", root_disk_path);
1401                         wmemcpy(path, root_disk_path, path_nchars + 1);
1402                 } else {
1403                         wmemcpy(path, L"\\\\?\\", 4);
1404                         path_nchars = 4 + dret;
1405                         /* Update pattern prefix */
1406                         if (params->config != NULL)
1407                         {
1408                                 params->config->_prefix = TSTRDUP(path);
1409                                 params->config->_prefix_num_tchars = path_nchars;
1410                                 if (params->config->_prefix == NULL)
1411                                 {
1412                                         ret = WIMLIB_ERR_NOMEM;
1413                                         goto out_free_path;
1414                                 }
1415                                 need_prefix_free = true;
1416                         }
1417                 }
1418         } else {
1419                 wmemcpy(path, root_disk_path, path_nchars + 1);
1420         }
1421
1422         memset(&state, 0, sizeof(state));
1423         ret = win32_build_dentry_tree_recursive(root_ret, path,
1424                                                 path_nchars, params,
1425                                                 &state, vol_flags);
1426         if (need_prefix_free)
1427                 FREE(params->config->_prefix);
1428 out_free_path:
1429         FREE(path);
1430         if (ret == 0)
1431                 win32_do_capture_warnings(root_disk_path, &state, params->add_flags);
1432         return ret;
1433 }
1434
1435 #endif /* __WIN32__ */