]> wimlib.net Git - wimlib/blob - src/ntfs-capture.c
Remove link_dentry()
[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                              u32 *reparse_tag_ret)
208 {
209         s64 pos = 0;
210         s64 bytes_remaining;
211         char buf[4096];
212         ntfs_attr *na;
213         SHA_CTX ctx;
214
215         na = ntfs_attr_open(ni, ar->type, attr_record_name(ar),
216                             ar->name_length);
217         if (!na) {
218                 ERROR_WITH_ERRNO("Failed to open NTFS attribute");
219                 return WIMLIB_ERR_NTFS_3G;
220         }
221
222         bytes_remaining = na->data_size;
223         sha1_init(&ctx);
224
225         DEBUG2("Calculating SHA1 message digest (%"PRIu64" bytes)",
226                bytes_remaining);
227
228         while (bytes_remaining) {
229                 s64 to_read = min(bytes_remaining, sizeof(buf));
230                 if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
231                         ERROR_WITH_ERRNO("Error reading NTFS attribute");
232                         return WIMLIB_ERR_NTFS_3G;
233                 }
234                 if (bytes_remaining == na->data_size && reparse_tag_ret)
235                         *reparse_tag_ret = le32_to_cpu(*(u32*)buf);
236                 sha1_update(&ctx, buf, to_read);
237                 pos += to_read;
238                 bytes_remaining -= to_read;
239         }
240         sha1_final(md, &ctx);
241         ntfs_attr_close(na);
242         return 0;
243 }
244
245 /* Load the streams from a WIM file or reparse point in the NTFS volume into the
246  * WIM lookup table */
247 static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni,
248                                 char path[], size_t path_len,
249                                 struct lookup_table *lookup_table,
250                                 ntfs_volume **ntfs_vol_p,
251                                 ATTR_TYPES type)
252 {
253         ntfs_attr_search_ctx *actx;
254         u8 attr_hash[SHA1_HASH_SIZE];
255         struct ntfs_location *ntfs_loc = NULL;
256         int ret = 0;
257         struct lookup_table_entry *lte;
258
259         DEBUG2("Capturing NTFS data streams from `%s'", path);
260
261         /* Get context to search the streams of the NTFS file. */
262         actx = ntfs_attr_get_search_ctx(ni, NULL);
263         if (!actx) {
264                 ERROR_WITH_ERRNO("Cannot get NTFS attribute search "
265                                  "context");
266                 return WIMLIB_ERR_NTFS_3G;
267         }
268
269         /* Capture each data stream or reparse data stream. */
270         while (!ntfs_attr_lookup(type, NULL, 0,
271                                  CASE_SENSITIVE, 0, NULL, 0, actx))
272         {
273                 char *stream_name_utf8;
274                 u32 reparse_tag;
275                 u64 data_size = ntfs_get_attribute_value_length(actx->attr);
276                 u64 name_length = actx->attr->name_length;
277
278                 if (data_size == 0) {
279                         if (errno != 0) {
280                                 ERROR_WITH_ERRNO("Failed to get size of attribute of "
281                                                  "`%s'", path);
282                                 ret = WIMLIB_ERR_NTFS_3G;
283                                 goto out_put_actx;
284                         }
285                         /* Empty stream.  No lookup table entry is needed. */
286                         lte = NULL;
287                 } else {
288                         if (type == AT_REPARSE_POINT && data_size < 8) {
289                                 ERROR("`%s': reparse point buffer too small",
290                                       path);
291                                 ret = WIMLIB_ERR_NTFS_3G;
292                                 goto out_put_actx;
293                         }
294                         /* Checksum the stream. */
295                         ret = ntfs_attr_sha1sum(ni, actx->attr, attr_hash, &reparse_tag);
296                         if (ret != 0)
297                                 goto out_put_actx;
298
299                         /* Make a lookup table entry for the stream, or use an existing
300                          * one if there's already an identical stream. */
301                         lte = __lookup_resource(lookup_table, attr_hash);
302                         ret = WIMLIB_ERR_NOMEM;
303                         if (lte) {
304                                 lte->refcnt++;
305                         } else {
306                                 ntfs_loc = CALLOC(1, sizeof(*ntfs_loc));
307                                 if (!ntfs_loc)
308                                         goto out_put_actx;
309                                 ntfs_loc->ntfs_vol_p = ntfs_vol_p;
310                                 ntfs_loc->path_utf8 = MALLOC(path_len + 1);
311                                 if (!ntfs_loc->path_utf8)
312                                         goto out_free_ntfs_loc;
313                                 memcpy(ntfs_loc->path_utf8, path, path_len + 1);
314                                 if (name_length) {
315                                         ntfs_loc->stream_name_utf16 = MALLOC(name_length * 2);
316                                         if (!ntfs_loc->stream_name_utf16)
317                                                 goto out_free_ntfs_loc;
318                                         memcpy(ntfs_loc->stream_name_utf16,
319                                                attr_record_name(actx->attr),
320                                                actx->attr->name_length * 2);
321                                         ntfs_loc->stream_name_utf16_num_chars = name_length;
322                                 }
323
324                                 lte = new_lookup_table_entry();
325                                 if (!lte)
326                                         goto out_free_ntfs_loc;
327                                 lte->ntfs_loc = ntfs_loc;
328                                 lte->resource_location = RESOURCE_IN_NTFS_VOLUME;
329                                 if (type == AT_REPARSE_POINT) {
330                                         dentry->d_inode->reparse_tag = reparse_tag;
331                                         ntfs_loc->is_reparse_point = true;
332                                         lte->resource_entry.original_size = data_size - 8;
333                                         lte->resource_entry.size = data_size - 8;
334                                 } else {
335                                         ntfs_loc->is_reparse_point = false;
336                                         lte->resource_entry.original_size = data_size;
337                                         lte->resource_entry.size = data_size;
338                                 }
339                                 ntfs_loc = NULL;
340                                 DEBUG("Add resource for `%s' (size = %zu)",
341                                       dentry->file_name_utf8,
342                                       lte->resource_entry.original_size);
343                                 copy_hash(lte->hash, attr_hash);
344                                 lookup_table_insert(lookup_table, lte);
345                         }
346                 }
347                 if (name_length == 0) {
348                         /* Unnamed data stream.  Put the reference to it in the
349                          * dentry's inode. */
350                         if (dentry->d_inode->lte) {
351                                 ERROR("Found two un-named data streams for "
352                                       "`%s'", path);
353                                 ret = WIMLIB_ERR_NTFS_3G;
354                                 goto out_free_lte;
355                         }
356                         dentry->d_inode->lte = lte;
357                 } else {
358                         /* Named data stream.  Put the reference to it in the
359                          * alternate data stream entries */
360                         struct ads_entry *new_ads_entry;
361                         size_t stream_name_utf8_len;
362                         stream_name_utf8 = utf16_to_utf8((const char*)attr_record_name(actx->attr),
363                                                          name_length * 2,
364                                                          &stream_name_utf8_len);
365                         if (!stream_name_utf8)
366                                 goto out_free_lte;
367                         new_ads_entry = inode_add_ads(dentry->d_inode, stream_name_utf8);
368                         FREE(stream_name_utf8);
369                         if (!new_ads_entry)
370                                 goto out_free_lte;
371
372                         wimlib_assert(new_ads_entry->stream_name_len == name_length * 2);
373
374                         new_ads_entry->lte = lte;
375                 }
376         }
377         ret = 0;
378         goto out_put_actx;
379 out_free_lte:
380         free_lookup_table_entry(lte);
381 out_free_ntfs_loc:
382         if (ntfs_loc) {
383                 FREE(ntfs_loc->path_utf8);
384                 FREE(ntfs_loc->stream_name_utf16);
385                 FREE(ntfs_loc);
386         }
387 out_put_actx:
388         ntfs_attr_put_search_ctx(actx);
389         if (ret == 0)
390                 DEBUG2("Successfully captured NTFS streams from `%s'", path);
391         else
392                 ERROR("Failed to capture NTFS streams from `%s", path);
393         return ret;
394 }
395
396 struct readdir_ctx {
397         struct dentry       *parent;
398         ntfs_inode          *dir_ni;
399         char                *path;
400         size_t               path_len;
401         struct lookup_table *lookup_table;
402         struct sd_set       *sd_set;
403         const struct capture_config *config;
404         ntfs_volume        **ntfs_vol_p;
405         int                  flags;
406 };
407
408 static int
409 build_dentry_tree_ntfs_recursive(struct dentry **root_p, ntfs_inode *dir_ni,
410                                  ntfs_inode *ni, char path[], size_t path_len,
411                                  int name_type,
412                                  struct lookup_table *lookup_table,
413                                  struct sd_set *sd_set,
414                                  const struct capture_config *config,
415                                  ntfs_volume **ntfs_vol_p,
416                                  int flags);
417
418 static int wim_ntfs_capture_filldir(void *dirent, const ntfschar *name,
419                                     const int name_len, const int name_type,
420                                     const s64 pos, const MFT_REF mref,
421                                     const unsigned dt_type)
422 {
423         struct readdir_ctx *ctx;
424         size_t utf8_name_len;
425         char *utf8_name;
426         struct dentry *child = NULL;
427         int ret;
428         size_t path_len;
429
430         if (name_type == FILE_NAME_DOS)
431                 return 0;
432
433         ret = -1;
434
435         utf8_name = utf16_to_utf8((const char*)name, name_len * 2,
436                                   &utf8_name_len);
437         if (!utf8_name)
438                 goto out;
439
440         if (utf8_name[0] == '.' &&
441              (utf8_name[1] == '\0' ||
442               (utf8_name[1] == '.' && utf8_name[2] == '\0'))) {
443                 ret = 0;
444                 goto out_free_utf8_name;
445         }
446
447         ctx = dirent;
448
449         ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref);
450         if (!ni) {
451                 ERROR_WITH_ERRNO("Failed to open NTFS inode");
452                 goto out_free_utf8_name;
453         }
454         path_len = ctx->path_len;
455         if (path_len != 1)
456                 ctx->path[path_len++] = '/';
457         memcpy(ctx->path + path_len, utf8_name, utf8_name_len + 1);
458         path_len += utf8_name_len;
459         ret = build_dentry_tree_ntfs_recursive(&child, ctx->dir_ni,
460                                                ni, ctx->path, path_len, name_type,
461                                                ctx->lookup_table, ctx->sd_set,
462                                                ctx->config, ctx->ntfs_vol_p,
463                                                ctx->flags);
464
465         if (child)
466                 dentry_add_child(ctx->parent, child);
467
468         ntfs_inode_close(ni);
469 out_free_utf8_name:
470         FREE(utf8_name);
471 out:
472         return ret;
473 }
474
475 static int change_dentry_short_name(struct dentry *dentry,
476                                     const char short_name_utf8[],
477                                     int short_name_utf8_len)
478 {
479         size_t short_name_utf16_len;
480         char *short_name_utf16;
481         short_name_utf16 = utf8_to_utf16(short_name_utf8, short_name_utf8_len,
482                                          &short_name_utf16_len);
483         if (!short_name_utf16) {
484                 ERROR_WITH_ERRNO("Failed to convert short name to UTF-16");
485                 return WIMLIB_ERR_NOMEM;
486         }
487         dentry->short_name = short_name_utf16;
488         dentry->short_name_len = short_name_utf16_len;
489         return 0;
490 }
491
492 /* Recursively build a WIM dentry tree corresponding to a NTFS volume.
493  * At the same time, update the WIM lookup table with lookup table entries for
494  * the NTFS streams, and build an array of security descriptors.
495  */
496 static int build_dentry_tree_ntfs_recursive(struct dentry **root_p,
497                                             ntfs_inode *dir_ni,
498                                             ntfs_inode *ni,
499                                             char path[],
500                                             size_t path_len,
501                                             int name_type,
502                                             struct lookup_table *lookup_table,
503                                             struct sd_set *sd_set,
504                                             const struct capture_config *config,
505                                             ntfs_volume **ntfs_vol_p,
506                                             int flags)
507 {
508         u32 attributes;
509         int mrec_flags;
510         int ret;
511         char dos_name_utf8[64];
512         struct dentry *root;
513
514         mrec_flags = ni->mrec->flags;
515         struct SECURITY_CONTEXT ctx;
516         memset(&ctx, 0, sizeof(ctx));
517         ctx.vol = ni->vol;
518         ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ATTRIB,
519                                          ni, dir_ni, (char *)&attributes,
520                                          sizeof(u32));
521         if (ret != 4) {
522                 ERROR_WITH_ERRNO("Failed to get NTFS attributes from `%s'",
523                                  path);
524                 return WIMLIB_ERR_NTFS_3G;
525         }
526
527         if (exclude_path(path, config, false)) {
528                 if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) {
529                         const char *file_type;
530                         if (attributes & MFT_RECORD_IS_DIRECTORY)
531                                 file_type = "directory";
532                         else
533                                 file_type = "file";
534                         printf("Excluding %s `%s' from capture\n",
535                                file_type, path);
536                 }
537                 *root_p = NULL;
538                 return 0;
539         }
540
541         if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
542                 printf("Scanning `%s'\n", path);
543
544         root = new_dentry_with_timeless_inode(path_basename(path));
545         if (!root)
546                 return WIMLIB_ERR_NOMEM;
547         *root_p = root;
548
549         if (dir_ni && (name_type == FILE_NAME_WIN32_AND_DOS
550                        || name_type == FILE_NAME_WIN32))
551         {
552                 ret = ntfs_get_ntfs_dos_name(ni, dir_ni, dos_name_utf8,
553                                              sizeof(dos_name_utf8));
554                 if (ret > 0) {
555                         DEBUG("Changing short name of `%s'", path);
556                         ret = change_dentry_short_name(root, dos_name_utf8,
557                                                        ret);
558                         if (ret != 0)
559                                 return ret;
560                 } else {
561                 #ifdef ENODATA
562                         if (errno != ENODATA) {
563                                 ERROR_WITH_ERRNO("Error getting DOS name "
564                                                  "of `%s'", path);
565                                 return WIMLIB_ERR_NTFS_3G;
566                         }
567                 #endif
568                 }
569         }
570
571         root->d_inode->creation_time    = le64_to_cpu(ni->creation_time);
572         root->d_inode->last_write_time  = le64_to_cpu(ni->last_data_change_time);
573         root->d_inode->last_access_time = le64_to_cpu(ni->last_access_time);
574         root->d_inode->attributes       = le32_to_cpu(attributes);
575         root->d_inode->ino              = ni->mft_no;
576         root->d_inode->resolved         = true;
577
578         if (attributes & FILE_ATTR_REPARSE_POINT) {
579                 /* Junction point, symbolic link, or other reparse point */
580                 ret = capture_ntfs_streams(root, ni, path, path_len,
581                                            lookup_table, ntfs_vol_p,
582                                            AT_REPARSE_POINT);
583         } else if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
584
585                 /* Normal directory */
586                 s64 pos = 0;
587                 struct readdir_ctx ctx = {
588                         .parent       = root,
589                         .dir_ni       = ni,
590                         .path         = path,
591                         .path_len     = path_len,
592                         .lookup_table = lookup_table,
593                         .sd_set       = sd_set,
594                         .config       = config,
595                         .ntfs_vol_p   = ntfs_vol_p,
596                         .flags        = flags,
597                 };
598                 ret = ntfs_readdir(ni, &pos, &ctx, wim_ntfs_capture_filldir);
599                 if (ret != 0) {
600                         ERROR_WITH_ERRNO("ntfs_readdir()");
601                         ret = WIMLIB_ERR_NTFS_3G;
602                 }
603         } else {
604                 /* Normal file */
605                 ret = capture_ntfs_streams(root, ni, path, path_len,
606                                            lookup_table, ntfs_vol_p,
607                                            AT_DATA);
608         }
609         if (ret != 0)
610                 return ret;
611
612         char _sd[1];
613         char *sd = _sd;
614         errno = 0;
615         ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL,
616                                          ni, dir_ni, sd,
617                                          sizeof(sd));
618         if (ret > sizeof(sd)) {
619                 sd = alloca(ret);
620                 ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL,
621                                                  ni, dir_ni, sd, ret);
622         }
623         if (ret > 0) {
624                 root->d_inode->security_id = sd_set_add_sd(sd_set, sd, ret);
625                 if (root->d_inode->security_id == -1) {
626                         ERROR("Out of memory");
627                         return WIMLIB_ERR_NOMEM;
628                 }
629                 DEBUG("Added security ID = %u for `%s'",
630                       root->d_inode->security_id, path);
631                 ret = 0;
632         } else if (ret < 0) {
633                 ERROR_WITH_ERRNO("Failed to get security information from "
634                                  "`%s'", path);
635                 ret = WIMLIB_ERR_NTFS_3G;
636         } else {
637                 root->d_inode->security_id = -1;
638                 DEBUG("No security ID for `%s'", path);
639         }
640         return ret;
641 }
642
643 static int build_dentry_tree_ntfs(struct dentry **root_p,
644                                   const char *device,
645                                   struct lookup_table *lookup_table,
646                                   struct wim_security_data *sd,
647                                   const struct capture_config *config,
648                                   int flags,
649                                   void *extra_arg)
650 {
651         ntfs_volume *vol;
652         ntfs_inode *root_ni;
653         int ret = 0;
654         struct sd_set sd_set = {
655                 .sd = sd,
656                 .root = NULL,
657         };
658         ntfs_volume **ntfs_vol_p = extra_arg;
659
660         DEBUG("Mounting NTFS volume `%s' read-only", device);
661
662         vol = ntfs_mount(device, MS_RDONLY);
663         if (!vol) {
664                 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
665                                  device);
666                 return WIMLIB_ERR_NTFS_3G;
667         }
668         ntfs_open_secure(vol);
669
670         /* We don't want to capture the special NTFS files such as $Bitmap.  Not
671          * to be confused with "hidden" or "system" files which are real files
672          * that we do need to capture.  */
673         NVolClearShowSysFiles(vol);
674
675         DEBUG("Opening root NTFS dentry");
676         root_ni = ntfs_inode_open(vol, FILE_root);
677         if (!root_ni) {
678                 ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
679                                  "`%s'", device);
680                 ret = WIMLIB_ERR_NTFS_3G;
681                 goto out;
682         }
683
684         /* Currently we assume that all the UTF-8 paths fit into this length and
685          * there is no check for overflow. */
686         char *path = MALLOC(32768);
687         if (!path) {
688                 ERROR("Could not allocate memory for NTFS pathname");
689                 goto out_cleanup;
690         }
691
692         path[0] = '/';
693         path[1] = '\0';
694         ret = build_dentry_tree_ntfs_recursive(root_p, NULL, root_ni, path, 1,
695                                                FILE_NAME_POSIX, lookup_table,
696                                                &sd_set, config, ntfs_vol_p,
697                                                flags);
698 out_cleanup:
699         FREE(path);
700         ntfs_inode_close(root_ni);
701         destroy_sd_set(&sd_set);
702
703 out:
704         if (ret) {
705                 if (ntfs_umount(vol, FALSE) != 0) {
706                         ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'",
707                                          device);
708                         if (ret == 0)
709                                 ret = WIMLIB_ERR_NTFS_3G;
710                 }
711         } else {
712                 /* We need to leave the NTFS volume mounted so that we can read
713                  * the NTFS files again when we are actually writing the WIM */
714                 *ntfs_vol_p = vol;
715         }
716         return ret;
717 }
718
719
720
721 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
722                                                 const char *device,
723                                                 const char *name,
724                                                 const char *config_str,
725                                                 size_t config_len,
726                                                 int flags)
727 {
728         if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
729                 ERROR("Cannot dereference files when capturing directly from NTFS");
730                 return WIMLIB_ERR_INVALID_PARAM;
731         }
732         return do_add_image(w, device, name, config_str, config_len, flags,
733                             build_dentry_tree_ntfs, &w->ntfs_vol);
734 }
735
736 #else /* WITH_NTFS_3G */
737 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
738                                                 const char *device,
739                                                 const char *name,
740                                                 const char *config_str,
741                                                 size_t config_len,
742                                                 int flags)
743 {
744         ERROR("wimlib was compiled without support for NTFS-3g, so");
745         ERROR("we cannot capture a WIM image directly from a NTFS volume");
746         return WIMLIB_ERR_UNSUPPORTED;
747 }
748 #endif /* WITH_NTFS_3G */