]> wimlib.net Git - wimlib/blob - src/ntfs-3g_capture.c
de472327cec5df0d0f335a11afd87fea31d12d22
[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         ret = utf16le_to_tstr(name, name_nbytes, &mbs_name, &mbs_name_nbytes);
595         if (ret)
596                 goto out;
597
598         if (should_ignore_filename(mbs_name, mbs_name_nbytes))
599                 goto out_free_mbs_name;
600
601         path_len = ctx->path_len;
602         if (path_len != 1)
603                 ctx->path[path_len++] = '/';
604         memcpy(ctx->path + path_len, mbs_name, mbs_name_nbytes + 1);
605         path_len += mbs_name_nbytes;
606         child = NULL;
607         ret = ntfs_3g_build_dentry_tree_recursive(&child, mref, ctx->path,
608                                                   path_len, name_type,
609                                                   ctx->volume, ctx->params);
610         if (child)
611                 dentry_add_child(ctx->parent, child);
612 out_free_mbs_name:
613         FREE(mbs_name);
614 out:
615         ctx->ret = ret;
616         return ret;
617 }
618
619 static int
620 ntfs_3g_recurse_directory(ntfs_inode *ni, char *path, size_t path_len,
621                           struct wim_dentry *parent,
622                           struct ntfs_volume_wrapper *volume,
623                           struct capture_params *params)
624 {
625         int ret;
626         s64 pos = 0;
627         struct readdir_ctx ctx = {
628                 .parent          = parent,
629                 .path            = path,
630                 .path_len        = path_len,
631                 .dos_name_map    = { .root = NULL },
632                 .volume          = volume,
633                 .params          = params,
634                 .ret             = 0,
635         };
636         ret = ntfs_readdir(ni, &pos, &ctx, filldir);
637         path[path_len] = '\0';
638         if (unlikely(ret)) {
639                 if (ctx.ret) {
640                         /* wimlib error  */
641                         ret = ctx.ret;
642                 } else {
643                         /* error from ntfs_readdir() itself  */
644                         ERROR_WITH_ERRNO("Error reading directory \"%s\"", path);
645                         ret = WIMLIB_ERR_NTFS_3G;
646                 }
647         } else {
648                 struct wim_dentry *child;
649
650                 ret = 0;
651                 for_dentry_child(child, parent) {
652                         ret = set_dentry_dos_name(child, &ctx.dos_name_map);
653                         if (ret)
654                                 break;
655                 }
656         }
657         destroy_dos_name_map(&ctx.dos_name_map);
658         return ret;
659 }
660
661 static int
662 ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_ret,
663                                     const MFT_REF mref,
664                                     char *path,
665                                     size_t path_len,
666                                     int name_type,
667                                     struct ntfs_volume_wrapper *volume,
668                                     struct capture_params *params)
669 {
670         u32 attributes;
671         int ret;
672         struct wim_dentry *root = NULL;
673         struct wim_inode *inode = NULL;
674         ntfs_inode *ni = NULL;
675
676         ret = try_exclude(path, params);
677         if (unlikely(ret < 0)) /* Excluded? */
678                 goto out_progress;
679         if (unlikely(ret > 0)) /* Error? */
680                 goto out;
681
682         ni = ntfs_inode_open(volume->vol, mref);
683         if (!ni) {
684                 ERROR_WITH_ERRNO("Failed to open NTFS file \"%s\"", path);
685                 ret = WIMLIB_ERR_NTFS_3G;
686                 goto out;
687         }
688
689         /* Get file attributes */
690         ret = ntfs_get_ntfs_attrib(ni, (char*)&attributes, sizeof(attributes));
691         if (ret != sizeof(attributes)) {
692                 ERROR_WITH_ERRNO("Failed to get NTFS attributes from \"%s\"", path);
693                 ret = WIMLIB_ERR_NTFS_3G;
694                 goto out;
695         }
696
697         if (unlikely(attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
698                 if (params->add_flags & WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE)
699                 {
700                         ERROR("Can't archive \"%s\" because NTFS-3g capture mode "
701                               "does not support encrypted files and directories", path);
702                         ret = WIMLIB_ERR_UNSUPPORTED_FILE;
703                         goto out;
704                 }
705                 params->progress.scan.cur_path = path;
706                 ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL);
707                 goto out;
708         }
709
710         /* Create a WIM dentry with an associated inode, which may be shared */
711         ret = inode_table_new_dentry(params->inode_table,
712                                      path_basename_with_len(path, path_len),
713                                      ni->mft_no, 0, false, &root);
714         if (ret)
715                 goto out;
716
717         if (name_type & FILE_NAME_WIN32) /* Win32 or Win32+DOS name (rather than POSIX) */
718                 root->d_is_win32_name = 1;
719
720         inode = root->d_inode;
721
722         if (inode->i_nlink > 1) {
723                 /* Shared inode; nothing more to do */
724                 goto out_progress;
725         }
726
727         inode->i_creation_time    = le64_to_cpu(ni->creation_time);
728         inode->i_last_write_time  = le64_to_cpu(ni->last_data_change_time);
729         inode->i_last_access_time = le64_to_cpu(ni->last_access_time);
730         inode->i_attributes       = attributes;
731
732         if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
733                 /* Scan the reparse point stream.  */
734                 ret = scan_ntfs_attrs_with_type(inode, ni, path, path_len,
735                                                 params->unhashed_blobs,
736                                                 volume, AT_REPARSE_POINT);
737                 if (ret)
738                         goto out;
739         }
740
741         /* Scan the data streams.
742          *
743          * Note: directories should not have an unnamed data stream, but they
744          * may have named data streams.  Nondirectories (including reparse
745          * points) can have an unnamed data stream as well as named data
746          * streams.  */
747         ret = scan_ntfs_attrs_with_type(inode, ni, path, path_len,
748                                         params->unhashed_blobs,
749                                         volume, AT_DATA);
750         if (ret)
751                 goto out;
752
753         /* Reparse-point fixups are a no-op because in NTFS-3g capture mode we
754          * only allow capturing an entire volume. */
755         if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
756             inode_is_symlink(inode))
757                 inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED;
758
759         if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)) {
760                 ret = get_security_descriptor(ni, inode, volume->vol,
761                                               params->sd_set);
762                 if (ret) {
763                         ERROR_WITH_ERRNO("Error reading security descriptor "
764                                          "of \"%s\"", path);
765                         goto out;
766                 }
767         }
768
769         if (inode_is_directory(inode)) {
770                 ret = ntfs_3g_recurse_directory(ni, path, path_len, root,
771                                                 volume, params);
772                 if (ret)
773                         goto out;
774         }
775
776 out_progress:
777         params->progress.scan.cur_path = path;
778         if (root == NULL)
779                 ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
780         else
781                 ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_OK, inode);
782 out:
783         if (ni)
784                 ntfs_inode_close(ni);
785         if (unlikely(ret)) {
786                 free_dentry_tree(root, params->blob_table);
787                 root = NULL;
788                 ret = report_capture_error(params, ret, path);
789         }
790         *root_ret = root;
791         return ret;
792 }
793
794 int
795 ntfs_3g_build_dentry_tree(struct wim_dentry **root_ret,
796                           const char *device,
797                           struct capture_params *params)
798 {
799         struct ntfs_volume_wrapper *volume;
800         ntfs_volume *vol;
801         char *path;
802         int ret;
803
804         volume = MALLOC(sizeof(struct ntfs_volume_wrapper));
805         if (!volume)
806                 return WIMLIB_ERR_NOMEM;
807
808         /* NTFS-3g 2013 renamed the "read-only" mount flag from MS_RDONLY to
809          * NTFS_MNT_RDONLY.
810          *
811          * Unfortunately we can't check for defined(NTFS_MNT_RDONLY) because
812          * NTFS_MNT_RDONLY is an enumerated constant.  Also, the NTFS-3g headers
813          * don't seem to contain any explicit version information.  So we have
814          * to rely on a test done at configure time to detect whether
815          * NTFS_MNT_RDONLY should be used.  */
816 #ifdef HAVE_NTFS_MNT_RDONLY
817         /* NTFS-3g 2013 */
818         vol = ntfs_mount(device, NTFS_MNT_RDONLY);
819 #elif defined(MS_RDONLY)
820         /* NTFS-3g 2011, 2012 */
821         vol = ntfs_mount(device, MS_RDONLY);
822 #else
823   #error "Can't find NTFS_MNT_RDONLY or MS_RDONLY flags"
824 #endif
825         if (!vol) {
826                 ERROR_WITH_ERRNO("Failed to mount NTFS volume \"%s\" read-only",
827                                  device);
828                 FREE(volume);
829                 return WIMLIB_ERR_NTFS_3G;
830         }
831
832         volume->vol = vol;
833         volume->refcnt = 1;
834
835         ntfs_open_secure(vol);
836
837         /* We don't want to capture the special NTFS files such as $Bitmap.  Not
838          * to be confused with "hidden" or "system" files which are real files
839          * that we do need to capture.  */
840         NVolClearShowSysFiles(vol);
841
842         /* Currently we assume that all the paths fit into this length and there
843          * is no check for overflow.  */
844         path = MALLOC(32768);
845         if (!path) {
846                 ret = WIMLIB_ERR_NOMEM;
847                 goto out_put_ntfs_volume;
848         }
849
850         path[0] = '/';
851         path[1] = '\0';
852         ret = ntfs_3g_build_dentry_tree_recursive(root_ret, FILE_root, path, 1,
853                                                   FILE_NAME_POSIX, volume,
854                                                   params);
855         FREE(path);
856 out_put_ntfs_volume:
857         ntfs_index_ctx_put(vol->secure_xsii);
858         ntfs_index_ctx_put(vol->secure_xsdh);
859         ntfs_inode_close(vol->secure_ni);
860         put_ntfs_volume(volume);
861         return ret;
862 }
863 #endif /* WITH_NTFS_3G */