]> wimlib.net Git - wimlib/blob - src/ntfs-3g_capture.c
v1.14.4
[wimlib] / src / ntfs-3g_capture.c
1 /*
2  * ntfs-3g_capture.c
3  *
4  * Capture a WIM image directly from an NTFS volume using libntfs-3g.  We capture
5  * everything we can, including security data and alternate data streams.
6  */
7
8 /*
9  * Copyright (C) 2012-2017 Eric Biggers
10  *
11  * This file is free software; you can redistribute it and/or modify it under
12  * the terms of the GNU Lesser General Public License as published by the Free
13  * Software Foundation; either version 3 of the License, or (at your option) any
14  * later version.
15  *
16  * This file is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this file; if not, see https://www.gnu.org/licenses/.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28
29 #ifdef WITH_NTFS_3G
30
31 #include <errno.h>
32
33 #include <ntfs-3g/attrib.h>
34 #include <ntfs-3g/compat.h> /* for ENODATA, if needed */
35 #include <ntfs-3g/object_id.h>
36 #include <ntfs-3g/reparse.h>
37 #include <ntfs-3g/security.h>
38 #include <ntfs-3g/volume.h>
39
40 #include "wimlib/alloca.h"
41 #include "wimlib/assert.h"
42 #include "wimlib/blob_table.h"
43 #include "wimlib/dentry.h"
44 #include "wimlib/encoding.h"
45 #include "wimlib/endianness.h"
46 #include "wimlib/error.h"
47 #include "wimlib/ntfs_3g.h"
48 #include "wimlib/object_id.h"
49 #include "wimlib/paths.h"
50 #include "wimlib/reparse.h"
51 #include "wimlib/scan.h"
52 #include "wimlib/security.h"
53
54 /* NTFS-3G 2013 renamed MS_RDONLY to NTFS_MNT_RDONLY.  We can't check for the
55  * existence of NTFS_MNT_RDONLY at compilation time because it's an enum.  We
56  * also can't check for MS_RDONLY being missing because it's also a system
57  * constant.  So check if the NTFS-3G specific MS_IGNORE_HIBERFILE is defined;
58  * if yes, then we need to use the old MS_RDONLY.  */
59 #ifdef MS_IGNORE_HIBERFILE
60 #  define NTFS_MNT_RDONLY MS_RDONLY
61 #endif
62
63 /* A reference-counted NTFS volume than is automatically unmounted when the
64  * reference count reaches 0  */
65 struct ntfs_volume_wrapper {
66         ntfs_volume *vol;
67         size_t refcnt;
68         bool dedup_warned;
69 };
70
71 /* Description of where data is located in an NTFS volume  */
72 struct ntfs_location {
73         struct ntfs_volume_wrapper *volume;
74         u64 mft_no;
75         ATTR_TYPES attr_type;
76         u32 attr_name_nchars;
77         ntfschar *attr_name;
78         u64 sort_key;
79 };
80
81 static struct ntfs_volume_wrapper *
82 get_ntfs_volume(struct ntfs_volume_wrapper *volume)
83 {
84         volume->refcnt++;
85         return volume;
86 }
87
88 static void
89 put_ntfs_volume(struct ntfs_volume_wrapper *volume)
90 {
91         if (--volume->refcnt == 0) {
92                 ntfs_umount(volume->vol, FALSE);
93                 FREE(volume);
94         }
95 }
96
97 static inline const ntfschar *
98 attr_record_name(const ATTR_RECORD *record)
99 {
100         return (const ntfschar *)
101                 ((const u8 *)record + le16_to_cpu(record->name_offset));
102 }
103
104 static ntfs_attr *
105 open_ntfs_attr(ntfs_inode *ni, const struct ntfs_location *loc)
106 {
107         ntfs_attr *na;
108
109         na = ntfs_attr_open(ni, loc->attr_type, loc->attr_name,
110                             loc->attr_name_nchars);
111         if (!na) {
112                 ERROR_WITH_ERRNO("Failed to open attribute of NTFS inode %"PRIu64,
113                                  loc->mft_no);
114         }
115         return na;
116 }
117
118 int
119 read_ntfs_attribute_prefix(const struct blob_descriptor *blob, u64 size,
120                            const struct consume_chunk_callback *cb,
121                            bool recover_data)
122 {
123         const struct ntfs_location *loc = blob->ntfs_loc;
124         ntfs_volume *vol = loc->volume->vol;
125         ntfs_inode *ni;
126         ntfs_attr *na;
127         s64 pos;
128         s64 bytes_remaining;
129         int ret;
130         u8 buf[BUFFER_SIZE];
131
132         ni = ntfs_inode_open(vol, loc->mft_no);
133         if (!ni) {
134                 ERROR_WITH_ERRNO("Failed to open NTFS inode %"PRIu64,
135                                  loc->mft_no);
136                 ret = WIMLIB_ERR_NTFS_3G;
137                 goto out;
138         }
139
140         na = open_ntfs_attr(ni, loc);
141         if (!na) {
142                 ret = WIMLIB_ERR_NTFS_3G;
143                 goto out_close_ntfs_inode;
144         }
145
146         pos = (loc->attr_type == AT_REPARSE_POINT) ? REPARSE_DATA_OFFSET : 0;
147         bytes_remaining = size;
148         while (bytes_remaining) {
149                 s64 to_read = min(bytes_remaining, sizeof(buf));
150                 if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
151                         ERROR_WITH_ERRNO("Error reading data from NTFS inode "
152                                          "%"PRIu64, loc->mft_no);
153                         ret = WIMLIB_ERR_NTFS_3G;
154                         goto out_close_ntfs_attr;
155                 }
156                 pos += to_read;
157                 bytes_remaining -= to_read;
158                 ret = consume_chunk(cb, buf, to_read);
159                 if (ret)
160                         goto out_close_ntfs_attr;
161         }
162         ret = 0;
163 out_close_ntfs_attr:
164         ntfs_attr_close(na);
165 out_close_ntfs_inode:
166         ntfs_inode_close(ni);
167 out:
168         return ret;
169 }
170
171 void
172 free_ntfs_location(struct ntfs_location *loc)
173 {
174         put_ntfs_volume(loc->volume);
175         if (loc->attr_name != AT_UNNAMED)
176                 FREE(loc->attr_name);
177         FREE(loc);
178 }
179
180 struct ntfs_location *
181 clone_ntfs_location(const struct ntfs_location *loc)
182 {
183         struct ntfs_location *new = memdup(loc, sizeof(*loc));
184         if (!new)
185                 goto err0;
186         if (loc->attr_name != AT_UNNAMED) {
187                 new->attr_name = utf16le_dup(loc->attr_name);
188                 if (!new->attr_name)
189                         goto err1;
190         }
191         new->volume = get_ntfs_volume(loc->volume);
192         return new;
193
194 err1:
195         FREE(new);
196 err0:
197         return NULL;
198 }
199
200 int
201 cmp_ntfs_locations(const struct ntfs_location *loc1,
202                    const struct ntfs_location *loc2)
203 {
204         return cmp_u64(loc1->sort_key, loc2->sort_key);
205 }
206
207 /* Read rptag and rpreserved from the NTFS inode and save them in the WIM inode.
208  */
209 static int
210 read_reparse_header(ntfs_inode *ni, struct wim_inode *inode)
211 {
212         struct {
213                 le32 rptag;
214                 le16 rpdatalen;
215                 le16 rpreserved;
216         } hdr;
217         s64 res;
218         ntfs_attr *na;
219
220         na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0);
221         if (!na)
222                 return WIMLIB_ERR_NTFS_3G;
223
224         res = ntfs_attr_pread(na, 0, sizeof(hdr), &hdr);
225
226         ntfs_attr_close(na);
227
228         if (res != sizeof(hdr))
229                 return WIMLIB_ERR_NTFS_3G;
230
231         inode->i_reparse_tag = le32_to_cpu(hdr.rptag);
232         inode->i_rp_reserved = le16_to_cpu(hdr.rpreserved);
233         return 0;
234 }
235
236
237 static void
238 warn_special_reparse_points(const struct wim_inode *inode,
239                             const struct scan_params *params,
240                             struct ntfs_volume_wrapper *volume)
241 {
242         if (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_DEDUP &&
243             (params->add_flags & WIMLIB_ADD_FLAG_WINCONFIG) &&
244             !volume->dedup_warned)
245         {
246                 WARNING(
247           "Filesystem includes files deduplicated with Windows'\n"
248 "          Data Deduplication feature, which to properly restore\n"
249 "          would require that the chunk store in \"System Volume Information\"\n"
250 "          be included in the WIM image. By default \"System Volume Information\"\n"
251 "          is excluded, so you may want to use a custom capture configuration\n"
252 "          file which includes it.");
253                 volume->dedup_warned = true;
254         }
255 }
256
257 static int
258 attr_type_to_wimlib_stream_type(ATTR_TYPES type)
259 {
260         switch (type) {
261         case AT_DATA:
262                 return STREAM_TYPE_DATA;
263         case AT_REPARSE_POINT:
264                 return STREAM_TYPE_REPARSE_POINT;
265         default:
266                 wimlib_assert(0);
267                 return STREAM_TYPE_UNKNOWN;
268         }
269 }
270
271 /* When sorting blobs located in NTFS volumes for sequential reading, we sort
272  * first by starting LCN of the attribute if available, otherwise no sort order
273  * is defined.  This usually results in better sequential access to the volume.
274  */
275 static int
276 set_attr_sort_key(ntfs_inode *ni, struct ntfs_location *loc)
277 {
278         ntfs_attr *na;
279         runlist_element *rl;
280
281         na = open_ntfs_attr(ni, loc);
282         if (!na)
283                 return WIMLIB_ERR_NTFS_3G;
284
285         rl = ntfs_attr_find_vcn(na, 0);
286         if (rl && rl->lcn != LCN_HOLE)
287                 loc->sort_key = rl->lcn;
288         else
289                 loc->sort_key = 0;
290
291         ntfs_attr_close(na);
292         return 0;
293 }
294
295 /*
296  * Add a new stream to the specified inode, with duplicate checking.
297  *
298  * This works around a problem where NTFS-3G can list multiple unnamed data
299  * streams for a single file.  In this case we can only keep one.  We'll prefer
300  * one that is nonempty.
301  */
302 static int
303 add_stream(struct wim_inode *inode, const char *path, int stream_type,
304            const utf16lechar *stream_name, struct blob_descriptor **blob_p,
305            struct list_head *unhashed_blobs)
306 {
307         struct blob_descriptor *blob = *blob_p;
308         struct wim_inode_stream *strm;
309
310         strm = inode_get_stream(inode, stream_type, stream_name);
311         if (unlikely(strm)) {
312                 /* Stream already existed.  */
313                 if (!blob)
314                         return 0;
315                 if (stream_blob_resolved(strm)) {
316                         WARNING("\"%s\" has multiple nonempty streams "
317                                 "with the same type and name!  Only the first "
318                                 "will be saved.", path);
319                         return 0;
320                 }
321                 inode_replace_stream_blob(inode, strm, blob, NULL);
322         } else {
323                 strm = inode_add_stream(inode, stream_type, stream_name, blob);
324                 if (unlikely(!strm))
325                         return WIMLIB_ERR_NOMEM;
326         }
327         prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
328         *blob_p = NULL;
329         return 0;
330
331 }
332
333 /* Save information about an NTFS attribute (stream) to a WIM inode.  */
334 static int
335 scan_ntfs_attr(struct wim_inode *inode,
336                ntfs_inode *ni,
337                const char *path,
338                struct list_head *unhashed_blobs,
339                struct ntfs_volume_wrapper *volume,
340                ATTR_TYPES type,
341                const ATTR_RECORD *record)
342 {
343         u64 data_size = ntfs_get_attribute_value_length(record);
344         const u32 name_nchars = record->name_length;
345         struct blob_descriptor *blob = NULL;
346         utf16lechar *stream_name = (utf16lechar *)NO_STREAM_NAME;
347         int ret;
348
349         if (unlikely(name_nchars)) {
350                 /* Named stream  */
351                 stream_name = utf16le_dupz(attr_record_name(record),
352                                            name_nchars * sizeof(ntfschar));
353                 if (!stream_name) {
354                         ret = WIMLIB_ERR_NOMEM;
355                         goto out_cleanup;
356                 }
357         }
358
359         if (unlikely(type == AT_REPARSE_POINT)) {
360                 if (data_size < REPARSE_DATA_OFFSET) {
361                         ERROR("Reparse point attribute of \"%s\" "
362                               "is too short!", path);
363                         ret = WIMLIB_ERR_INVALID_REPARSE_DATA;
364                         goto out_cleanup;
365                 }
366                 data_size -= REPARSE_DATA_OFFSET;
367
368                 ret = read_reparse_header(ni, inode);
369                 if (ret) {
370                         ERROR_WITH_ERRNO("Error reading reparse point header "
371                                          "of \"%s\"", path);
372                         goto out_cleanup;
373                 }
374         }
375
376         /* If the stream is non-empty, set up a blob descriptor for it.  */
377         if (data_size != 0) {
378                 blob = new_blob_descriptor();
379                 if (unlikely(!blob)) {
380                         ret = WIMLIB_ERR_NOMEM;
381                         goto out_cleanup;
382                 }
383
384                 blob->ntfs_loc = MALLOC(sizeof(struct ntfs_location));
385                 if (unlikely(!blob->ntfs_loc)) {
386                         ret = WIMLIB_ERR_NOMEM;
387                         goto out_cleanup;
388                 }
389
390                 blob->blob_location = BLOB_IN_NTFS_VOLUME;
391                 blob->size = data_size;
392                 blob->ntfs_loc->volume = get_ntfs_volume(volume);
393                 blob->ntfs_loc->mft_no = ni->mft_no;
394                 blob->ntfs_loc->attr_type = type;
395
396                 if (unlikely(name_nchars)) {
397                         blob->ntfs_loc->attr_name_nchars = name_nchars;
398                         blob->ntfs_loc->attr_name = utf16le_dup(stream_name);
399                         if (!blob->ntfs_loc->attr_name) {
400                                 ret = WIMLIB_ERR_NOMEM;
401                                 goto out_cleanup;
402                         }
403                 } else {
404                         blob->ntfs_loc->attr_name_nchars = 0;
405                         blob->ntfs_loc->attr_name = AT_UNNAMED;
406                 }
407
408                 ret = set_attr_sort_key(ni, blob->ntfs_loc);
409                 if (ret)
410                         goto out_cleanup;
411         }
412
413         ret = add_stream(inode, path, attr_type_to_wimlib_stream_type(type),
414                          stream_name, &blob, unhashed_blobs);
415 out_cleanup:
416         free_blob_descriptor(blob);
417         if (stream_name != NO_STREAM_NAME)
418                 FREE(stream_name);
419         return ret;
420 }
421
422 /* Scan attributes of the specified type from a file in the NTFS volume  */
423 static int
424 scan_ntfs_attrs_with_type(struct wim_inode *inode,
425                           ntfs_inode *ni,
426                           const char *path,
427                           struct list_head *unhashed_blobs,
428                           struct ntfs_volume_wrapper *volume,
429                           ATTR_TYPES type)
430 {
431         ntfs_attr_search_ctx *actx;
432         int ret;
433
434         actx = ntfs_attr_get_search_ctx(ni, NULL);
435         if (!actx) {
436                 ERROR_WITH_ERRNO("Failed to get NTFS attribute search "
437                                  "context for \"%s\"", path);
438                 return WIMLIB_ERR_NTFS_3G;
439         }
440
441         while (!ntfs_attr_lookup(type, NULL, 0,
442                                  CASE_SENSITIVE, 0, NULL, 0, actx))
443         {
444                 ret = scan_ntfs_attr(inode,
445                                      ni,
446                                      path,
447                                      unhashed_blobs,
448                                      volume,
449                                      type,
450                                      actx->attr);
451                 if (ret)
452                         goto out_put_actx;
453         }
454         if (errno != ENOENT) {
455                 ERROR_WITH_ERRNO("Error listing NTFS attributes of \"%s\"", path);
456                 ret = WIMLIB_ERR_NTFS_3G;
457                 goto out_put_actx;
458         }
459         ret = 0;
460 out_put_actx:
461         ntfs_attr_put_search_ctx(actx);
462         return ret;
463 }
464
465 static noinline_for_stack int
466 load_object_id(ntfs_inode *ni, struct wim_inode *inode)
467 {
468         OBJECT_ID_ATTR attr;
469         int len;
470
471         len = ntfs_get_ntfs_object_id(ni, (char *)&attr, sizeof(attr));
472         if (likely(len == -ENODATA || len == 0))
473                 return 0;
474         if (len < 0)
475                 return WIMLIB_ERR_NTFS_3G;
476         if (!inode_set_object_id(inode, &attr, len))
477                 return WIMLIB_ERR_NOMEM;
478         return 0;
479 }
480
481 /* Load the security descriptor of an NTFS inode into the corresponding WIM
482  * inode and the WIM image's security descriptor set.  */
483 static noinline_for_stack int
484 get_security_descriptor(ntfs_inode *ni, struct wim_inode *inode,
485                         ntfs_volume *vol, struct wim_sd_set *sd_set)
486 {
487         struct SECURITY_CONTEXT scx = {.vol = vol};
488         char _buf[4096];
489         char *buf = _buf;
490         size_t avail_size = sizeof(_buf);
491         int ret;
492
493 retry:
494         ret = ntfs_get_ntfs_acl(&scx, ni, buf, avail_size);
495         if (unlikely(ret < 0)) {
496                 ret = WIMLIB_ERR_NTFS_3G;
497                 goto out;
498         }
499
500         if (unlikely(ret > avail_size)) {
501                 if (unlikely(buf != _buf))
502                         FREE(buf);
503                 buf = MALLOC(ret);
504                 if (!buf) {
505                         ret = WIMLIB_ERR_NOMEM;
506                         goto out;
507                 }
508                 avail_size = ret;
509                 goto retry;
510         }
511
512         if (likely(ret > 0)) {
513                 inode->i_security_id = sd_set_add_sd(sd_set, buf, ret);
514                 if (unlikely(inode->i_security_id < 0)) {
515                         ret = WIMLIB_ERR_NOMEM;
516                         goto out;
517                 }
518         }
519
520         ret = 0;
521 out:
522         if (unlikely(buf != _buf))
523                 FREE(buf);
524         return ret;
525 }
526
527 /* Binary tree that maps NTFS inode numbers to DOS names */
528 struct dos_name_map {
529         struct avl_tree_node *root;
530 };
531
532 struct dos_name_node {
533         struct avl_tree_node index_node;
534         char dos_name[24];
535         int name_nbytes;
536         u64 ntfs_ino;
537 };
538
539 #define DOS_NAME_NODE(avl_node) \
540         avl_tree_entry(avl_node, struct dos_name_node, index_node)
541
542 static int
543 _avl_cmp_by_ntfs_ino(const struct avl_tree_node *n1,
544                      const struct avl_tree_node *n2)
545 {
546         return cmp_u64(DOS_NAME_NODE(n1)->ntfs_ino,
547                        DOS_NAME_NODE(n2)->ntfs_ino);
548 }
549
550 /* Inserts a new DOS name into the map */
551 static int
552 insert_dos_name(struct dos_name_map *map, const ntfschar *dos_name,
553                 size_t name_nbytes, u64 ntfs_ino)
554 {
555         struct dos_name_node *new_node;
556
557         new_node = MALLOC(sizeof(struct dos_name_node));
558         if (!new_node)
559                 return WIMLIB_ERR_NOMEM;
560
561         /* DOS names are supposed to be 12 characters max (that's 24 bytes,
562          * assuming 2-byte ntfs characters) */
563         wimlib_assert(name_nbytes <= sizeof(new_node->dos_name));
564
565         /* Initialize the DOS name, DOS name length, and NTFS inode number of
566          * the search tree node */
567         memcpy(new_node->dos_name, dos_name, name_nbytes);
568         new_node->name_nbytes = name_nbytes;
569         new_node->ntfs_ino = ntfs_ino;
570
571         /* Insert the search tree node */
572         if (avl_tree_insert(&map->root, &new_node->index_node,
573                             _avl_cmp_by_ntfs_ino))
574         {
575                 /* This should be impossible since an NTFS inode cannot have
576                  * multiple DOS names, and we only should get each DOS name
577                  * entry once from the ntfs_readdir() calls. */
578                 WARNING("NTFS inode %"PRIu64" has multiple DOS names", ntfs_ino);
579                 FREE(new_node);
580         }
581         return 0;
582 }
583
584 /* Returns a structure that contains the DOS name and its length for an NTFS
585  * inode, or NULL if the inode has no DOS name. */
586 static struct dos_name_node *
587 lookup_dos_name(const struct dos_name_map *map, u64 ntfs_ino)
588 {
589         struct dos_name_node dummy;
590         struct avl_tree_node *res;
591
592         dummy.ntfs_ino = ntfs_ino;
593
594         res = avl_tree_lookup_node(map->root, &dummy.index_node,
595                                    _avl_cmp_by_ntfs_ino);
596         if (!res)
597                 return NULL;
598         return DOS_NAME_NODE(res);
599 }
600
601 static int
602 set_dentry_dos_name(struct wim_dentry *dentry, const struct dos_name_map *map)
603 {
604         const struct dos_name_node *node;
605
606         if (dentry->d_is_win32_name) {
607                 node = lookup_dos_name(map, dentry->d_inode->i_ino);
608                 if (node) {
609                         dentry->d_short_name = utf16le_dupz(node->dos_name,
610                                                             node->name_nbytes);
611                         if (!dentry->d_short_name)
612                                 return WIMLIB_ERR_NOMEM;
613                         dentry->d_short_name_nbytes = node->name_nbytes;
614                 } else {
615                         WARNING("NTFS inode %"PRIu64" has Win32 name with no "
616                                 "corresponding DOS name",
617                                 dentry->d_inode->i_ino);
618                 }
619         }
620         return 0;
621 }
622
623 static void
624 destroy_dos_name_map(struct dos_name_map *map)
625 {
626         struct dos_name_node *node;
627         avl_tree_for_each_in_postorder(node, map->root,
628                                        struct dos_name_node, index_node)
629                 FREE(node);
630 }
631
632 struct readdir_ctx {
633         struct wim_dentry *parent;
634         struct dos_name_map dos_name_map;
635         struct ntfs_volume_wrapper *volume;
636         struct scan_params *params;
637         int ret;
638 };
639
640 static int
641 ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_p,
642                                     const MFT_REF mref,
643                                     const char *filename,
644                                     int name_type,
645                                     struct ntfs_volume_wrapper *volume,
646                                     struct scan_params *params);
647
648 static int
649 filldir(void *_ctx, const ntfschar *name, const int name_nchars,
650         const int name_type, const s64 pos, const MFT_REF mref,
651         const unsigned dt_type)
652 {
653         struct readdir_ctx *ctx = _ctx;
654         struct scan_params *params = ctx->params;
655         const size_t name_nbytes = name_nchars * sizeof(ntfschar);
656         char *mbs_name;
657         size_t mbs_name_nbytes;
658         size_t orig_path_nchars;
659         struct wim_dentry *child;
660         int ret;
661
662         if (name_type & FILE_NAME_DOS) {
663                 /* If this is the entry for a DOS name, store it for later. */
664                 ret = insert_dos_name(&ctx->dos_name_map, name,
665                                       name_nbytes, MREF(mref));
666
667                 /* Return now if an error occurred or if this is just a DOS name
668                  * and not a Win32+DOS name. */
669                 if (ret != 0 || name_type == FILE_NAME_DOS)
670                         goto out;
671         }
672
673         ret = utf16le_to_tstr(name, name_nbytes, &mbs_name, &mbs_name_nbytes);
674         if (ret)
675                 goto out;
676
677         if (should_ignore_filename(mbs_name, mbs_name_nbytes))
678                 goto out_free_mbs_name;
679
680         ret = WIMLIB_ERR_NOMEM;
681         if (!pathbuf_append_name(params, mbs_name, mbs_name_nbytes,
682                                  &orig_path_nchars))
683                 goto out_free_mbs_name;
684
685         child = NULL;
686         ret = ntfs_3g_build_dentry_tree_recursive(&child, mref, mbs_name,
687                                                   name_type, ctx->volume,
688                                                   params);
689         pathbuf_truncate(params, orig_path_nchars);
690         attach_scanned_tree(ctx->parent, child, params->blob_table);
691 out_free_mbs_name:
692         FREE(mbs_name);
693 out:
694         ctx->ret = ret;
695         return ret;
696 }
697
698 static int
699 ntfs_3g_recurse_directory(ntfs_inode *ni, struct wim_dentry *parent,
700                           struct ntfs_volume_wrapper *volume,
701                           struct scan_params *params)
702 {
703         int ret;
704         s64 pos = 0;
705         struct readdir_ctx ctx = {
706                 .parent          = parent,
707                 .dos_name_map    = { .root = NULL },
708                 .volume          = volume,
709                 .params          = params,
710                 .ret             = 0,
711         };
712         ret = ntfs_readdir(ni, &pos, &ctx, filldir);
713         if (unlikely(ret)) {
714                 if (ctx.ret) {
715                         /* wimlib error  */
716                         ret = ctx.ret;
717                 } else {
718                         /* error from ntfs_readdir() itself  */
719                         ERROR_WITH_ERRNO("Error reading directory \"%s\"",
720                                          params->cur_path);
721                         ret = WIMLIB_ERR_NTFS_3G;
722                 }
723         } else {
724                 struct wim_dentry *child;
725
726                 ret = 0;
727                 for_dentry_child(child, parent) {
728                         ret = set_dentry_dos_name(child, &ctx.dos_name_map);
729                         if (ret)
730                                 break;
731                 }
732         }
733         destroy_dos_name_map(&ctx.dos_name_map);
734         return ret;
735 }
736
737 static int
738 ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_ret,
739                                     const MFT_REF mref,
740                                     const char *filename,
741                                     int name_type,
742                                     struct ntfs_volume_wrapper *volume,
743                                     struct scan_params *params)
744 {
745         const char *path = params->cur_path;
746         u32 attributes;
747         int ret;
748         struct wim_dentry *root = NULL;
749         struct wim_inode *inode = NULL;
750         ntfs_inode *ni = NULL;
751
752         ret = try_exclude(params);
753         if (unlikely(ret < 0)) /* Excluded? */
754                 goto out_progress;
755         if (unlikely(ret > 0)) /* Error? */
756                 goto out;
757
758         ni = ntfs_inode_open(volume->vol, mref);
759         if (!ni) {
760                 ERROR_WITH_ERRNO("Failed to open NTFS file \"%s\"", path);
761                 ret = WIMLIB_ERR_NTFS_3G;
762                 goto out;
763         }
764
765         /* Get file attributes */
766         ret = ntfs_get_ntfs_attrib(ni, (char*)&attributes, sizeof(attributes));
767         if (ret != sizeof(attributes)) {
768                 ERROR_WITH_ERRNO("Failed to get NTFS attributes from \"%s\"", path);
769                 ret = WIMLIB_ERR_NTFS_3G;
770                 goto out;
771         }
772
773         if (unlikely(attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
774                 if (params->add_flags & WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE)
775                 {
776                         ERROR("Can't archive \"%s\" because NTFS-3G capture mode "
777                               "does not support encrypted files and directories", path);
778                         ret = WIMLIB_ERR_UNSUPPORTED_FILE;
779                         goto out;
780                 }
781                 ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL);
782                 goto out;
783         }
784
785         /* Create a WIM dentry with an associated inode, which may be shared */
786         ret = inode_table_new_dentry(params->inode_table, filename,
787                                      ni->mft_no, 0, false, &root);
788         if (ret)
789                 goto out;
790
791         if (name_type & FILE_NAME_WIN32) /* Win32 or Win32+DOS name (rather than POSIX) */
792                 root->d_is_win32_name = 1;
793
794         inode = root->d_inode;
795
796         if (inode->i_nlink > 1) {
797                 /* Shared inode; nothing more to do */
798                 goto out_progress;
799         }
800
801         inode->i_creation_time    = le64_to_cpu(ni->creation_time);
802         inode->i_last_write_time  = le64_to_cpu(ni->last_data_change_time);
803         inode->i_last_access_time = le64_to_cpu(ni->last_access_time);
804         inode->i_attributes       = attributes;
805
806         if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
807                 /* Scan the reparse point stream.  */
808                 ret = scan_ntfs_attrs_with_type(inode, ni, path,
809                                                 params->unhashed_blobs,
810                                                 volume, AT_REPARSE_POINT);
811                 if (ret)
812                         goto out;
813
814                 warn_special_reparse_points(inode, params, volume);
815         }
816
817         /* Load the object ID.  */
818         ret = load_object_id(ni, inode);
819         if (ret) {
820                 ERROR_WITH_ERRNO("Error reading object ID of \"%s\"", path);
821                 goto out;
822         }
823
824         /* Scan the data streams.
825          *
826          * Note: directories should not have an unnamed data stream, but they
827          * may have named data streams.  Nondirectories (including reparse
828          * points) can have an unnamed data stream as well as named data
829          * streams.  */
830         ret = scan_ntfs_attrs_with_type(inode, ni, path, params->unhashed_blobs,
831                                         volume, AT_DATA);
832         if (ret)
833                 goto out;
834
835         /* Reparse-point fixups are a no-op because in NTFS-3G capture mode we
836          * only allow capturing an entire volume. */
837         if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
838             inode_is_symlink(inode))
839                 inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED;
840
841         if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)) {
842                 ret = get_security_descriptor(ni, inode, volume->vol,
843                                               params->sd_set);
844                 if (ret) {
845                         ERROR_WITH_ERRNO("Error reading security descriptor "
846                                          "of \"%s\"", path);
847                         goto out;
848                 }
849         }
850
851         if (inode_is_directory(inode)) {
852                 ret = ntfs_3g_recurse_directory(ni, root, volume, params);
853                 if (ret)
854                         goto out;
855         }
856
857 out_progress:
858         if (root == NULL)
859                 ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
860         else
861                 ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_OK, inode);
862 out:
863         if (ni)
864                 ntfs_inode_close(ni);
865         if (unlikely(ret)) {
866                 free_dentry_tree(root, params->blob_table);
867                 root = NULL;
868                 ret = report_scan_error(params, ret);
869         }
870         *root_ret = root;
871         return ret;
872 }
873
874 int
875 ntfs_3g_build_dentry_tree(struct wim_dentry **root_ret,
876                           const char *device, struct scan_params *params)
877 {
878         struct ntfs_volume_wrapper *volume;
879         ntfs_volume *vol;
880         int ret;
881
882         volume = CALLOC(1, sizeof(struct ntfs_volume_wrapper));
883         if (!volume)
884                 return WIMLIB_ERR_NOMEM;
885
886         vol = ntfs_mount(device, NTFS_MNT_RDONLY);
887         if (!vol) {
888                 ERROR_WITH_ERRNO("Failed to mount NTFS volume \"%s\" read-only",
889                                  device);
890                 FREE(volume);
891                 return WIMLIB_ERR_NTFS_3G;
892         }
893
894         volume->vol = vol;
895         volume->refcnt = 1;
896
897         /* Currently, libntfs-3g users that need to read security descriptors
898          * are required to call ntfs_open_secure() to open the volume's security
899          * descriptor index, "$Secure".  This is only required to work on NTFS
900          * v3.0+, as older versions did not have a security descriptor index. */
901         if (ntfs_open_secure(vol) && vol->major_ver >= 3) {
902                 ERROR_WITH_ERRNO("Unable to open security descriptor index of "
903                                  "NTFS volume \"%s\"", device);
904                 ret = WIMLIB_ERR_NTFS_3G;
905                 goto out_put_ntfs_volume;
906         }
907
908         /* We don't want to capture the special NTFS files such as $Bitmap.  Not
909          * to be confused with "hidden" or "system" files which are real files
910          * that we do need to capture.  */
911         NVolClearShowSysFiles(vol);
912
913         ret = pathbuf_init(params, "/");
914         if (ret)
915                 goto out_close_secure;
916
917         ret = ntfs_3g_build_dentry_tree_recursive(root_ret, FILE_root, "",
918                                                   FILE_NAME_POSIX, volume,
919                                                   params);
920 out_close_secure:
921         /* Undo the effects of ntfs_open_secure().  This is not yet done
922          * automatically by ntfs_umount().  But NULL out the inode to
923          * potentially be robust against future versions doing so. */
924         if (vol->secure_ni) {
925                 ntfs_index_ctx_put(vol->secure_xsii);
926                 ntfs_index_ctx_put(vol->secure_xsdh);
927                 ntfs_inode_close(vol->secure_ni);
928                 vol->secure_ni = NULL;
929         }
930 out_put_ntfs_volume:
931         put_ntfs_volume(volume);
932         return ret;
933 }
934 #endif /* WITH_NTFS_3G */