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