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