]> wimlib.net Git - wimlib/blob - src/ntfs-capture.c
fb670dc0183845e08f8e2349bc1c8b4cc6434295
[wimlib] / src / ntfs-capture.c
1 /*
2  * ntfs-capture.c
3  *
4  * Capture a WIM image from a NTFS volume.  We capture everything we can,
5  * including security data and alternate data streams.
6  */
7
8 /*
9  * Copyright (C) 2012 Eric Biggers
10  *
11  * This file is part of wimlib, a library for working with WIM files.
12  *
13  * wimlib is free software; you can redistribute it and/or modify it under the
14  * terms of the GNU General Public License as published by the Free
15  * Software Foundation; either version 3 of the License, or (at your option)
16  * any later version.
17  *
18  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20  * A PARTICULAR PURPOSE. See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with wimlib; if not, see http://www.gnu.org/licenses/.
25  */
26
27
28 #include "config.h"
29
30 #ifdef WITH_NTFS_3G
31 #include <ntfs-3g/endians.h>
32 #include <ntfs-3g/types.h>
33 #endif
34
35 #include "wimlib_internal.h"
36
37
38 #ifdef WITH_NTFS_3G
39 #include "dentry.h"
40 #include "lookup_table.h"
41 #include "io.h"
42 #include <ntfs-3g/layout.h>
43 #include <ntfs-3g/acls.h>
44 #include <ntfs-3g/attrib.h>
45 #include <ntfs-3g/misc.h>
46 #include <ntfs-3g/reparse.h>
47 #include <ntfs-3g/security.h> /* security.h before xattrs.h */
48 #include <ntfs-3g/xattrs.h>
49 #include <ntfs-3g/volume.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <errno.h>
53
54 /* Structure that allows searching the security descriptors by SHA1 message
55  * digest. */
56 struct sd_set {
57         struct wim_security_data *sd;
58         struct sd_node *root;
59 };
60
61 /* Binary tree node of security descriptors, indexed by the @hash field. */
62 struct sd_node {
63         int security_id;
64         u8 hash[SHA1_HASH_SIZE];
65         struct sd_node *left;
66         struct sd_node *right;
67 };
68
69 static void free_sd_tree(struct sd_node *root)
70 {
71         if (root) {
72                 free_sd_tree(root->left);
73                 free_sd_tree(root->right);
74                 FREE(root);
75         }
76 }
77 /* Frees a security descriptor index set. */
78 static void destroy_sd_set(struct sd_set *sd_set)
79 {
80         free_sd_tree(sd_set->root);
81 }
82
83 /* Inserts a a new node into the security descriptor index tree. */
84 static void insert_sd_node(struct sd_node *new, struct sd_node *root)
85 {
86         int cmp = hashes_cmp(new->hash, root->hash);
87         if (cmp < 0) {
88                 if (root->left)
89                         insert_sd_node(new, root->left);
90                 else
91                         root->left = new;
92         } else if (cmp > 0) {
93                 if (root->right)
94                         insert_sd_node(new, root->right);
95                 else
96                         root->right = new;
97         } else {
98                 wimlib_assert(0);
99         }
100 }
101
102 /* Returns the security ID of the security data having a SHA1 message digest of
103  * @hash in the security descriptor index tree rooted at @root.
104  *
105  * If not found, return -1. */
106 static int lookup_sd(const u8 hash[SHA1_HASH_SIZE], struct sd_node *root)
107 {
108         int cmp;
109         if (!root)
110                 return -1;
111         cmp = hashes_cmp(hash, root->hash);
112         if (cmp < 0)
113                 return lookup_sd(hash, root->left);
114         else if (cmp > 0)
115                 return lookup_sd(hash, root->right);
116         else
117                 return root->security_id;
118 }
119
120 /*
121  * Adds a security descriptor to the indexed security descriptor set as well as
122  * the corresponding `struct wim_security_data', and returns the new security
123  * ID; or, if there is an existing security descriptor that is the same, return
124  * the security ID for it.  If a new security descriptor cannot be allocated,
125  * return -1.
126  */
127 static int sd_set_add_sd(struct sd_set *sd_set, const char descriptor[],
128                          size_t size)
129 {
130         u8 hash[SHA1_HASH_SIZE];
131         int security_id;
132         struct sd_node *new;
133         u8 **descriptors;
134         u64 *sizes;
135         u8 *descr_copy;
136         struct wim_security_data *sd;
137
138         sha1_buffer((const u8*)descriptor, size, hash);
139
140         security_id = lookup_sd(hash, sd_set->root);
141         if (security_id >= 0)
142                 return security_id;
143
144         new = MALLOC(sizeof(*new));
145         if (!new)
146                 goto out;
147         descr_copy = MALLOC(size);
148         if (!descr_copy)
149                 goto out_free_node;
150
151         sd = sd_set->sd;
152
153         memcpy(descr_copy, descriptor, size);
154         new->security_id = sd->num_entries;
155         new->left = NULL;
156         new->right = NULL;
157         copy_hash(new->hash, hash);
158
159
160         descriptors = REALLOC(sd->descriptors,
161                               (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
162         if (!descriptors)
163                 goto out_free_descr;
164         sd->descriptors = descriptors;
165         sizes = REALLOC(sd->sizes,
166                         (sd->num_entries + 1) * sizeof(sd->sizes[0]));
167         if (!sizes)
168                 goto out_free_descr;
169         sd->sizes = sizes;
170         sd->descriptors[sd->num_entries] = descr_copy;
171         sd->sizes[sd->num_entries] = size;
172         sd->num_entries++;
173         DEBUG("There are now %d security descriptors", sd->num_entries);
174         sd->total_length += size + sizeof(sd->sizes[0]);
175
176         if (sd_set->root)
177                 insert_sd_node(new, sd_set->root);
178         else
179                 sd_set->root = new;
180         return new->security_id;
181 out_free_descr:
182         FREE(descr_copy);
183 out_free_node:
184         FREE(new);
185 out:
186         return -1;
187 }
188
189 static inline ntfschar *attr_record_name(ATTR_RECORD *ar)
190 {
191         return (ntfschar*)((u8*)ar + le16_to_cpu(ar->name_offset));
192 }
193
194 /* Calculates the SHA1 message digest of a NTFS attribute.
195  *
196  * @ni:  The NTFS inode containing the attribute.
197  * @ar:  The ATTR_RECORD describing the attribute.
198  * @md:  If successful, the returned SHA1 message digest.
199  * @reparse_tag_ret:    Optional pointer into which the first 4 bytes of the
200  *                              attribute will be written (to get the reparse
201  *                              point ID)
202  *
203  * Return 0 on success or nonzero on error.
204  */
205 static int ntfs_attr_sha1sum(ntfs_inode *ni, ATTR_RECORD *ar,
206                              u8 md[SHA1_HASH_SIZE],
207                              bool is_reparse_point,
208                              u32 *reparse_tag_ret)
209 {
210         s64 pos = 0;
211         s64 bytes_remaining;
212         char buf[BUFFER_SIZE];
213         ntfs_attr *na;
214         SHA_CTX ctx;
215
216         na = ntfs_attr_open(ni, ar->type, attr_record_name(ar),
217                             ar->name_length);
218         if (!na) {
219                 ERROR_WITH_ERRNO("Failed to open NTFS attribute");
220                 return WIMLIB_ERR_NTFS_3G;
221         }
222
223         bytes_remaining = na->data_size;
224
225         if (is_reparse_point) {
226                 if (ntfs_attr_pread(na, 0, 8, buf) != 8)
227                         goto out_error;
228                 *reparse_tag_ret = le32_to_cpu(*(u32*)buf);
229                 pos = 8;
230                 bytes_remaining -= 8;
231         }
232
233         sha1_init(&ctx);
234         while (bytes_remaining) {
235                 s64 to_read = min(bytes_remaining, sizeof(buf));
236                 if (ntfs_attr_pread(na, pos, to_read, buf) != to_read)
237                         goto out_error;
238                 sha1_update(&ctx, buf, to_read);
239                 pos += to_read;
240                 bytes_remaining -= to_read;
241         }
242         sha1_final(md, &ctx);
243         ntfs_attr_close(na);
244         return 0;
245 out_error:
246         ERROR_WITH_ERRNO("Error reading NTFS attribute");
247         return WIMLIB_ERR_NTFS_3G;
248 }
249
250 /* Load the streams from a file or reparse point in the NTFS volume into the WIM
251  * lookup table */
252 static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni,
253                                 char path[], size_t path_len,
254                                 struct lookup_table *lookup_table,
255                                 ntfs_volume **ntfs_vol_p,
256                                 ATTR_TYPES type)
257 {
258         ntfs_attr_search_ctx *actx;
259         u8 attr_hash[SHA1_HASH_SIZE];
260         struct ntfs_location *ntfs_loc = NULL;
261         int ret = 0;
262         struct lookup_table_entry *lte;
263
264         DEBUG2("Capturing NTFS data streams from `%s'", path);
265
266         /* Get context to search the streams of the NTFS file. */
267         actx = ntfs_attr_get_search_ctx(ni, NULL);
268         if (!actx) {
269                 ERROR_WITH_ERRNO("Cannot get NTFS attribute search "
270                                  "context");
271                 return WIMLIB_ERR_NTFS_3G;
272         }
273
274         /* Capture each data stream or reparse data stream. */
275         while (!ntfs_attr_lookup(type, NULL, 0,
276                                  CASE_SENSITIVE, 0, NULL, 0, actx))
277         {
278                 char *stream_name_utf8;
279                 u32 reparse_tag;
280                 u64 data_size = ntfs_get_attribute_value_length(actx->attr);
281                 u64 name_length = actx->attr->name_length;
282
283                 if (data_size == 0) {
284                         if (errno != 0) {
285                                 ERROR_WITH_ERRNO("Failed to get size of attribute of "
286                                                  "`%s'", path);
287                                 ret = WIMLIB_ERR_NTFS_3G;
288                                 goto out_put_actx;
289                         }
290                         /* Empty stream.  No lookup table entry is needed. */
291                         lte = NULL;
292                 } else {
293                         if (type == AT_REPARSE_POINT && data_size < 8) {
294                                 ERROR("`%s': reparse point buffer too small",
295                                       path);
296                                 ret = WIMLIB_ERR_NTFS_3G;
297                                 goto out_put_actx;
298                         }
299                         /* Checksum the stream. */
300                         ret = ntfs_attr_sha1sum(ni, actx->attr, attr_hash,
301                                                 type == AT_REPARSE_POINT, &reparse_tag);
302                         if (ret != 0)
303                                 goto out_put_actx;
304
305                         /* Make a lookup table entry for the stream, or use an existing
306                          * one if there's already an identical stream. */
307                         lte = __lookup_resource(lookup_table, attr_hash);
308                         ret = WIMLIB_ERR_NOMEM;
309                         if (lte) {
310                                 lte->refcnt++;
311                         } else {
312                                 ntfs_loc = CALLOC(1, sizeof(*ntfs_loc));
313                                 if (!ntfs_loc)
314                                         goto out_put_actx;
315                                 ntfs_loc->ntfs_vol_p = ntfs_vol_p;
316                                 ntfs_loc->path_utf8 = MALLOC(path_len + 1);
317                                 if (!ntfs_loc->path_utf8)
318                                         goto out_free_ntfs_loc;
319                                 memcpy(ntfs_loc->path_utf8, path, path_len + 1);
320                                 if (name_length) {
321                                         ntfs_loc->stream_name_utf16 = MALLOC(name_length * 2);
322                                         if (!ntfs_loc->stream_name_utf16)
323                                                 goto out_free_ntfs_loc;
324                                         memcpy(ntfs_loc->stream_name_utf16,
325                                                attr_record_name(actx->attr),
326                                                actx->attr->name_length * 2);
327                                         ntfs_loc->stream_name_utf16_num_chars = name_length;
328                                 }
329
330                                 lte = new_lookup_table_entry();
331                                 if (!lte)
332                                         goto out_free_ntfs_loc;
333                                 lte->ntfs_loc = ntfs_loc;
334                                 lte->resource_location = RESOURCE_IN_NTFS_VOLUME;
335                                 if (type == AT_REPARSE_POINT) {
336                                         dentry->d_inode->reparse_tag = reparse_tag;
337                                         ntfs_loc->is_reparse_point = true;
338                                         lte->resource_entry.original_size = data_size - 8;
339                                         lte->resource_entry.size = data_size - 8;
340                                 } else {
341                                         ntfs_loc->is_reparse_point = false;
342                                         lte->resource_entry.original_size = data_size;
343                                         lte->resource_entry.size = data_size;
344                                 }
345                                 ntfs_loc = NULL;
346                                 DEBUG("Add resource for `%s' (size = %zu)",
347                                       dentry->file_name_utf8,
348                                       lte->resource_entry.original_size);
349                                 copy_hash(lte->hash, attr_hash);
350                                 lookup_table_insert(lookup_table, lte);
351                         }
352                 }
353                 if (name_length == 0) {
354                         /* Unnamed data stream.  Put the reference to it in the
355                          * dentry's inode. */
356                         if (dentry->d_inode->lte) {
357                                 ERROR("Found two un-named data streams for "
358                                       "`%s'", path);
359                                 ret = WIMLIB_ERR_NTFS_3G;
360                                 goto out_free_lte;
361                         }
362                         dentry->d_inode->lte = lte;
363                 } else {
364                         /* Named data stream.  Put the reference to it in the
365                          * alternate data stream entries */
366                         struct ads_entry *new_ads_entry;
367                         size_t stream_name_utf8_len;
368                         stream_name_utf8 = utf16_to_utf8((const char*)attr_record_name(actx->attr),
369                                                          name_length * 2,
370                                                          &stream_name_utf8_len);
371                         if (!stream_name_utf8)
372                                 goto out_free_lte;
373                         new_ads_entry = inode_add_ads(dentry->d_inode, stream_name_utf8);
374                         FREE(stream_name_utf8);
375                         if (!new_ads_entry)
376                                 goto out_free_lte;
377
378                         wimlib_assert(new_ads_entry->stream_name_len == name_length * 2);
379
380                         new_ads_entry->lte = lte;
381                 }
382         }
383         ret = 0;
384         goto out_put_actx;
385 out_free_lte:
386         free_lookup_table_entry(lte);
387 out_free_ntfs_loc:
388         if (ntfs_loc) {
389                 FREE(ntfs_loc->path_utf8);
390                 FREE(ntfs_loc->stream_name_utf16);
391                 FREE(ntfs_loc);
392         }
393 out_put_actx:
394         ntfs_attr_put_search_ctx(actx);
395         if (ret == 0)
396                 DEBUG2("Successfully captured NTFS streams from `%s'", path);
397         else
398                 ERROR("Failed to capture NTFS streams from `%s", path);
399         return ret;
400 }
401
402 struct readdir_ctx {
403         struct dentry       *parent;
404         ntfs_inode          *dir_ni;
405         char                *path;
406         size_t               path_len;
407         struct lookup_table *lookup_table;
408         struct sd_set       *sd_set;
409         const struct capture_config *config;
410         ntfs_volume        **ntfs_vol_p;
411         int                  flags;
412 };
413
414 static int
415 build_dentry_tree_ntfs_recursive(struct dentry **root_p, ntfs_inode *dir_ni,
416                                  ntfs_inode *ni, char path[], size_t path_len,
417                                  int name_type,
418                                  struct lookup_table *lookup_table,
419                                  struct sd_set *sd_set,
420                                  const struct capture_config *config,
421                                  ntfs_volume **ntfs_vol_p,
422                                  int flags);
423
424 static int wim_ntfs_capture_filldir(void *dirent, const ntfschar *name,
425                                     const int name_len, const int name_type,
426                                     const s64 pos, const MFT_REF mref,
427                                     const unsigned dt_type)
428 {
429         struct readdir_ctx *ctx;
430         size_t utf8_name_len;
431         char *utf8_name;
432         struct dentry *child = NULL;
433         int ret;
434         size_t path_len;
435
436         if (name_type == FILE_NAME_DOS)
437                 return 0;
438
439         ret = -1;
440
441         utf8_name = utf16_to_utf8((const char*)name, name_len * 2,
442                                   &utf8_name_len);
443         if (!utf8_name)
444                 goto out;
445
446         if (utf8_name[0] == '.' &&
447              (utf8_name[1] == '\0' ||
448               (utf8_name[1] == '.' && utf8_name[2] == '\0'))) {
449                 ret = 0;
450                 goto out_free_utf8_name;
451         }
452
453         ctx = dirent;
454
455         ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref);
456         if (!ni) {
457                 ERROR_WITH_ERRNO("Failed to open NTFS inode");
458                 goto out_free_utf8_name;
459         }
460         path_len = ctx->path_len;
461         if (path_len != 1)
462                 ctx->path[path_len++] = '/';
463         memcpy(ctx->path + path_len, utf8_name, utf8_name_len + 1);
464         path_len += utf8_name_len;
465         ret = build_dentry_tree_ntfs_recursive(&child, ctx->dir_ni,
466                                                ni, ctx->path, path_len, name_type,
467                                                ctx->lookup_table, ctx->sd_set,
468                                                ctx->config, ctx->ntfs_vol_p,
469                                                ctx->flags);
470
471         if (child)
472                 dentry_add_child(ctx->parent, child);
473
474         ntfs_inode_close(ni);
475 out_free_utf8_name:
476         FREE(utf8_name);
477 out:
478         return ret;
479 }
480
481 static int change_dentry_short_name(struct dentry *dentry,
482                                     const char short_name_utf8[],
483                                     int short_name_utf8_len)
484 {
485         size_t short_name_utf16_len;
486         char *short_name_utf16;
487         short_name_utf16 = utf8_to_utf16(short_name_utf8, short_name_utf8_len,
488                                          &short_name_utf16_len);
489         if (!short_name_utf16) {
490                 ERROR_WITH_ERRNO("Failed to convert short name to UTF-16");
491                 return WIMLIB_ERR_NOMEM;
492         }
493         dentry->short_name = short_name_utf16;
494         dentry->short_name_len = short_name_utf16_len;
495         return 0;
496 }
497
498 /* Recursively build a WIM dentry tree corresponding to a NTFS volume.
499  * At the same time, update the WIM lookup table with lookup table entries for
500  * the NTFS streams, and build an array of security descriptors.
501  */
502 static int build_dentry_tree_ntfs_recursive(struct dentry **root_p,
503                                             ntfs_inode *dir_ni,
504                                             ntfs_inode *ni,
505                                             char path[],
506                                             size_t path_len,
507                                             int name_type,
508                                             struct lookup_table *lookup_table,
509                                             struct sd_set *sd_set,
510                                             const struct capture_config *config,
511                                             ntfs_volume **ntfs_vol_p,
512                                             int flags)
513 {
514         u32 attributes;
515         int mrec_flags;
516         int ret;
517         char dos_name_utf8[64];
518         struct dentry *root;
519
520         mrec_flags = ni->mrec->flags;
521         struct SECURITY_CONTEXT ctx;
522         memset(&ctx, 0, sizeof(ctx));
523         ctx.vol = ni->vol;
524         ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ATTRIB,
525                                          ni, dir_ni, (char *)&attributes,
526                                          sizeof(u32));
527         if (ret != 4) {
528                 ERROR_WITH_ERRNO("Failed to get NTFS attributes from `%s'",
529                                  path);
530                 return WIMLIB_ERR_NTFS_3G;
531         }
532
533         if (exclude_path(path, config, false)) {
534                 if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) {
535                         const char *file_type;
536                         if (attributes & MFT_RECORD_IS_DIRECTORY)
537                                 file_type = "directory";
538                         else
539                                 file_type = "file";
540                         printf("Excluding %s `%s' from capture\n",
541                                file_type, path);
542                 }
543                 *root_p = NULL;
544                 return 0;
545         }
546
547         if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
548                 printf("Scanning `%s'\n", path);
549
550         root = new_dentry_with_timeless_inode(path_basename(path));
551         if (!root)
552                 return WIMLIB_ERR_NOMEM;
553         *root_p = root;
554
555         if (dir_ni && (name_type == FILE_NAME_WIN32_AND_DOS
556                        || name_type == FILE_NAME_WIN32))
557         {
558                 ret = ntfs_get_ntfs_dos_name(ni, dir_ni, dos_name_utf8,
559                                              sizeof(dos_name_utf8));
560                 if (ret > 0) {
561                         DEBUG("Changing short name of `%s'", path);
562                         ret = change_dentry_short_name(root, dos_name_utf8,
563                                                        ret);
564                         if (ret != 0)
565                                 return ret;
566                 } else {
567                 #ifdef ENODATA
568                         if (errno != ENODATA) {
569                                 ERROR_WITH_ERRNO("Error getting DOS name "
570                                                  "of `%s'", path);
571                                 return WIMLIB_ERR_NTFS_3G;
572                         }
573                 #endif
574                 }
575         }
576
577         root->d_inode->creation_time    = le64_to_cpu(ni->creation_time);
578         root->d_inode->last_write_time  = le64_to_cpu(ni->last_data_change_time);
579         root->d_inode->last_access_time = le64_to_cpu(ni->last_access_time);
580         root->d_inode->attributes       = le32_to_cpu(attributes);
581         root->d_inode->ino              = ni->mft_no;
582         root->d_inode->resolved         = true;
583
584         if (attributes & FILE_ATTR_REPARSE_POINT) {
585                 /* Junction point, symbolic link, or other reparse point */
586                 ret = capture_ntfs_streams(root, ni, path, path_len,
587                                            lookup_table, ntfs_vol_p,
588                                            AT_REPARSE_POINT);
589         } else if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
590
591                 /* Normal directory */
592                 s64 pos = 0;
593                 struct readdir_ctx ctx = {
594                         .parent       = root,
595                         .dir_ni       = ni,
596                         .path         = path,
597                         .path_len     = path_len,
598                         .lookup_table = lookup_table,
599                         .sd_set       = sd_set,
600                         .config       = config,
601                         .ntfs_vol_p   = ntfs_vol_p,
602                         .flags        = flags,
603                 };
604                 ret = ntfs_readdir(ni, &pos, &ctx, wim_ntfs_capture_filldir);
605                 if (ret != 0) {
606                         ERROR_WITH_ERRNO("ntfs_readdir()");
607                         ret = WIMLIB_ERR_NTFS_3G;
608                 }
609         } else {
610                 /* Normal file */
611                 ret = capture_ntfs_streams(root, ni, path, path_len,
612                                            lookup_table, ntfs_vol_p,
613                                            AT_DATA);
614         }
615         if (ret != 0)
616                 return ret;
617
618         char _sd[1];
619         char *sd = _sd;
620         errno = 0;
621         ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL,
622                                          ni, dir_ni, sd,
623                                          sizeof(sd));
624         if (ret > sizeof(sd)) {
625                 sd = alloca(ret);
626                 ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL,
627                                                  ni, dir_ni, sd, ret);
628         }
629         if (ret > 0) {
630                 root->d_inode->security_id = sd_set_add_sd(sd_set, sd, ret);
631                 if (root->d_inode->security_id == -1) {
632                         ERROR("Out of memory");
633                         return WIMLIB_ERR_NOMEM;
634                 }
635                 DEBUG("Added security ID = %u for `%s'",
636                       root->d_inode->security_id, path);
637                 ret = 0;
638         } else if (ret < 0) {
639                 ERROR_WITH_ERRNO("Failed to get security information from "
640                                  "`%s'", path);
641                 ret = WIMLIB_ERR_NTFS_3G;
642         } else {
643                 root->d_inode->security_id = -1;
644                 DEBUG("No security ID for `%s'", path);
645         }
646         return ret;
647 }
648
649 static int build_dentry_tree_ntfs(struct dentry **root_p,
650                                   const char *device,
651                                   struct lookup_table *lookup_table,
652                                   struct wim_security_data *sd,
653                                   const struct capture_config *config,
654                                   int flags,
655                                   void *extra_arg)
656 {
657         ntfs_volume *vol;
658         ntfs_inode *root_ni;
659         int ret = 0;
660         struct sd_set sd_set = {
661                 .sd = sd,
662                 .root = NULL,
663         };
664         ntfs_volume **ntfs_vol_p = extra_arg;
665
666         DEBUG("Mounting NTFS volume `%s' read-only", device);
667
668         vol = ntfs_mount(device, MS_RDONLY);
669         if (!vol) {
670                 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
671                                  device);
672                 return WIMLIB_ERR_NTFS_3G;
673         }
674         ntfs_open_secure(vol);
675
676         /* We don't want to capture the special NTFS files such as $Bitmap.  Not
677          * to be confused with "hidden" or "system" files which are real files
678          * that we do need to capture.  */
679         NVolClearShowSysFiles(vol);
680
681         DEBUG("Opening root NTFS dentry");
682         root_ni = ntfs_inode_open(vol, FILE_root);
683         if (!root_ni) {
684                 ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
685                                  "`%s'", device);
686                 ret = WIMLIB_ERR_NTFS_3G;
687                 goto out;
688         }
689
690         /* Currently we assume that all the UTF-8 paths fit into this length and
691          * there is no check for overflow. */
692         char *path = MALLOC(32768);
693         if (!path) {
694                 ERROR("Could not allocate memory for NTFS pathname");
695                 goto out_cleanup;
696         }
697
698         path[0] = '/';
699         path[1] = '\0';
700         ret = build_dentry_tree_ntfs_recursive(root_p, NULL, root_ni, path, 1,
701                                                FILE_NAME_POSIX, lookup_table,
702                                                &sd_set, config, ntfs_vol_p,
703                                                flags);
704 out_cleanup:
705         FREE(path);
706         ntfs_inode_close(root_ni);
707         destroy_sd_set(&sd_set);
708
709 out:
710         if (ret) {
711                 if (ntfs_umount(vol, FALSE) != 0) {
712                         ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'",
713                                          device);
714                         if (ret == 0)
715                                 ret = WIMLIB_ERR_NTFS_3G;
716                 }
717         } else {
718                 /* We need to leave the NTFS volume mounted so that we can read
719                  * the NTFS files again when we are actually writing the WIM */
720                 *ntfs_vol_p = vol;
721         }
722         return ret;
723 }
724
725
726
727 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
728                                                 const char *device,
729                                                 const char *name,
730                                                 const char *config_str,
731                                                 size_t config_len,
732                                                 int flags)
733 {
734         if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
735                 ERROR("Cannot dereference files when capturing directly from NTFS");
736                 return WIMLIB_ERR_INVALID_PARAM;
737         }
738         return do_add_image(w, device, name, config_str, config_len, flags,
739                             build_dentry_tree_ntfs, &w->ntfs_vol);
740 }
741
742 #else /* WITH_NTFS_3G */
743 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
744                                                 const char *device,
745                                                 const char *name,
746                                                 const char *config_str,
747                                                 size_t config_len,
748                                                 int flags)
749 {
750         ERROR("wimlib was compiled without support for NTFS-3g, so");
751         ERROR("we cannot capture a WIM image directly from a NTFS volume");
752         return WIMLIB_ERR_UNSUPPORTED;
753 }
754 #endif /* WITH_NTFS_3G */