]> wimlib.net Git - wimlib/blob - src/win32_capture.c
60d212d13ce102f62de70ebd711e045bb68e1db7
[wimlib] / src / win32_capture.c
1 /*
2  * win32_capture.c - Windows-specific code for capturing files into a WIM image.
3  *
4  * This now uses the native Windows NT API a lot and not just Win32.
5  */
6
7 /*
8  * Copyright (C) 2013, 2014, 2015 Eric Biggers
9  *
10  * This file is free software; you can redistribute it and/or modify it under
11  * the terms of the GNU Lesser General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option) any
13  * later version.
14  *
15  * This file is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this file; 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/assert.h"
33 #include "wimlib/blob_table.h"
34 #include "wimlib/capture.h"
35 #include "wimlib/dentry.h"
36 #include "wimlib/encoding.h"
37 #include "wimlib/endianness.h"
38 #include "wimlib/error.h"
39 #include "wimlib/paths.h"
40 #include "wimlib/reparse.h"
41 #include "wimlib/wof.h"
42
43 struct winnt_scan_stats {
44         unsigned long num_get_sd_access_denied;
45         unsigned long num_get_sacl_priv_notheld;
46 };
47
48 static inline const wchar_t *
49 printable_path(const wchar_t *full_path)
50 {
51         /* Skip over \\?\ or \??\  */
52         return full_path + 4;
53 }
54
55 /*
56  * If cur_dir is not NULL, open an existing file relative to the already-open
57  * directory cur_dir.
58  *
59  * Otherwise, open the file specified by @path, which must be a Windows NT
60  * namespace path.
61  */
62 static NTSTATUS
63 winnt_openat(HANDLE cur_dir, const wchar_t *path, size_t path_nchars,
64              ACCESS_MASK perms, HANDLE *h_ret)
65 {
66         UNICODE_STRING name;
67         OBJECT_ATTRIBUTES attr;
68         IO_STATUS_BLOCK iosb;
69         NTSTATUS status;
70
71         name.Length = path_nchars * sizeof(wchar_t);
72         name.MaximumLength = name.Length + sizeof(wchar_t);
73         name.Buffer = (wchar_t *)path;
74
75         attr.Length = sizeof(attr);
76         attr.RootDirectory = cur_dir;
77         attr.ObjectName = &name;
78         attr.Attributes = 0;
79         attr.SecurityDescriptor = NULL;
80         attr.SecurityQualityOfService = NULL;
81
82 retry:
83         status = (*func_NtOpenFile)(h_ret, perms, &attr, &iosb,
84                                     FILE_SHARE_VALID_FLAGS,
85                                     FILE_OPEN_REPARSE_POINT |
86                                             FILE_OPEN_FOR_BACKUP_INTENT |
87                                             FILE_SYNCHRONOUS_IO_NONALERT |
88                                             FILE_SEQUENTIAL_ONLY);
89         if (!NT_SUCCESS(status)) {
90                 /* Try requesting fewer permissions  */
91                 if (status == STATUS_ACCESS_DENIED ||
92                     status == STATUS_PRIVILEGE_NOT_HELD) {
93                         if (perms & ACCESS_SYSTEM_SECURITY) {
94                                 perms &= ~ACCESS_SYSTEM_SECURITY;
95                                 goto retry;
96                         }
97                         if (perms & READ_CONTROL) {
98                                 perms &= ~READ_CONTROL;
99                                 goto retry;
100                         }
101                 }
102         }
103         return status;
104 }
105
106 /* Read the first @size bytes from the file, or named data stream of a file,
107  * described by @blob.  */
108 int
109 read_winnt_stream_prefix(const struct blob_descriptor *blob, u64 size,
110                          const struct read_blob_callbacks *cbs)
111 {
112         const wchar_t *path;
113         HANDLE h;
114         NTSTATUS status;
115         u8 buf[BUFFER_SIZE];
116         u64 bytes_remaining;
117         int ret;
118
119         /* This is an NT namespace path.  */
120         path = blob->file_on_disk;
121
122         status = winnt_openat(NULL, path, wcslen(path),
123                               FILE_READ_DATA | SYNCHRONIZE, &h);
124         if (!NT_SUCCESS(status)) {
125                 winnt_error(status, L"\"%ls\": Can't open for reading",
126                             printable_path(path));
127                 return WIMLIB_ERR_OPEN;
128         }
129
130         ret = 0;
131         bytes_remaining = size;
132         while (bytes_remaining) {
133                 IO_STATUS_BLOCK iosb;
134                 ULONG count;
135                 ULONG bytes_read;
136
137                 count = min(sizeof(buf), bytes_remaining);
138
139                 status = (*func_NtReadFile)(h, NULL, NULL, NULL,
140                                             &iosb, buf, count, NULL, NULL);
141                 if (unlikely(!NT_SUCCESS(status))) {
142                         if (status == STATUS_END_OF_FILE) {
143                                 ERROR("\"%ls\": File was concurrently truncated",
144                                       printable_path(path));
145                                 ret = WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED;
146                         } else {
147                                 winnt_error(status, L"\"%ls\": Error reading data",
148                                             printable_path(path));
149                                 ret = WIMLIB_ERR_READ;
150                         }
151                         break;
152                 }
153
154                 bytes_read = iosb.Information;
155
156                 bytes_remaining -= bytes_read;
157                 ret = call_consume_chunk(buf, bytes_read, cbs);
158                 if (ret)
159                         break;
160         }
161         (*func_NtClose)(h);
162         return ret;
163 }
164
165 struct win32_encrypted_read_ctx {
166         const struct read_blob_callbacks *cbs;
167         int wimlib_err_code;
168         u64 bytes_remaining;
169 };
170
171 static DWORD WINAPI
172 win32_encrypted_export_cb(unsigned char *data, void *_ctx, unsigned long len)
173 {
174         struct win32_encrypted_read_ctx *ctx = _ctx;
175         int ret;
176         size_t bytes_to_consume = min(len, ctx->bytes_remaining);
177
178         if (bytes_to_consume == 0)
179                 return ERROR_SUCCESS;
180
181         ret = call_consume_chunk(data, bytes_to_consume, ctx->cbs);
182         if (ret) {
183                 ctx->wimlib_err_code = ret;
184                 /* It doesn't matter what error code is returned here, as long
185                  * as it isn't ERROR_SUCCESS.  */
186                 return ERROR_READ_FAULT;
187         }
188         ctx->bytes_remaining -= bytes_to_consume;
189         return ERROR_SUCCESS;
190 }
191
192 int
193 read_win32_encrypted_file_prefix(const struct blob_descriptor *blob,
194                                  u64 size,
195                                  const struct read_blob_callbacks *cbs)
196 {
197         struct win32_encrypted_read_ctx export_ctx;
198         DWORD err;
199         void *file_ctx;
200         int ret;
201         DWORD flags = 0;
202
203         if (blob->file_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
204                 flags |= CREATE_FOR_DIR;
205
206         export_ctx.cbs = cbs;
207         export_ctx.wimlib_err_code = 0;
208         export_ctx.bytes_remaining = size;
209
210         err = OpenEncryptedFileRaw(blob->file_on_disk, flags, &file_ctx);
211         if (err != ERROR_SUCCESS) {
212                 win32_error(err,
213                             L"Failed to open encrypted file \"%ls\" for raw read",
214                             printable_path(blob->file_on_disk));
215                 return WIMLIB_ERR_OPEN;
216         }
217         err = ReadEncryptedFileRaw(win32_encrypted_export_cb,
218                                    &export_ctx, file_ctx);
219         if (err != ERROR_SUCCESS) {
220                 ret = export_ctx.wimlib_err_code;
221                 if (ret == 0) {
222                         win32_error(err,
223                                     L"Failed to read encrypted file \"%ls\"",
224                                     printable_path(blob->file_on_disk));
225                         ret = WIMLIB_ERR_READ;
226                 }
227         } else if (export_ctx.bytes_remaining != 0) {
228                 ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from "
229                       "encrypted file \"%ls\"",
230                       size - export_ctx.bytes_remaining, size,
231                       printable_path(blob->file_on_disk));
232                 ret = WIMLIB_ERR_READ;
233         } else {
234                 ret = 0;
235         }
236         CloseEncryptedFileRaw(file_ctx);
237         return ret;
238 }
239
240 /*
241  * Load the short name of a file into a WIM dentry.
242  */
243 static noinline_for_stack NTSTATUS
244 winnt_get_short_name(HANDLE h, struct wim_dentry *dentry)
245 {
246         /* It's not any harder to just make the NtQueryInformationFile() system
247          * call ourselves, and it saves a dumb call to FindFirstFile() which of
248          * course has to create its own handle.  */
249         NTSTATUS status;
250         IO_STATUS_BLOCK iosb;
251         u8 buf[128] _aligned_attribute(8);
252         const FILE_NAME_INFORMATION *info;
253
254         status = (*func_NtQueryInformationFile)(h, &iosb, buf, sizeof(buf),
255                                                 FileAlternateNameInformation);
256         info = (const FILE_NAME_INFORMATION *)buf;
257         if (NT_SUCCESS(status) && info->FileNameLength != 0) {
258                 dentry->d_short_name = utf16le_dupz(info->FileName,
259                                                     info->FileNameLength);
260                 if (!dentry->d_short_name)
261                         return STATUS_NO_MEMORY;
262                 dentry->d_short_name_nbytes = info->FileNameLength;
263         }
264         return status;
265 }
266
267 /*
268  * Load the security descriptor of a file into the corresponding inode and the
269  * WIM image's security descriptor set.
270  */
271 static noinline_for_stack NTSTATUS
272 winnt_get_security_descriptor(HANDLE h, struct wim_inode *inode,
273                               struct wim_sd_set *sd_set,
274                               struct winnt_scan_stats *stats, int add_flags)
275 {
276         SECURITY_INFORMATION requestedInformation;
277         u8 _buf[4096] _aligned_attribute(8);
278         u8 *buf;
279         ULONG bufsize;
280         ULONG len_needed;
281         NTSTATUS status;
282
283         /*
284          * LABEL_SECURITY_INFORMATION is needed on Windows Vista and 7 because
285          * Microsoft decided to add mandatory integrity labels to the SACL but
286          * not have them returned by SACL_SECURITY_INFORMATION.
287          *
288          * BACKUP_SECURITY_INFORMATION is needed on Windows 8 because Microsoft
289          * decided to add even more stuff to the SACL and still not have it
290          * returned by SACL_SECURITY_INFORMATION; but they did remember that
291          * backup applications exist and simply want to read the stupid thing
292          * once and for all, so they added a flag to read the entire security
293          * descriptor.
294          *
295          * Older versions of Windows tolerate these new flags being passed in.
296          */
297         requestedInformation = OWNER_SECURITY_INFORMATION |
298                                GROUP_SECURITY_INFORMATION |
299                                DACL_SECURITY_INFORMATION |
300                                SACL_SECURITY_INFORMATION |
301                                LABEL_SECURITY_INFORMATION |
302                                BACKUP_SECURITY_INFORMATION;
303
304         buf = _buf;
305         bufsize = sizeof(_buf);
306
307         /*
308          * We need the file's security descriptor in
309          * SECURITY_DESCRIPTOR_RELATIVE format, and we currently have a handle
310          * opened with as many relevant permissions as possible.  At this point,
311          * on Windows there are a number of options for reading a file's
312          * security descriptor:
313          *
314          * GetFileSecurity():  This takes in a path and returns the
315          * SECURITY_DESCRIPTOR_RELATIVE.  Problem: this uses an internal handle,
316          * not ours, and the handle created internally doesn't specify
317          * FILE_FLAG_BACKUP_SEMANTICS.  Therefore there can be access denied
318          * errors on some files and directories, even when running as the
319          * Administrator.
320          *
321          * GetSecurityInfo():  This takes in a handle and returns the security
322          * descriptor split into a bunch of different parts.  This should work,
323          * but it's dumb because we have to put the security descriptor back
324          * together again.
325          *
326          * BackupRead():  This can read the security descriptor, but this is a
327          * difficult-to-use API, probably only works as the Administrator, and
328          * the format of the returned data is not well documented.
329          *
330          * NtQuerySecurityObject():  This is exactly what we need, as it takes
331          * in a handle and returns the security descriptor in
332          * SECURITY_DESCRIPTOR_RELATIVE format.  Only problem is that it's a
333          * ntdll function and therefore not officially part of the Win32 API.
334          * Oh well.
335          */
336         while (!(NT_SUCCESS(status = (*func_NtQuerySecurityObject)(h,
337                                                                    requestedInformation,
338                                                                    (PSECURITY_DESCRIPTOR)buf,
339                                                                    bufsize,
340                                                                    &len_needed))))
341         {
342                 switch (status) {
343                 case STATUS_BUFFER_TOO_SMALL:
344                         wimlib_assert(buf == _buf);
345                         buf = MALLOC(len_needed);
346                         if (!buf)
347                                 return STATUS_NO_MEMORY;
348                         bufsize = len_needed;
349                         break;
350                 case STATUS_PRIVILEGE_NOT_HELD:
351                 case STATUS_ACCESS_DENIED:
352                         if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS) {
353                 default:
354                                 /* Permission denied in STRICT_ACLS mode, or
355                                  * unknown error.  */
356                                 goto out_free_buf;
357                         }
358                         if (requestedInformation & SACL_SECURITY_INFORMATION) {
359                                 /* Try again without the SACL.  */
360                                 stats->num_get_sacl_priv_notheld++;
361                                 requestedInformation &= ~(SACL_SECURITY_INFORMATION |
362                                                           LABEL_SECURITY_INFORMATION |
363                                                           BACKUP_SECURITY_INFORMATION);
364                                 break;
365                         }
366                         /* Fake success (useful when capturing as
367                          * non-Administrator).  */
368                         stats->num_get_sd_access_denied++;
369                         status = STATUS_SUCCESS;
370                         goto out_free_buf;
371                 }
372         }
373
374         /* Add the security descriptor to the WIM image, and save its ID in
375          * file's inode.  */
376         inode->i_security_id = sd_set_add_sd(sd_set, buf, len_needed);
377         if (unlikely(inode->i_security_id < 0))
378                 status = STATUS_NO_MEMORY;
379 out_free_buf:
380         if (unlikely(buf != _buf))
381                 FREE(buf);
382         return status;
383 }
384
385 static int
386 winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
387                                   HANDLE cur_dir,
388                                   wchar_t *full_path,
389                                   size_t full_path_nchars,
390                                   const wchar_t *filename,
391                                   size_t filename_nchars,
392                                   struct capture_params *params,
393                                   struct winnt_scan_stats *stats,
394                                   u32 vol_flags);
395
396 static int
397 winnt_recurse_directory(HANDLE h,
398                         wchar_t *full_path,
399                         size_t full_path_nchars,
400                         struct wim_dentry *parent,
401                         struct capture_params *params,
402                         struct winnt_scan_stats *stats,
403                         u32 vol_flags)
404 {
405         void *buf;
406         const size_t bufsize = 8192;
407         IO_STATUS_BLOCK iosb;
408         NTSTATUS status;
409         int ret;
410
411         buf = MALLOC(bufsize);
412         if (!buf)
413                 return WIMLIB_ERR_NOMEM;
414
415         /* Using NtQueryDirectoryFile() we can re-use the same open handle,
416          * which we opened with FILE_FLAG_BACKUP_SEMANTICS.  */
417
418         while (NT_SUCCESS(status = (*func_NtQueryDirectoryFile)(h, NULL, NULL, NULL,
419                                                                 &iosb, buf, bufsize,
420                                                                 FileNamesInformation,
421                                                                 FALSE, NULL, FALSE)))
422         {
423                 const FILE_NAMES_INFORMATION *info = buf;
424                 for (;;) {
425                         if (!should_ignore_filename(info->FileName,
426                                                     info->FileNameLength / 2))
427                         {
428                                 wchar_t *p;
429                                 wchar_t *filename;
430                                 struct wim_dentry *child;
431
432                                 p = full_path + full_path_nchars;
433                                 /* Only add a backslash if we don't already have
434                                  * one.  This prevents a duplicate backslash
435                                  * from being added when the path to the capture
436                                  * dir had a trailing backslash.  */
437                                 if (*(p - 1) != L'\\')
438                                         *p++ = L'\\';
439                                 filename = p;
440                                 p = wmempcpy(filename, info->FileName,
441                                              info->FileNameLength / 2);
442                                 *p = '\0';
443
444                                 ret = winnt_build_dentry_tree_recursive(
445                                                         &child,
446                                                         h,
447                                                         full_path,
448                                                         p - full_path,
449                                                         filename,
450                                                         info->FileNameLength / 2,
451                                                         params,
452                                                         stats,
453                                                         vol_flags);
454
455                                 full_path[full_path_nchars] = L'\0';
456
457                                 if (ret)
458                                         goto out_free_buf;
459                                 attach_scanned_tree(parent, child, params->blob_table);
460                         }
461                         if (info->NextEntryOffset == 0)
462                                 break;
463                         info = (const FILE_NAMES_INFORMATION *)
464                                         ((const u8 *)info + info->NextEntryOffset);
465                 }
466         }
467
468         if (unlikely(status != STATUS_NO_MORE_FILES)) {
469                 winnt_error(status, L"\"%ls\": Can't read directory",
470                             printable_path(full_path));
471                 ret = WIMLIB_ERR_READ;
472         }
473 out_free_buf:
474         FREE(buf);
475         return ret;
476 }
477
478 /* Reparse point fixup status code  */
479 #define RP_FIXED        (-1)
480
481 static bool
482 file_has_ino_and_dev(HANDLE h, u64 ino, u64 dev)
483 {
484         NTSTATUS status;
485         IO_STATUS_BLOCK iosb;
486         FILE_INTERNAL_INFORMATION int_info;
487         FILE_FS_VOLUME_INFORMATION vol_info;
488
489         status = (*func_NtQueryInformationFile)(h, &iosb,
490                                                 &int_info, sizeof(int_info),
491                                                 FileInternalInformation);
492         if (!NT_SUCCESS(status))
493                 return false;
494
495         if (int_info.IndexNumber.QuadPart != ino)
496                 return false;
497
498         status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
499                                                       &vol_info, sizeof(vol_info),
500                                                       FileFsVolumeInformation);
501         if (!(NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW))
502                 return false;
503
504         if (iosb.Information <
505              offsetof(FILE_FS_VOLUME_INFORMATION, VolumeSerialNumber) +
506              sizeof(vol_info.VolumeSerialNumber))
507                 return false;
508
509         return (vol_info.VolumeSerialNumber == dev);
510 }
511
512 /*
513  * This is the Windows equivalent of unix_relativize_link_target(); see there
514  * for general details.  This version works with an "absolute" Windows link
515  * target, specified from the root of the Windows kernel object namespace.  Note
516  * that we have to open directories with a trailing slash when present because
517  * \??\E: opens the E: device itself and not the filesystem root directory.
518  */
519 static const wchar_t *
520 winnt_relativize_link_target(const wchar_t *target, size_t target_nbytes,
521                              u64 ino, u64 dev)
522 {
523         UNICODE_STRING name;
524         OBJECT_ATTRIBUTES attr;
525         IO_STATUS_BLOCK iosb;
526         NTSTATUS status;
527         const wchar_t *target_end;
528         const wchar_t *p;
529
530         target_end = target + (target_nbytes / sizeof(wchar_t));
531
532         /* Empty path??? */
533         if (target_end == target)
534                 return target;
535
536         /* No leading slash???  */
537         if (target[0] != L'\\')
538                 return target;
539
540         /* UNC path???  */
541         if ((target_end - target) >= 2 &&
542             target[0] == L'\\' && target[1] == L'\\')
543                 return target;
544
545         attr.Length = sizeof(attr);
546         attr.RootDirectory = NULL;
547         attr.ObjectName = &name;
548         attr.Attributes = 0;
549         attr.SecurityDescriptor = NULL;
550         attr.SecurityQualityOfService = NULL;
551
552         name.Buffer = (wchar_t *)target;
553         name.Length = 0;
554         p = target;
555         do {
556                 HANDLE h;
557                 const wchar_t *orig_p = p;
558
559                 /* Skip non-backslashes  */
560                 while (p != target_end && *p != L'\\')
561                         p++;
562
563                 /* Skip backslashes  */
564                 while (p != target_end && *p == L'\\')
565                         p++;
566
567                 /* Append path component  */
568                 name.Length += (p - orig_p) * sizeof(wchar_t);
569                 name.MaximumLength = name.Length;
570
571                 /* Try opening the file  */
572                 status = (*func_NtOpenFile) (&h,
573                                              FILE_READ_ATTRIBUTES | FILE_TRAVERSE,
574                                              &attr,
575                                              &iosb,
576                                              FILE_SHARE_VALID_FLAGS,
577                                              FILE_OPEN_FOR_BACKUP_INTENT);
578
579                 if (NT_SUCCESS(status)) {
580                         /* Reset root directory  */
581                         if (attr.RootDirectory)
582                                 (*func_NtClose)(attr.RootDirectory);
583                         attr.RootDirectory = h;
584                         name.Buffer = (wchar_t *)p;
585                         name.Length = 0;
586
587                         if (file_has_ino_and_dev(h, ino, dev))
588                                 goto out_close_root_dir;
589                 }
590         } while (p != target_end);
591
592         p = target;
593
594 out_close_root_dir:
595         if (attr.RootDirectory)
596                 (*func_NtClose)(attr.RootDirectory);
597         while (p > target && *(p - 1) == L'\\')
598                 p--;
599         return p;
600 }
601
602 static int
603 winnt_rpfix_progress(struct capture_params *params, const wchar_t *path,
604                      const struct link_reparse_point *link, int scan_status)
605 {
606         size_t print_name_nchars = link->print_name_nbytes / sizeof(wchar_t);
607         wchar_t print_name0[print_name_nchars + 1];
608
609         wmemcpy(print_name0, link->print_name, print_name_nchars);
610         print_name0[print_name_nchars] = L'\0';
611
612         params->progress.scan.cur_path = printable_path(path);
613         params->progress.scan.symlink_target = print_name0;
614         return do_capture_progress(params, scan_status, NULL);
615 }
616
617 static int
618 winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p,
619                 const wchar_t *path, struct capture_params *params)
620 {
621         struct link_reparse_point link;
622         const wchar_t *rel_target;
623         int ret;
624
625         if (parse_link_reparse_point(rpbuf, *rpbuflen_p, &link)) {
626                 /* Couldn't understand the reparse data; don't do the fixup.  */
627                 return 0;
628         }
629
630         /*
631          * Don't do reparse point fixups on relative symbolic links.
632          *
633          * On Windows, a relative symbolic link is supposed to be identifiable
634          * by having reparse tag WIM_IO_REPARSE_TAG_SYMLINK and flags
635          * SYMBOLIC_LINK_RELATIVE.  We will use this information, although this
636          * may not always do what the user expects, since drive-relative
637          * symbolic links such as "\Users\Public" have SYMBOLIC_LINK_RELATIVE
638          * set, in addition to truly relative symbolic links such as "Users" or
639          * "Users\Public".  However, WIMGAPI (as of Windows 8.1) has this same
640          * behavior.
641          *
642          * Otherwise, as far as I can tell, the targets of symbolic links that
643          * are NOT relative, as well as junctions (note: a mountpoint is the
644          * sames thing as a junction), must be NT namespace paths, for example:
645          *
646          *     - \??\e:\Users\Public
647          *     - \DosDevices\e:\Users\Public
648          *     - \Device\HardDiskVolume4\Users\Public
649          *     - \??\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public
650          *     - \DosDevices\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public
651          */
652         if (link_is_relative_symlink(&link))
653                 return 0;
654
655         rel_target = winnt_relativize_link_target(link.substitute_name,
656                                                   link.substitute_name_nbytes,
657                                                   params->capture_root_ino,
658                                                   params->capture_root_dev);
659
660         if (rel_target == link.substitute_name) {
661                 /* Target points outside of the tree being captured or had an
662                  * unrecognized path format.  Don't adjust it.  */
663                 return winnt_rpfix_progress(params, path, &link,
664                                             WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK);
665         }
666
667         /* We have an absolute target pointing within the directory being
668          * captured. @rel_target is the suffix of the link target that is the
669          * part relative to the directory being captured.
670          *
671          * We will cut off the prefix before this part (which is the path to the
672          * directory being captured) and add a dummy prefix.  Since the process
673          * will need to be reversed when applying the image, it doesn't matter
674          * what exactly the prefix is, as long as it looks like an absolute
675          * path.  */
676
677         static const wchar_t prefix[6] = L"\\??\\X:";
678         static const size_t num_unprintable_chars = 4;
679
680         size_t rel_target_nbytes =
681                 link.substitute_name_nbytes - ((const u8 *)rel_target -
682                                                (const u8 *)link.substitute_name);
683
684         wchar_t tmp[(sizeof(prefix) + rel_target_nbytes) / sizeof(wchar_t)];
685
686         memcpy(tmp, prefix, sizeof(prefix));
687         memcpy(tmp + ARRAY_LEN(prefix), rel_target, rel_target_nbytes);
688
689         link.substitute_name = tmp;
690         link.substitute_name_nbytes = sizeof(tmp);
691
692         link.print_name = link.substitute_name + num_unprintable_chars;
693         link.print_name_nbytes = link.substitute_name_nbytes -
694                                  (num_unprintable_chars * sizeof(wchar_t));
695
696         if (make_link_reparse_point(&link, rpbuf, rpbuflen_p))
697                 return 0;
698
699         ret = winnt_rpfix_progress(params, path, &link,
700                                    WIMLIB_SCAN_DENTRY_FIXED_SYMLINK);
701         if (ret)
702                 return ret;
703         return RP_FIXED;
704 }
705
706 /* Load the reparse data of a file into the corresponding WIM inode.  If the
707  * reparse point is a symbolic link or junction with an absolute target and
708  * RPFIX mode is enabled, then also rewrite its target to be relative to the
709  * capture root.  */
710 static noinline_for_stack int
711 winnt_load_reparse_data(HANDLE h, struct wim_inode *inode,
712                         const wchar_t *full_path, struct capture_params *params)
713 {
714         struct reparse_buffer_disk rpbuf;
715         DWORD bytes_returned;
716         u16 rpbuflen;
717         int ret;
718
719         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
720                 /* See comment above assign_stream_types_encrypted()  */
721                 WARNING("Ignoring reparse data of encrypted file \"%ls\"",
722                         printable_path(full_path));
723                 return 0;
724         }
725
726         if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT,
727                              NULL, 0, &rpbuf, REPARSE_POINT_MAX_SIZE,
728                              &bytes_returned, NULL))
729         {
730                 win32_error(GetLastError(), L"\"%ls\": Can't get reparse point",
731                             printable_path(full_path));
732                 return WIMLIB_ERR_READLINK;
733         }
734
735         rpbuflen = bytes_returned;
736
737         if (unlikely(rpbuflen < REPARSE_DATA_OFFSET)) {
738                 ERROR("\"%ls\": reparse point buffer is too short",
739                       printable_path(full_path));
740                 return WIMLIB_ERR_INVALID_REPARSE_DATA;
741         }
742
743         if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX) {
744                 ret = winnt_try_rpfix(&rpbuf, &rpbuflen, full_path, params);
745                 if (ret == RP_FIXED)
746                         inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED;
747                 else if (ret)
748                         return ret;
749         }
750
751         inode->i_reparse_tag = le32_to_cpu(rpbuf.rptag);
752         inode->i_rp_reserved = le16_to_cpu(rpbuf.rpreserved);
753
754         if (!inode_add_stream_with_data(inode,
755                                         STREAM_TYPE_REPARSE_POINT,
756                                         NO_STREAM_NAME,
757                                         rpbuf.rpdata,
758                                         rpbuflen - REPARSE_DATA_OFFSET,
759                                         params->blob_table))
760                 return WIMLIB_ERR_NOMEM;
761
762         return 0;
763 }
764
765 static DWORD WINAPI
766 win32_tally_encrypted_size_cb(unsigned char *_data, void *_size_ret,
767                               unsigned long len)
768 {
769         *(u64*)_size_ret += len;
770         return ERROR_SUCCESS;
771 }
772
773 static int
774 win32_get_encrypted_file_size(const wchar_t *path, bool is_dir, u64 *size_ret)
775 {
776         DWORD err;
777         void *file_ctx;
778         int ret;
779         DWORD flags = 0;
780
781         if (is_dir)
782                 flags |= CREATE_FOR_DIR;
783
784         err = OpenEncryptedFileRaw(path, flags, &file_ctx);
785         if (err != ERROR_SUCCESS) {
786                 win32_error(err,
787                             L"Failed to open encrypted file \"%ls\" for raw read",
788                             printable_path(path));
789                 return WIMLIB_ERR_OPEN;
790         }
791         *size_ret = 0;
792         err = ReadEncryptedFileRaw(win32_tally_encrypted_size_cb,
793                                    size_ret, file_ctx);
794         if (err != ERROR_SUCCESS) {
795                 win32_error(err,
796                             L"Failed to read raw encrypted data from \"%ls\"",
797                             printable_path(path));
798                 ret = WIMLIB_ERR_READ;
799         } else {
800                 ret = 0;
801         }
802         CloseEncryptedFileRaw(file_ctx);
803         return ret;
804 }
805
806 static int
807 winnt_scan_efsrpc_raw_data(struct wim_inode *inode, const wchar_t *nt_path,
808                            struct list_head *unhashed_blobs)
809 {
810         struct blob_descriptor *blob;
811         struct wim_inode_stream *strm;
812         int ret;
813
814         blob = new_blob_descriptor();
815         if (!blob)
816                 goto err_nomem;
817
818         blob->file_on_disk = WCSDUP(nt_path);
819         if (!blob->file_on_disk)
820                 goto err_nomem;
821         blob->blob_location = BLOB_WIN32_ENCRYPTED;
822
823         /* OpenEncryptedFileRaw() expects a Win32 name.  */
824         wimlib_assert(!wmemcmp(blob->file_on_disk, L"\\??\\", 4));
825         blob->file_on_disk[1] = L'\\';
826
827         blob->file_inode = inode;
828
829         ret = win32_get_encrypted_file_size(blob->file_on_disk,
830                                             (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY),
831                                             &blob->size);
832         if (ret)
833                 goto err;
834
835         /* Empty EFSRPC data does not make sense  */
836         wimlib_assert(blob->size != 0);
837
838         strm = inode_add_stream(inode, STREAM_TYPE_EFSRPC_RAW_DATA,
839                                 NO_STREAM_NAME, blob);
840         if (!strm)
841                 goto err_nomem;
842
843         prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
844         return 0;
845
846 err_nomem:
847         ret = WIMLIB_ERR_NOMEM;
848 err:
849         free_blob_descriptor(blob);
850         return ret;
851 }
852
853 static bool
854 get_data_stream_name(wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
855                      wchar_t **stream_name_ret, size_t *stream_name_nchars_ret)
856 {
857         const wchar_t *sep, *type, *end;
858
859         /* The stream name should be returned as :NAME:TYPE  */
860         if (raw_stream_name_nchars < 1)
861                 return false;
862         if (raw_stream_name[0] != L':')
863                 return false;
864
865         raw_stream_name++;
866         raw_stream_name_nchars--;
867
868         end = raw_stream_name + raw_stream_name_nchars;
869
870         sep = wmemchr(raw_stream_name, L':', raw_stream_name_nchars);
871         if (!sep)
872                 return false;
873
874         type = sep + 1;
875         if (end - type != 5)
876                 return false;
877
878         if (wmemcmp(type, L"$DATA", 5))
879                 return false;
880
881         *stream_name_ret = raw_stream_name;
882         *stream_name_nchars_ret = sep - raw_stream_name;
883         return true;
884 }
885
886 /* Build the path to the data stream.  For unnamed streams, this is simply the
887  * path to the file.  For named streams, this is the path to the file, followed
888  * by a colon, followed by the stream name.  */
889 static wchar_t *
890 build_data_stream_path(const wchar_t *path, size_t path_nchars,
891                        const wchar_t *stream_name, size_t stream_name_nchars)
892 {
893         size_t stream_path_nchars;
894         wchar_t *stream_path;
895         wchar_t *p;
896
897         stream_path_nchars = path_nchars;
898         if (stream_name_nchars)
899                 stream_path_nchars += 1 + stream_name_nchars;
900
901         stream_path = MALLOC((stream_path_nchars + 1) * sizeof(wchar_t));
902         if (stream_path) {
903                 p = wmempcpy(stream_path, path, path_nchars);
904                 if (stream_name_nchars) {
905                         *p++ = L':';
906                         p = wmempcpy(p, stream_name, stream_name_nchars);
907                 }
908                 *p++ = L'\0';
909         }
910         return stream_path;
911 }
912
913 static int
914 winnt_scan_data_stream(const wchar_t *path, size_t path_nchars,
915                        wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
916                        u64 stream_size,
917                        struct wim_inode *inode, struct list_head *unhashed_blobs)
918 {
919         wchar_t *stream_name;
920         size_t stream_name_nchars;
921         struct blob_descriptor *blob;
922         struct wim_inode_stream *strm;
923
924         /* Given the raw stream name (which is something like
925          * :streamname:$DATA), extract just the stream name part (streamname).
926          * Ignore any non-$DATA streams.  */
927         if (!get_data_stream_name(raw_stream_name, raw_stream_name_nchars,
928                                   &stream_name, &stream_name_nchars))
929                 return 0;
930
931         stream_name[stream_name_nchars] = L'\0';
932
933         /* If the stream is non-empty, set up a blob descriptor for it.  */
934         if (stream_size != 0) {
935                 blob = new_blob_descriptor();
936                 if (!blob)
937                         goto err_nomem;
938                 blob->file_on_disk = build_data_stream_path(path,
939                                                             path_nchars,
940                                                             stream_name,
941                                                             stream_name_nchars);
942                 if (!blob->file_on_disk)
943                         goto err_nomem;
944                 blob->blob_location = BLOB_IN_WINNT_FILE_ON_DISK;
945                 blob->size = stream_size;
946                 blob->file_inode = inode;
947         } else {
948                 blob = NULL;
949         }
950
951         strm = inode_add_stream(inode, STREAM_TYPE_DATA, stream_name, blob);
952         if (!strm)
953                 goto err_nomem;
954
955         prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
956         return 0;
957
958 err_nomem:
959         free_blob_descriptor(blob);
960         return WIMLIB_ERR_NOMEM;
961 }
962
963 /*
964  * Load information about the data streams of an open file into a WIM inode.
965  *
966  * We use the NtQueryInformationFile() system call instead of FindFirstStream()
967  * and FindNextStream().  This is done for two reasons:
968  *
969  * - FindFirstStream() opens its own handle to the file or directory and
970  *   apparently does so without specifying FILE_FLAG_BACKUP_SEMANTICS, thereby
971  *   causing access denied errors on certain files (even when running as the
972  *   Administrator).
973  * - FindFirstStream() and FindNextStream() is only available on Windows Vista
974  *   and later, whereas the stream support in NtQueryInformationFile() was
975  *   already present in Windows XP.
976  */
977 static noinline_for_stack int
978 winnt_scan_data_streams(HANDLE h, const wchar_t *path, size_t path_nchars,
979                         struct wim_inode *inode, struct list_head *unhashed_blobs,
980                         u64 file_size, u32 vol_flags)
981 {
982         int ret;
983         u8 _buf[4096] _aligned_attribute(8);
984         u8 *buf;
985         size_t bufsize;
986         IO_STATUS_BLOCK iosb;
987         NTSTATUS status;
988         FILE_STREAM_INFORMATION *info;
989
990         buf = _buf;
991         bufsize = sizeof(_buf);
992
993         if (!(vol_flags & FILE_NAMED_STREAMS))
994                 goto unnamed_only;
995
996         /* Get a buffer containing the stream information.  */
997         while (!NT_SUCCESS(status = (*func_NtQueryInformationFile)(h,
998                                                                    &iosb,
999                                                                    buf,
1000                                                                    bufsize,
1001                                                                    FileStreamInformation)))
1002         {
1003
1004                 switch (status) {
1005                 case STATUS_BUFFER_OVERFLOW:
1006                         {
1007                                 u8 *newbuf;
1008
1009                                 bufsize *= 2;
1010                                 if (buf == _buf)
1011                                         newbuf = MALLOC(bufsize);
1012                                 else
1013                                         newbuf = REALLOC(buf, bufsize);
1014                                 if (!newbuf) {
1015                                         ret = WIMLIB_ERR_NOMEM;
1016                                         goto out_free_buf;
1017                                 }
1018                                 buf = newbuf;
1019                         }
1020                         break;
1021                 case STATUS_NOT_IMPLEMENTED:
1022                 case STATUS_NOT_SUPPORTED:
1023                 case STATUS_INVALID_INFO_CLASS:
1024                         goto unnamed_only;
1025                 default:
1026                         winnt_error(status,
1027                                     L"\"%ls\": Failed to query stream information",
1028                                     printable_path(path));
1029                         ret = WIMLIB_ERR_READ;
1030                         goto out_free_buf;
1031                 }
1032         }
1033
1034         if (iosb.Information == 0) {
1035                 /* No stream information.  */
1036                 ret = 0;
1037                 goto out_free_buf;
1038         }
1039
1040         /* Parse one or more stream information structures.  */
1041         info = (FILE_STREAM_INFORMATION *)buf;
1042         for (;;) {
1043                 /* Load the stream information.  */
1044                 ret = winnt_scan_data_stream(path, path_nchars,
1045                                              info->StreamName,
1046                                              info->StreamNameLength / 2,
1047                                              info->StreamSize.QuadPart,
1048                                              inode, unhashed_blobs);
1049                 if (ret)
1050                         goto out_free_buf;
1051
1052                 if (info->NextEntryOffset == 0) {
1053                         /* No more stream information.  */
1054                         break;
1055                 }
1056                 /* Advance to next stream information.  */
1057                 info = (FILE_STREAM_INFORMATION *)
1058                                 ((u8 *)info + info->NextEntryOffset);
1059         }
1060         ret = 0;
1061         goto out_free_buf;
1062
1063 unnamed_only:
1064         /* The volume does not support named streams.  Only capture the unnamed
1065          * data stream.  */
1066         if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
1067                                    FILE_ATTRIBUTE_REPARSE_POINT))
1068         {
1069                 ret = 0;
1070                 goto out_free_buf;
1071         }
1072
1073         {
1074                 wchar_t stream_name[] = L"::$DATA";
1075                 ret = winnt_scan_data_stream(path, path_nchars, stream_name, 7,
1076                                              file_size, inode, unhashed_blobs);
1077         }
1078 out_free_buf:
1079         /* Free buffer if allocated on heap.  */
1080         if (unlikely(buf != _buf))
1081                 FREE(buf);
1082         return ret;
1083 }
1084
1085 static noinline_for_stack u64
1086 get_sort_key(HANDLE h)
1087 {
1088         STARTING_VCN_INPUT_BUFFER in = { .StartingVcn.QuadPart = 0 };
1089         RETRIEVAL_POINTERS_BUFFER out;
1090         DWORD bytesReturned;
1091
1092         if (!DeviceIoControl(h, FSCTL_GET_RETRIEVAL_POINTERS,
1093                              &in, sizeof(in),
1094                              &out, sizeof(out),
1095                              &bytesReturned, NULL))
1096                 return 0;
1097
1098         if (out.ExtentCount < 1)
1099                 return 0;
1100
1101         return out.Extents[0].Lcn.QuadPart;
1102 }
1103
1104 static void
1105 set_sort_key(struct wim_inode *inode, u64 sort_key)
1106 {
1107         for (unsigned i = 0; i < inode->i_num_streams; i++) {
1108                 struct wim_inode_stream *strm = &inode->i_streams[i];
1109                 struct blob_descriptor *blob = stream_blob_resolved(strm);
1110                 if (blob && (blob->blob_location == BLOB_IN_WINNT_FILE_ON_DISK ||
1111                              blob->blob_location == BLOB_WIN32_ENCRYPTED))
1112                         blob->sort_key = sort_key;
1113         }
1114 }
1115
1116 /* Special case to better support in-place updates of backing WIM files: in
1117  * WIMBOOT mode, if the file is backed by a WIM (perhaps the WIM being updated)
1118  * and WOF is not attached to the volume, then capture the file as the
1119  * "dereferenced file" rather than as the "pointer file".  Note that this only
1120  * requires stream manipulation --- there is no special handling of metadata
1121  * such as security descriptors required.  */
1122 static noinline_for_stack int
1123 fixup_wim_backed_file(struct wim_inode *inode, struct blob_table *blob_table)
1124 {
1125         struct wim_inode_stream *strm;
1126         struct blob_descriptor *blob;
1127         u8 _rpdata[REPARSE_DATA_MAX_SIZE] _aligned_attribute(8);
1128         struct {
1129                 struct wof_external_info wof_info;
1130                 struct wim_provider_rpdata wim_info;
1131         } *rpdata = (void *)_rpdata;
1132         int ret;
1133
1134         strm = inode_get_unnamed_stream(inode, STREAM_TYPE_REPARSE_POINT);
1135         blob = stream_blob_resolved(strm);
1136         if (!blob || blob->size < sizeof(*rpdata))
1137                 return 0;
1138
1139         ret = read_blob_into_buf(blob, rpdata);
1140         if (ret)
1141                 return ret;
1142
1143         if (rpdata->wof_info.version != WOF_CURRENT_VERSION ||
1144             rpdata->wof_info.provider != WOF_PROVIDER_WIM ||
1145             rpdata->wim_info.version != 2)
1146                 return 0;
1147
1148         blob = lookup_blob(blob_table, rpdata->wim_info.unnamed_data_stream_hash);
1149         if (!blob)
1150                 return 0;
1151
1152         /* All okay --- remove the reparse point and redirect the unnamed data
1153          * stream to the "dereferenced" one.  */
1154         inode_remove_stream(inode, strm, blob_table);
1155         strm = inode_get_unnamed_data_stream(inode);
1156         wimlib_assert(strm != NULL);
1157         inode_replace_stream_blob(inode, strm, blob, blob_table);
1158         inode->i_attributes &= ~(FILE_ATTRIBUTE_REPARSE_POINT |
1159                                  FILE_ATTRIBUTE_SPARSE_FILE);
1160         if (inode->i_attributes == 0)
1161                 inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
1162         return 0;
1163 }
1164
1165 static noinline_for_stack u32
1166 get_volume_information(HANDLE h, const wchar_t *full_path,
1167                        struct capture_params *params)
1168 {
1169         FILE_FS_ATTRIBUTE_INFORMATION attr_info;
1170         FILE_FS_VOLUME_INFORMATION vol_info;
1171         IO_STATUS_BLOCK iosb;
1172         NTSTATUS status;
1173         u32 vol_flags;
1174
1175         /* Get volume flags  */
1176         status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
1177                                                       &attr_info,
1178                                                       sizeof(attr_info),
1179                                                       FileFsAttributeInformation);
1180         if (likely((NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) &&
1181                    (iosb.Information >=
1182                         offsetof(FILE_FS_ATTRIBUTE_INFORMATION,
1183                                  FileSystemAttributes) +
1184                         sizeof(attr_info.FileSystemAttributes))))
1185         {
1186                 vol_flags = attr_info.FileSystemAttributes;
1187         } else {
1188                 winnt_warning(status, L"\"%ls\": Can't get volume attributes",
1189                               printable_path(full_path));
1190                 vol_flags = 0;
1191         }
1192
1193         /* Get volume ID.  */
1194         status = (*func_NtQueryVolumeInformationFile)(h, &iosb,
1195                                                       &vol_info,
1196                                                       sizeof(vol_info),
1197                                                       FileFsVolumeInformation);
1198         if (likely((NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) &&
1199                    (iosb.Information >=
1200                         offsetof(FILE_FS_VOLUME_INFORMATION,
1201                                  VolumeSerialNumber) +
1202                         sizeof(vol_info.VolumeSerialNumber))))
1203         {
1204                 params->capture_root_dev = vol_info.VolumeSerialNumber;
1205         } else {
1206                 winnt_warning(status, L"\"%ls\": Can't get volume ID",
1207                               printable_path(full_path));
1208                 params->capture_root_dev = 0;
1209         }
1210         return vol_flags;
1211 }
1212
1213 struct file_info {
1214         u32 attributes;
1215         u32 num_links;
1216         u64 creation_time;
1217         u64 last_write_time;
1218         u64 last_access_time;
1219         u64 ino;
1220         u64 end_of_file;
1221 };
1222
1223 static noinline_for_stack NTSTATUS
1224 get_file_info(HANDLE h, struct file_info *info)
1225 {
1226         IO_STATUS_BLOCK iosb;
1227         NTSTATUS status;
1228         FILE_ALL_INFORMATION all_info;
1229
1230         status = (*func_NtQueryInformationFile)(h, &iosb, &all_info,
1231                                                 sizeof(all_info),
1232                                                 FileAllInformation);
1233
1234         if (unlikely(!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW))
1235                 return status;
1236
1237         info->attributes = all_info.BasicInformation.FileAttributes;
1238         info->num_links = all_info.StandardInformation.NumberOfLinks;
1239         info->creation_time = all_info.BasicInformation.CreationTime.QuadPart;
1240         info->last_write_time = all_info.BasicInformation.LastWriteTime.QuadPart;
1241         info->last_access_time = all_info.BasicInformation.LastAccessTime.QuadPart;
1242         info->ino = all_info.InternalInformation.IndexNumber.QuadPart;
1243         info->end_of_file = all_info.StandardInformation.EndOfFile.QuadPart;
1244         return STATUS_SUCCESS;
1245 }
1246
1247 static int
1248 winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
1249                                   HANDLE cur_dir,
1250                                   wchar_t *full_path,
1251                                   size_t full_path_nchars,
1252                                   const wchar_t *filename,
1253                                   size_t filename_nchars,
1254                                   struct capture_params *params,
1255                                   struct winnt_scan_stats *stats,
1256                                   u32 vol_flags)
1257 {
1258         struct wim_dentry *root = NULL;
1259         struct wim_inode *inode = NULL;
1260         HANDLE h = NULL;
1261         int ret;
1262         NTSTATUS status;
1263         struct file_info file_info;
1264         ACCESS_MASK requestedPerms;
1265         u64 sort_key;
1266
1267         ret = try_exclude(full_path, params);
1268         if (unlikely(ret < 0)) /* Excluded? */
1269                 goto out_progress;
1270         if (unlikely(ret > 0)) /* Error? */
1271                 goto out;
1272
1273         /* Open the file.  */
1274         requestedPerms = FILE_READ_DATA |
1275                          FILE_READ_ATTRIBUTES |
1276                          READ_CONTROL |
1277                          ACCESS_SYSTEM_SECURITY |
1278                          SYNCHRONIZE;
1279 retry_open:
1280         status = winnt_openat(cur_dir,
1281                               (cur_dir ? filename : full_path),
1282                               (cur_dir ? filename_nchars : full_path_nchars),
1283                               requestedPerms,
1284                               &h);
1285         if (unlikely(!NT_SUCCESS(status))) {
1286                 if (status == STATUS_DELETE_PENDING) {
1287                         WARNING("\"%ls\": Deletion pending; skipping file",
1288                                 printable_path(full_path));
1289                         ret = 0;
1290                         goto out;
1291                 }
1292                 if (status == STATUS_ACCESS_DENIED &&
1293                     (requestedPerms & FILE_READ_DATA)) {
1294                         /* This happens on encrypted files.  */
1295                         requestedPerms &= ~FILE_READ_DATA;
1296                         goto retry_open;
1297                 }
1298
1299                 winnt_error(status, L"\"%ls\": Can't open file",
1300                             printable_path(full_path));
1301                 if (status == STATUS_FVE_LOCKED_VOLUME)
1302                         ret = WIMLIB_ERR_FVE_LOCKED_VOLUME;
1303                 else
1304                         ret = WIMLIB_ERR_OPEN;
1305                 goto out;
1306         }
1307
1308         /* Get information about the file.  */
1309         status = get_file_info(h, &file_info);
1310         if (!NT_SUCCESS(status)) {
1311                 winnt_error(status, L"\"%ls\": Can't get file information",
1312                             printable_path(full_path));
1313                 ret = WIMLIB_ERR_STAT;
1314                 goto out;
1315         }
1316
1317         if (unlikely(!(requestedPerms & FILE_READ_DATA)) &&
1318             !(file_info.attributes & FILE_ATTRIBUTE_ENCRYPTED))
1319         {
1320                 ERROR("\"%ls\": Permission to read data was denied",
1321                       printable_path(full_path));
1322                 ret = WIMLIB_ERR_OPEN;
1323                 goto out;
1324         }
1325
1326         if (unlikely(!cur_dir)) {
1327                 /* Root of tree being captured; get volume information.  */
1328                 vol_flags = get_volume_information(h, full_path, params);
1329                 params->capture_root_ino = file_info.ino;
1330         }
1331
1332
1333         /* Create a WIM dentry with an associated inode, which may be shared.
1334          *
1335          * However, we need to explicitly check for directories and files with
1336          * only 1 link and refuse to hard link them.  This is because Windows
1337          * has a bug where it can return duplicate File IDs for files and
1338          * directories on the FAT filesystem.
1339          *
1340          * Since we don't follow mount points on Windows, we don't need to query
1341          * the volume ID per-file.  Just once, for the root, is enough.  But we
1342          * can't simply pass 0, because then there could be inode collisions
1343          * among multiple calls to win32_build_dentry_tree() that are scanning
1344          * files on different volumes.  */
1345         ret = inode_table_new_dentry(params->inode_table,
1346                                      filename,
1347                                      file_info.ino,
1348                                      params->capture_root_dev,
1349                                      (file_info.num_links <= 1),
1350                                      &root);
1351         if (ret)
1352                 goto out;
1353
1354         /* Get the short (DOS) name of the file.  */
1355         status = winnt_get_short_name(h, root);
1356
1357         /* If we can't read the short filename for any reason other than
1358          * out-of-memory, just ignore the error and assume the file has no short
1359          * name.  This shouldn't be an issue, since the short names are
1360          * essentially obsolete anyway.  */
1361         if (unlikely(status == STATUS_NO_MEMORY)) {
1362                 ret = WIMLIB_ERR_NOMEM;
1363                 goto out;
1364         }
1365
1366         inode = root->d_inode;
1367
1368         if (inode->i_nlink > 1) {
1369                 /* Shared inode (hard link); skip reading per-inode information.
1370                  */
1371                 goto out_progress;
1372         }
1373
1374         inode->i_attributes = file_info.attributes;
1375         inode->i_creation_time = file_info.creation_time;
1376         inode->i_last_write_time = file_info.last_write_time;
1377         inode->i_last_access_time = file_info.last_access_time;
1378
1379         /* Get the file's security descriptor, unless we are capturing in
1380          * NO_ACLS mode or the volume does not support security descriptors.  */
1381         if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)
1382             && (vol_flags & FILE_PERSISTENT_ACLS))
1383         {
1384                 status = winnt_get_security_descriptor(h, inode,
1385                                                        params->sd_set, stats,
1386                                                        params->add_flags);
1387                 if (!NT_SUCCESS(status)) {
1388                         winnt_error(status,
1389                                     L"\"%ls\": Can't read security descriptor",
1390                                     printable_path(full_path));
1391                         ret = WIMLIB_ERR_STAT;
1392                         goto out;
1393                 }
1394         }
1395
1396         /* If this is a reparse point, load the reparse data.  */
1397         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1398                 ret = winnt_load_reparse_data(h, inode, full_path, params);
1399                 if (ret)
1400                         goto out;
1401         }
1402
1403         sort_key = get_sort_key(h);
1404
1405         if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
1406                 /* Load information about the raw encrypted data.  This is
1407                  * needed for any directory or non-directory that has
1408                  * FILE_ATTRIBUTE_ENCRYPTED set.
1409                  *
1410                  * Note: since OpenEncryptedFileRaw() fails with
1411                  * ERROR_SHARING_VIOLATION if there are any open handles to the
1412                  * file, we have to close the file and re-open it later if
1413                  * needed.  */
1414                 (*func_NtClose)(h);
1415                 h = NULL;
1416                 ret = winnt_scan_efsrpc_raw_data(inode, full_path,
1417                                                  params->unhashed_blobs);
1418                 if (ret)
1419                         goto out;
1420         } else {
1421                 /*
1422                  * Load information about data streams (unnamed and named).
1423                  *
1424                  * Skip this step for encrypted files, since the data from
1425                  * ReadEncryptedFileRaw() already contains all data streams (and
1426                  * they do in fact all get restored by WriteEncryptedFileRaw().)
1427                  *
1428                  * Note: WIMGAPI (as of Windows 8.1) gets wrong and stores both
1429                  * the EFSRPC data and the named data stream(s)...!
1430                  */
1431                 ret = winnt_scan_data_streams(h,
1432                                               full_path,
1433                                               full_path_nchars,
1434                                               inode,
1435                                               params->unhashed_blobs,
1436                                               file_info.end_of_file,
1437                                               vol_flags);
1438                 if (ret)
1439                         goto out;
1440         }
1441
1442         if (unlikely((params->add_flags & WIMLIB_ADD_FLAG_WIMBOOT) &&
1443                      (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
1444                      (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_WOF)))
1445         {
1446                 ret = fixup_wim_backed_file(inode, params->blob_table);
1447                 if (ret)
1448                         goto out;
1449         }
1450
1451         set_sort_key(inode, sort_key);
1452
1453         if (inode_is_directory(inode)) {
1454
1455                 /* Directory: recurse to children.  */
1456
1457                 if (unlikely(!h)) {
1458                         /* Re-open handle that was closed to read raw encrypted
1459                          * data.  */
1460                         status = winnt_openat(cur_dir,
1461                                               (cur_dir ?
1462                                                filename : full_path),
1463                                               (cur_dir ?
1464                                                filename_nchars : full_path_nchars),
1465                                               FILE_LIST_DIRECTORY | SYNCHRONIZE,
1466                                               &h);
1467                         if (!NT_SUCCESS(status)) {
1468                                 winnt_error(status,
1469                                             L"\"%ls\": Can't re-open file",
1470                                             printable_path(full_path));
1471                                 ret = WIMLIB_ERR_OPEN;
1472                                 goto out;
1473                         }
1474                 }
1475                 ret = winnt_recurse_directory(h,
1476                                               full_path,
1477                                               full_path_nchars,
1478                                               root,
1479                                               params,
1480                                               stats,
1481                                               vol_flags);
1482                 if (ret)
1483                         goto out;
1484         }
1485
1486 out_progress:
1487         params->progress.scan.cur_path = printable_path(full_path);
1488         if (likely(root))
1489                 ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_OK, inode);
1490         else
1491                 ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
1492 out:
1493         if (likely(h))
1494                 (*func_NtClose)(h);
1495         if (unlikely(ret)) {
1496                 free_dentry_tree(root, params->blob_table);
1497                 root = NULL;
1498                 ret = report_capture_error(params, ret, full_path);
1499         }
1500         *root_ret = root;
1501         return ret;
1502 }
1503
1504 static void
1505 winnt_do_scan_warnings(const wchar_t *path, const struct winnt_scan_stats *stats)
1506 {
1507         if (likely(stats->num_get_sacl_priv_notheld == 0 &&
1508                    stats->num_get_sd_access_denied == 0))
1509                 return;
1510
1511         WARNING("Scan of \"%ls\" complete, but with one or more warnings:", path);
1512         if (stats->num_get_sacl_priv_notheld != 0) {
1513                 WARNING("- Could not capture SACL (System Access Control List)\n"
1514                         "            on %lu files or directories.",
1515                         stats->num_get_sacl_priv_notheld);
1516         }
1517         if (stats->num_get_sd_access_denied != 0) {
1518                 WARNING("- Could not capture security descriptor at all\n"
1519                         "            on %lu files or directories.",
1520                         stats->num_get_sd_access_denied);
1521         }
1522         WARNING("To fully capture all security descriptors, run the program\n"
1523                 "          with Administrator rights.");
1524 }
1525
1526 #define WINDOWS_NT_MAX_PATH 32768
1527
1528 /* Win32 version of capturing a directory tree.  */
1529 int
1530 win32_build_dentry_tree(struct wim_dentry **root_ret,
1531                         const wchar_t *root_disk_path,
1532                         struct capture_params *params)
1533 {
1534         wchar_t *path;
1535         int ret;
1536         UNICODE_STRING ntpath;
1537         struct winnt_scan_stats stats;
1538         size_t ntpath_nchars;
1539
1540         /* WARNING: There is no check for overflow later when this buffer is
1541          * being used!  But it's as long as the maximum path length understood
1542          * by Windows NT (which is NOT the same as MAX_PATH).  */
1543         path = MALLOC((WINDOWS_NT_MAX_PATH + 1) * sizeof(wchar_t));
1544         if (!path)
1545                 return WIMLIB_ERR_NOMEM;
1546
1547         ret = win32_path_to_nt_path(root_disk_path, &ntpath);
1548         if (ret)
1549                 goto out_free_path;
1550
1551         if (ntpath.Length < 4 * sizeof(wchar_t) ||
1552             ntpath.Length > WINDOWS_NT_MAX_PATH * sizeof(wchar_t) ||
1553             wmemcmp(ntpath.Buffer, L"\\??\\", 4))
1554         {
1555                 ERROR("\"%ls\": unrecognized path format", root_disk_path);
1556                 ret = WIMLIB_ERR_INVALID_PARAM;
1557         } else {
1558                 ntpath_nchars = ntpath.Length / sizeof(wchar_t);
1559                 wmemcpy(path, ntpath.Buffer, ntpath_nchars);
1560                 path[ntpath_nchars] = L'\0';
1561
1562                 params->capture_root_nchars = ntpath_nchars;
1563                 if (path[ntpath_nchars - 1] == L'\\')
1564                         params->capture_root_nchars--;
1565                 ret = 0;
1566         }
1567         HeapFree(GetProcessHeap(), 0, ntpath.Buffer);
1568         if (ret)
1569                 goto out_free_path;
1570
1571         memset(&stats, 0, sizeof(stats));
1572
1573         ret = winnt_build_dentry_tree_recursive(root_ret, NULL,
1574                                                 path, ntpath_nchars,
1575                                                 L"", 0, params, &stats, 0);
1576 out_free_path:
1577         FREE(path);
1578         if (ret == 0)
1579                 winnt_do_scan_warnings(root_disk_path, &stats);
1580         return ret;
1581 }
1582
1583 #endif /* __WIN32__ */