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