]> wimlib.net Git - wimlib/blob - src/ntfs-capture.c
More test cases and DOS names capture/apply fixes
[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 *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                 ret = 1;
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                 link_dentry(child, ctx->parent);
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         u32 sd_size = 0;
511         int ret;
512         char dos_name_utf8[64];
513         struct dentry *root;
514
515         mrec_flags = ni->mrec->flags;
516         attributes = ntfs_get_inode_attributes(ni);
517
518         if (exclude_path(path, config, false)) {
519                 if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) {
520                         const char *file_type;
521                         if (attributes & MFT_RECORD_IS_DIRECTORY)
522                                 file_type = "directory";
523                         else
524                                 file_type = "file";
525                         printf("Excluding %s `%s' from capture\n",
526                                file_type, path);
527                 }
528                 *root_p = NULL;
529                 return 0;
530         }
531
532         if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
533                 printf("Scanning `%s'\n", path);
534
535         root = new_dentry(path_basename(path));
536         if (!root)
537                 return WIMLIB_ERR_NOMEM;
538         *root_p = root;
539
540         if (dir_ni && (name_type == FILE_NAME_WIN32_AND_DOS
541                        || name_type == FILE_NAME_WIN32))
542         {
543                 ret = ntfs_get_ntfs_dos_name(ni, dir_ni, dos_name_utf8,
544                                              sizeof(dos_name_utf8));
545                 if (ret > 0) {
546                         DEBUG("Changing short name of `%s'", path);
547                         ret = change_dentry_short_name(root, dos_name_utf8,
548                                                        ret);
549                         if (ret != 0)
550                                 return ret;
551                 } else {
552                         if (errno != ENODATA) {
553                                 ERROR_WITH_ERRNO("Error getting DOS name "
554                                                  "of `%s'", path);
555                                 return WIMLIB_ERR_NTFS_3G;
556                         }
557                 }
558         }
559
560         root->creation_time    = le64_to_cpu(ni->creation_time);
561         root->last_write_time  = le64_to_cpu(ni->last_data_change_time);
562         root->last_access_time = le64_to_cpu(ni->last_access_time);
563         root->attributes       = le32_to_cpu(attributes);
564         root->link_group_id    = ni->mft_no;
565         root->resolved         = true;
566
567         if (attributes & FILE_ATTR_REPARSE_POINT) {
568                 /* Junction point, symbolic link, or other reparse point */
569                 ret = capture_ntfs_streams(root, ni, path, path_len,
570                                            lookup_table, ntfs_vol_p,
571                                            AT_REPARSE_POINT);
572         } else if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
573
574                 /* Normal directory */
575                 s64 pos = 0;
576                 struct readdir_ctx ctx = {
577                         .parent       = root,
578                         .dir_ni       = ni,
579                         .path         = path,
580                         .path_len     = path_len,
581                         .lookup_table = lookup_table,
582                         .sd_set       = sd_set,
583                         .config       = config,
584                         .ntfs_vol_p   = ntfs_vol_p,
585                         .flags        = flags,
586                 };
587                 ret = ntfs_readdir(ni, &pos, &ctx, wim_ntfs_capture_filldir);
588                 if (ret != 0) {
589                         ERROR_WITH_ERRNO("ntfs_readdir()");
590                         ret = WIMLIB_ERR_NTFS_3G;
591                 }
592         } else {
593                 /* Normal file */
594                 ret = capture_ntfs_streams(root, ni, path, path_len,
595                                            lookup_table, ntfs_vol_p,
596                                            AT_DATA);
597         }
598         if (ret != 0)
599                 return ret;
600
601         ret = ntfs_get_inode_security(ni,
602                                       OWNER_SECURITY_INFORMATION |
603                                       GROUP_SECURITY_INFORMATION |
604                                       DACL_SECURITY_INFORMATION  |
605                                       SACL_SECURITY_INFORMATION,
606                                       NULL, 0, &sd_size);
607         char sd[sd_size];
608         ret = ntfs_get_inode_security(ni,
609                                       OWNER_SECURITY_INFORMATION |
610                                       GROUP_SECURITY_INFORMATION |
611                                       DACL_SECURITY_INFORMATION  |
612                                       SACL_SECURITY_INFORMATION,
613                                       sd, sd_size, &sd_size);
614         if (ret == 0) {
615                 ERROR_WITH_ERRNO("Failed to get security information from "
616                                  "`%s'", path);
617                 ret = WIMLIB_ERR_NTFS_3G;
618         } else {
619                 if (ret > 0) {
620                         /*print_security_descriptor(sd, sd_size);*/
621                         root->security_id = sd_set_add_sd(sd_set, sd, sd_size);
622                         if (root->security_id == -1) {
623                                 ERROR("Out of memory");
624                                 return WIMLIB_ERR_NOMEM;
625                         }
626                         DEBUG("Added security ID = %u for `%s'",
627                               root->security_id, path);
628                 } else { 
629                         root->security_id = -1;
630                         DEBUG("No security ID for `%s'", path);
631                 }
632                 ret = 0;
633         }
634         return ret;
635 }
636
637 static int build_dentry_tree_ntfs(struct dentry **root_p,
638                                   const char *device,
639                                   struct lookup_table *lookup_table,
640                                   struct wim_security_data *sd,
641                                   const struct capture_config *config,
642                                   int flags,
643                                   void *extra_arg)
644 {
645         ntfs_volume *vol;
646         ntfs_inode *root_ni;
647         int ret = 0;
648         struct sd_set sd_set = {
649                 .sd = sd,
650                 .root = NULL,
651         };
652         ntfs_volume **ntfs_vol_p = extra_arg;
653
654         DEBUG("Mounting NTFS volume `%s' read-only", device);
655         
656         vol = ntfs_mount(device, MS_RDONLY);
657         if (!vol) {
658                 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
659                                  device);
660                 return WIMLIB_ERR_NTFS_3G;
661         }
662         ntfs_open_secure(vol);
663
664         /* We don't want to capture the special NTFS files such as $Bitmap.  Not
665          * to be confused with "hidden" or "system" files which are real files
666          * that we do need to capture.  */
667         NVolClearShowSysFiles(vol);
668
669         DEBUG("Opening root NTFS dentry");
670         root_ni = ntfs_inode_open(vol, FILE_root);
671         if (!root_ni) {
672                 ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
673                                  "`%s'", device);
674                 ret = WIMLIB_ERR_NTFS_3G;
675                 goto out;
676         }
677
678         /* Currently we assume that all the UTF-8 paths fit into this length and
679          * there is no check for overflow. */
680         char *path = MALLOC(32768);
681         if (!path) {
682                 ERROR("Could not allocate memory for NTFS pathname");
683                 goto out_cleanup;
684         }
685
686         path[0] = '/';
687         path[1] = '\0';
688         ret = build_dentry_tree_ntfs_recursive(root_p, NULL, root_ni, path, 1,
689                                                FILE_NAME_POSIX, lookup_table,
690                                                &sd_set, config, ntfs_vol_p,
691                                                flags);
692 out_cleanup:
693         FREE(path);
694         ntfs_inode_close(root_ni);
695         destroy_sd_set(&sd_set);
696
697 out:
698         if (ret) {
699                 if (ntfs_umount(vol, FALSE) != 0) {
700                         ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'",
701                                          device);
702                         if (ret == 0)
703                                 ret = WIMLIB_ERR_NTFS_3G;
704                 }
705         } else {
706                 /* We need to leave the NTFS volume mounted so that we can read
707                  * the NTFS files again when we are actually writing the WIM */
708                 *ntfs_vol_p = vol;
709         }
710         return ret;
711 }
712
713
714
715 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
716                                                 const char *device,
717                                                 const char *name,
718                                                 const char *config_str,
719                                                 size_t config_len,
720                                                 int flags)
721 {
722         if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
723                 ERROR("Cannot dereference files when capturing directly from NTFS");
724                 return WIMLIB_ERR_INVALID_PARAM;
725         }
726         return do_add_image(w, device, name, config_str, config_len, flags,
727                             build_dentry_tree_ntfs, &w->ntfs_vol);
728 }
729
730 #else /* WITH_NTFS_3G */
731 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
732                                                 const char *device,
733                                                 const char *name,
734                                                 const char *config_str,
735                                                 size_t config_len,
736                                                 int flags)
737 {
738         ERROR("wimlib was compiled without support for NTFS-3g, so");
739         ERROR("we cannot capture a WIM image directly from a NTFS volume");
740         return WIMLIB_ERR_UNSUPPORTED;
741 }
742 #endif /* WITH_NTFS_3G */