]> wimlib.net Git - wimlib/blob - src/ntfs-capture.c
new NTFS 3g patches
[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 #include "config.h"
28 #include "wimlib_internal.h"
29
30
31 #ifdef WITH_NTFS_3G
32 #include "dentry.h"
33 #include "lookup_table.h"
34 #include "io.h"
35 #include <ntfs-3g/layout.h>
36 #include <ntfs-3g/acls.h>
37 #include <ntfs-3g/attrib.h>
38 #include <ntfs-3g/misc.h>
39 #include <ntfs-3g/reparse.h>
40 #include <ntfs-3g/security.h>
41 #include <ntfs-3g/volume.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <errno.h>
45
46 #ifndef WITH_NEW_NTFS_3G
47 extern int ntfs_get_inode_security(ntfs_inode *ni, u32 selection, char *buf,
48                                    u32 buflen, u32 *psize);
49
50 extern u32 ntfs_get_inode_attributes(ntfs_inode *ni);
51 #endif
52
53 /* Structure that allows searching the security descriptors by SHA1 message
54  * digest. */
55 struct sd_set {
56         struct wim_security_data *sd;
57         struct sd_node *root;
58 };
59
60 /* Binary tree node of security descriptors, indexed by the @hash field. */
61 struct sd_node {
62         int security_id;
63         u8 hash[SHA1_HASH_SIZE];
64         struct sd_node *left;
65         struct sd_node *right;
66 };
67
68 static void free_sd_tree(struct sd_node *root)
69 {
70         if (root) {
71                 free_sd_tree(root->left);
72                 free_sd_tree(root->right);
73                 FREE(root);
74         }
75 }
76 /* Frees a security descriptor index set. */
77 static void destroy_sd_set(struct sd_set *sd_set)
78 {
79         free_sd_tree(sd_set->root);
80 }
81
82 /* Inserts a a new node into the security descriptor index tree. */
83 static void insert_sd_node(struct sd_node *new, struct sd_node *root)
84 {
85         int cmp = hashes_cmp(new->hash, root->hash);
86         if (cmp < 0) {
87                 if (root->left)
88                         insert_sd_node(new, root->left);
89                 else 
90                         root->left = new;
91         } else if (cmp > 0) {
92                 if (root->right)
93                         insert_sd_node(new, root->right);
94                 else 
95                         root->right = new;
96         } else {
97                 wimlib_assert(0);
98         }
99 }
100
101 /* Returns the security ID of the security data having a SHA1 message digest of
102  * @hash in the security descriptor index tree rooted at @root. 
103  *
104  * If not found, return -1. */
105 static int lookup_sd(const u8 hash[SHA1_HASH_SIZE], struct sd_node *root)
106 {
107         int cmp;
108         if (!root)
109                 return -1;
110         cmp = hashes_cmp(hash, root->hash);
111         if (cmp < 0)
112                 return lookup_sd(hash, root->left);
113         else if (cmp > 0)
114                 return lookup_sd(hash, root->right);
115         else
116                 return root->security_id;
117 }
118
119 /*
120  * Adds a security descriptor to the indexed security descriptor set as well as
121  * the corresponding `struct wim_security_data', and returns the new security
122  * ID; or, if there is an existing security descriptor that is the same, return
123  * the security ID for it.  If a new security descriptor cannot be allocated,
124  * return -1.
125  */
126 static int sd_set_add_sd(struct sd_set *sd_set, const char descriptor[],
127                          size_t size)
128 {
129         u8 hash[SHA1_HASH_SIZE];
130         int security_id;
131         struct sd_node *new;
132         u8 **descriptors;
133         u64 *sizes;
134         u8 *descr_copy;
135         struct wim_security_data *sd;
136
137         sha1_buffer((const u8*)descriptor, size, hash);
138
139         security_id = lookup_sd(hash, sd_set->root);
140         if (security_id >= 0)
141                 return security_id;
142
143         new = MALLOC(sizeof(*new));
144         if (!new)
145                 goto out;
146         descr_copy = MALLOC(size);
147         if (!descr_copy)
148                 goto out_free_node;
149
150         sd = sd_set->sd;
151
152         memcpy(descr_copy, descriptor, size);
153         new->security_id = sd->num_entries;
154         new->left = NULL;
155         new->right = NULL;
156         copy_hash(new->hash, hash);
157
158
159         descriptors = REALLOC(sd->descriptors,
160                               (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
161         if (!descriptors)
162                 goto out_free_descr;
163         sd->descriptors = descriptors;
164         sizes = REALLOC(sd->sizes,
165                         (sd->num_entries + 1) * sizeof(sd->sizes[0]));
166         if (!sizes)
167                 goto out_free_descr;
168         sd->sizes = sizes;
169         sd->descriptors[sd->num_entries] = descr_copy;
170         sd->sizes[sd->num_entries] = size;
171         sd->num_entries++;
172         DEBUG("There are now %d security descriptors", sd->num_entries);
173         sd->total_length += size + sizeof(sd->sizes[0]);
174
175         if (sd_set->root)
176                 insert_sd_node(new, sd_set->root);
177         else
178                 sd_set->root = new;
179         return new->security_id;
180 out_free_descr:
181         FREE(descr_copy);
182 out_free_node:
183         FREE(new);
184 out:
185         return -1;
186 }
187
188 static inline ntfschar *attr_record_name(ATTR_RECORD *ar)
189 {
190         return (ntfschar*)((u8*)ar + le16_to_cpu(ar->name_offset));
191 }
192
193 /* Calculates the SHA1 message digest of a NTFS attribute. 
194  *
195  * @ni:  The NTFS inode containing the attribute.
196  * @ar:  The ATTR_RECORD describing the attribute.
197  * @md:  If successful, the returned SHA1 message digest.
198  * @reparse_tag_ret:    Optional pointer into which the first 4 bytes of the
199  *                              attribute will be written (to get the reparse
200  *                              point ID)
201  *
202  * Return 0 on success or nonzero on error.
203  */
204 static int ntfs_attr_sha1sum(ntfs_inode *ni, ATTR_RECORD *ar,
205                              u8 md[SHA1_HASH_SIZE],
206                              u32 *reparse_tag_ret)
207 {
208         s64 pos = 0;
209         s64 bytes_remaining;
210         char buf[4096];
211         ntfs_attr *na;
212         SHA_CTX ctx;
213
214         na = ntfs_attr_open(ni, ar->type, attr_record_name(ar),
215                             ar->name_length);
216         if (!na) {
217                 ERROR_WITH_ERRNO("Failed to open NTFS attribute");
218                 return WIMLIB_ERR_NTFS_3G;
219         }
220
221         bytes_remaining = na->data_size;
222         sha1_init(&ctx);
223
224         DEBUG2("Calculating SHA1 message digest (%"PRIu64" bytes)",
225                bytes_remaining);
226
227         while (bytes_remaining) {
228                 s64 to_read = min(bytes_remaining, sizeof(buf));
229                 if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
230                         ERROR_WITH_ERRNO("Error reading NTFS attribute");
231                         return WIMLIB_ERR_NTFS_3G;
232                 }
233                 if (bytes_remaining == na->data_size && reparse_tag_ret)
234                         *reparse_tag_ret = le32_to_cpu(*(u32*)buf);
235                 sha1_update(&ctx, buf, to_read);
236                 pos += to_read;
237                 bytes_remaining -= to_read;
238         }
239         sha1_final(md, &ctx);
240         ntfs_attr_close(na);
241         return 0;
242 }
243
244 /* Load the streams from a WIM file or reparse point in the NTFS volume into the
245  * WIM lookup table */
246 static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni,
247                                 char path[], size_t path_len,
248                                 struct lookup_table *lookup_table,
249                                 ntfs_volume **ntfs_vol_p,
250                                 ATTR_TYPES type)
251 {
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                 size_t stream_name_utf16_len;
275                 u32 reparse_tag;
276                 u64 data_size = ntfs_get_attribute_value_length(actx->attr);
277                 u64 name_length = actx->attr->name_length;
278
279                 if (data_size == 0) { 
280                         if (errno != 0) {
281                                 ERROR_WITH_ERRNO("Failed to get size of attribute of "
282                                                  "`%s'", path);
283                                 ret = WIMLIB_ERR_NTFS_3G;
284                                 goto out_put_actx;
285                         }
286                         /* Empty stream.  No lookup table entry is needed. */
287                         lte = NULL;
288                 } else {
289                         if (type == AT_REPARSE_POINT && data_size < 8) {
290                                 ERROR("`%s': reparse point buffer too small");
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->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. */
350                         if (dentry->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->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 = dentry_add_ads(dentry, 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 *ni,
410                                  char path[], size_t path_len,
411                                  struct lookup_table *lookup_table,
412                                  struct sd_set *sd_set,
413                                  const struct capture_config *config,
414                                  ntfs_volume **ntfs_vol_p,
415                                  int flags);
416
417 static int wim_ntfs_capture_filldir(void *dirent, const ntfschar *name,
418                                     const int name_len, const int name_type,
419                                     const s64 pos, const MFT_REF mref,
420                                     const unsigned dt_type)
421 {
422         struct readdir_ctx *ctx;
423         size_t utf8_name_len;
424         char *utf8_name;
425         struct dentry *child = NULL;
426         int ret;
427         size_t path_len;
428
429         if (name_type == FILE_NAME_DOS)
430                 return 0;
431
432         ret = -1;
433
434         utf8_name = utf16_to_utf8((const char*)name, name_len * 2,
435                                   &utf8_name_len);
436         if (!utf8_name)
437                 goto out;
438
439         if (utf8_name[0] == '.' &&
440              (utf8_name[1] == '\0' ||
441               (utf8_name[1] == '.' && utf8_name[2] == '\0'))) {
442                 ret = 0;
443                 goto out_free_utf8_name;
444         }
445
446         ctx = dirent;
447
448         ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref);
449         if (!ni) {
450                 ERROR_WITH_ERRNO("Failed to open NTFS inode");
451                 ret = 1;
452         }
453         path_len = ctx->path_len;
454         if (path_len != 1)
455                 ctx->path[path_len++] = '/';
456         memcpy(ctx->path + path_len, utf8_name, utf8_name_len + 1);
457         path_len += utf8_name_len;
458         ret = build_dentry_tree_ntfs_recursive(&child, ni, ctx->path, path_len,
459                                                ctx->lookup_table, ctx->sd_set,
460                                                ctx->config, ctx->ntfs_vol_p,
461                                                ctx->flags);
462
463         if (child)
464                 link_dentry(child, ctx->parent);
465
466         ntfs_inode_close(ni);
467 out_free_utf8_name:
468         FREE(utf8_name);
469 out:
470         return ret;
471 }
472
473 /* Recursively build a WIM dentry tree corresponding to a NTFS volume.
474  * At the same time, update the WIM lookup table with lookup table entries for
475  * the NTFS streams, and build an array of security descriptors.
476  */
477 static int build_dentry_tree_ntfs_recursive(struct dentry **root_p,
478                                             ntfs_inode *ni,
479                                             char path[],
480                                             size_t path_len,
481                                             struct lookup_table *lookup_table,
482                                             struct sd_set *sd_set,
483                                             const struct capture_config *config,
484                                             ntfs_volume **ntfs_vol_p,
485                                             int flags)
486 {
487         u32 attributes;
488         int mrec_flags;
489         u32 sd_size = 0;
490         int ret = 0;
491         struct dentry *root;
492
493         mrec_flags = ni->mrec->flags;
494         attributes = ntfs_get_inode_attributes(ni);
495
496         if (exclude_path(path, config, false)) {
497                 if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) {
498                         const char *file_type;
499                         if (attributes & MFT_RECORD_IS_DIRECTORY)
500                                 file_type = "directory";
501                         else
502                                 file_type = "file";
503                         printf("Excluding %s `%s' from capture\n",
504                                file_type, path);
505                 }
506                 *root_p = NULL;
507                 return 0;
508         }
509
510         if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
511                 printf("Scanning `%s'\n", path);
512
513         root = new_dentry(path_basename(path));
514         if (!root)
515                 return WIMLIB_ERR_NOMEM;
516
517         *root_p = root;
518         root->creation_time    = le64_to_cpu(ni->creation_time);
519         root->last_write_time  = le64_to_cpu(ni->last_data_change_time);
520         root->last_access_time = le64_to_cpu(ni->last_access_time);
521         root->attributes       = le32_to_cpu(attributes);
522         root->link_group_id    = ni->mft_no;
523         root->resolved         = true;
524
525         if (attributes & FILE_ATTR_REPARSE_POINT) {
526                 /* Junction point, symbolic link, or other reparse point */
527                 ret = capture_ntfs_streams(root, ni, path, path_len,
528                                            lookup_table, ntfs_vol_p,
529                                            AT_REPARSE_POINT);
530         } else if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
531
532                 /* Normal directory */
533                 s64 pos = 0;
534                 struct readdir_ctx ctx = {
535                         .parent       = root,
536                         .dir_ni       = ni,
537                         .path         = path,
538                         .path_len     = path_len,
539                         .lookup_table = lookup_table,
540                         .sd_set       = sd_set,
541                         .config       = config,
542                         .ntfs_vol_p   = ntfs_vol_p,
543                         .flags        = flags,
544                 };
545                 ret = ntfs_readdir(ni, &pos, &ctx, wim_ntfs_capture_filldir);
546                 if (ret != 0) {
547                         ERROR_WITH_ERRNO("ntfs_readdir()");
548                         ret = WIMLIB_ERR_NTFS_3G;
549                 }
550         } else {
551                 /* Normal file */
552                 ret = capture_ntfs_streams(root, ni, path, path_len,
553                                            lookup_table, ntfs_vol_p,
554                                            AT_DATA);
555         }
556         if (ret != 0)
557                 return ret;
558
559         ret = ntfs_get_inode_security(ni,
560                                       OWNER_SECURITY_INFORMATION |
561                                       GROUP_SECURITY_INFORMATION |
562                                       DACL_SECURITY_INFORMATION  |
563                                       SACL_SECURITY_INFORMATION,
564                                       NULL, 0, &sd_size);
565         char sd[sd_size];
566         ret = ntfs_get_inode_security(ni,
567                                       OWNER_SECURITY_INFORMATION |
568                                       GROUP_SECURITY_INFORMATION |
569                                       DACL_SECURITY_INFORMATION  |
570                                       SACL_SECURITY_INFORMATION,
571                                       sd, sd_size, &sd_size);
572         if (ret == 0) {
573                 ERROR_WITH_ERRNO("Failed to get security information from "
574                                  "`%s'", path);
575                 ret = WIMLIB_ERR_NTFS_3G;
576         } else {
577                 if (ret > 0) {
578                         /*print_security_descriptor(sd, sd_size);*/
579                         root->security_id = sd_set_add_sd(sd_set, sd, sd_size);
580                         if (root->security_id == -1) {
581                                 ERROR("Out of memory");
582                                 return WIMLIB_ERR_NOMEM;
583                         }
584                         DEBUG("Added security ID = %u for `%s'",
585                               root->security_id, path);
586                 } else { 
587                         root->security_id = -1;
588                         DEBUG("No security ID for `%s'", path);
589                 }
590                 ret = 0;
591         }
592         return ret;
593 }
594
595 static int build_dentry_tree_ntfs(struct dentry **root_p,
596                                   const char *device,
597                                   struct lookup_table *lookup_table,
598                                   struct wim_security_data *sd,
599                                   const struct capture_config *config,
600                                   int flags,
601                                   void *extra_arg)
602 {
603         ntfs_volume *vol;
604         ntfs_inode *root_ni;
605         int ret = 0;
606         struct sd_set sd_set = {
607                 .sd = sd,
608                 .root = NULL,
609         };
610         ntfs_volume **ntfs_vol_p = extra_arg;
611
612         DEBUG("Mounting NTFS volume `%s' read-only", device);
613         
614         vol = ntfs_mount(device, MS_RDONLY);
615         if (!vol) {
616                 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
617                                  device);
618                 return WIMLIB_ERR_NTFS_3G;
619         }
620         ntfs_open_secure(vol);
621
622         /* We don't want to capture the special NTFS files such as $Bitmap.  Not
623          * to be confused with "hidden" or "system" files which are real files
624          * that we do need to capture.  */
625         NVolClearShowSysFiles(vol);
626
627         DEBUG("Opening root NTFS dentry");
628         root_ni = ntfs_inode_open(vol, FILE_root);
629         if (!root_ni) {
630                 ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
631                                  "`%s'", device);
632                 ret = WIMLIB_ERR_NTFS_3G;
633                 goto out;
634         }
635
636         /* Currently we assume that all the UTF-8 paths fit into this length and
637          * there is no check for overflow. */
638         char *path = MALLOC(32768);
639         if (!path) {
640                 ERROR("Could not allocate memory for NTFS pathname");
641                 goto out_cleanup;
642         }
643
644         path[0] = '/';
645         path[1] = '\0';
646         ret = build_dentry_tree_ntfs_recursive(root_p, root_ni, path, 1,
647                                                lookup_table, &sd_set,
648                                                config, ntfs_vol_p, flags);
649 out_cleanup:
650         FREE(path);
651         ntfs_inode_close(root_ni);
652         destroy_sd_set(&sd_set);
653
654 out:
655         if (ret) {
656                 if (ntfs_umount(vol, FALSE) != 0) {
657                         ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'",
658                                          device);
659                         if (ret == 0)
660                                 ret = WIMLIB_ERR_NTFS_3G;
661                 }
662         } else {
663                 /* We need to leave the NTFS volume mounted so that we can read
664                  * the NTFS files again when we are actually writing the WIM */
665                 *ntfs_vol_p = vol;
666         }
667         return ret;
668 }
669
670
671
672 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
673                                                 const char *device,
674                                                 const char *name,
675                                                 const char *config_str,
676                                                 size_t config_len,
677                                                 int flags)
678 {
679         if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
680                 ERROR("Cannot dereference files when capturing directly from NTFS");
681                 return WIMLIB_ERR_INVALID_PARAM;
682         }
683         return do_add_image(w, device, name, config_str, config_len, flags,
684                             build_dentry_tree_ntfs, &w->ntfs_vol);
685 }
686
687 #else /* WITH_NTFS_3G */
688 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
689                                                 const char *device,
690                                                 const char *name,
691                                                 const char *config_str,
692                                                 size_t config_len,
693                                                 int flags)
694 {
695         ERROR("wimlib was compiled without support for NTFS-3g, so");
696         ERROR("we cannot capture a WIM image directly from a NTFS volume");
697         return WIMLIB_ERR_UNSUPPORTED;
698 }
699 #endif /* WITH_NTFS_3G */