4 * Capture a WIM image from a NTFS volume. We capture everything we can,
5 * including security data and alternate data streams. There should be no loss
10 * Copyright (C) 2012 Eric Biggers
12 * This file is part of wimlib, a library for working with WIM files.
14 * wimlib is free software; you can redistribute it and/or modify it under the
15 * terms of the GNU Lesser General Public License as published by the Free
16 * Software Foundation; either version 2.1 of the License, or (at your option)
19 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
20 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
21 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with wimlib; if not, see http://www.gnu.org/licenses/.
29 #include "wimlib_internal.h"
34 #include "lookup_table.h"
36 #include <ntfs-3g/layout.h>
37 #include <ntfs-3g/acls.h>
38 #include <ntfs-3g/attrib.h>
39 #include <ntfs-3g/misc.h>
40 #include <ntfs-3g/reparse.h>
41 #include <ntfs-3g/security.h>
42 #include <ntfs-3g/volume.h>
46 extern int ntfs_inode_get_security(ntfs_inode *ni, u32 selection, char *buf,
47 u32 buflen, u32 *psize);
49 extern int ntfs_inode_get_attributes(ntfs_inode *ni);
51 /* Structure that allows searching the security descriptors by SHA1 message
54 struct wim_security_data *sd;
58 /* Binary tree node of security descriptors, indexed by the @hash field. */
61 u8 hash[SHA1_HASH_SIZE];
63 struct sd_node *right;
66 /* Frees a security descriptor index tree. */
67 static void free_sd_set(struct sd_node *root)
70 free_sd_set(root->left);
71 free_sd_set(root->right);
76 /* Inserts a a new node into the security descriptor index tree. */
77 static void insert_sd_node(struct sd_node *new, struct sd_node *root)
79 int cmp = hashes_cmp(new->hash, root->hash);
82 insert_sd_node(new, root->left);
87 insert_sd_node(new, root->right);
95 /* Returns the security ID of the security data having a SHA1 message digest of
96 * @hash in the security descriptor index tree rooted at @root.
98 * If not found, return -1. */
99 static int lookup_sd(const u8 hash[SHA1_HASH_SIZE], struct sd_node *root)
104 cmp = hashes_cmp(hash, root->hash);
106 return lookup_sd(hash, root->left);
108 return lookup_sd(hash, root->right);
110 return root->security_id;
114 * Adds a security descriptor to the indexed security descriptor set as well as
115 * the corresponding `struct wim_security_data', and returns the new security
116 * ID; or, if there is an existing security descriptor that is the same, return
117 * the security ID for it. If a new security descriptor cannot be allocated,
120 static int sd_set_add_sd(struct sd_set *sd_set, const u8 *descriptor,
123 u8 hash[SHA1_HASH_SIZE];
129 struct wim_security_data *sd;
130 sha1_buffer(descriptor, size, hash);
132 security_id = lookup_sd(hash, sd_set->root);
133 if (security_id >= 0)
136 new = MALLOC(sizeof(*new));
139 descr_copy = MALLOC(size);
145 memcpy(descr_copy, descriptor, size);
146 new->security_id = sd->num_entries;
149 copy_hash(new->hash, hash);
152 descriptors = REALLOC(sd->descriptors,
153 (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
156 sd->descriptors = descriptors;
157 sizes = REALLOC(sd->sizes,
158 (sd->num_entries + 1) * sizeof(sd->sizes[0]));
162 sd->descriptors[sd->num_entries] = descr_copy;
163 sd->sizes[sd->num_entries] = size;
165 sd->total_length += size + sizeof(sd->sizes[0]);
168 insert_sd_node(sd_set->root, new);
171 return new->security_id;
180 static inline ntfschar *attr_record_name(ATTR_RECORD *ar)
182 return (ntfschar*)((u8*)ar + le16_to_cpu(ar->name_offset));
185 /* Calculates the SHA1 message digest of a NTFS attribute.
187 * @ni: The NTFS inode containing the attribute.
188 * @ar: The ATTR_RECORD describing the attribute.
189 * @md: If successful, the returned SHA1 message digest.
191 * Return 0 on success or nonzero on error.
193 static int ntfs_attr_sha1sum(ntfs_inode *ni, ATTR_RECORD *ar,
194 u8 md[SHA1_HASH_SIZE])
202 na = ntfs_attr_open(ni, ar->type, attr_record_name(ar),
205 ERROR_WITH_ERRNO("Failed to open NTFS attribute");
206 return WIMLIB_ERR_NTFS_3G;
209 bytes_remaining = na->data_size;
212 while (bytes_remaining) {
213 s64 to_read = min(bytes_remaining, sizeof(buf));
214 if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
215 ERROR_WITH_ERRNO("Error reading NTFS attribute");
216 return WIMLIB_ERR_NTFS_3G;
218 sha1_update(&ctx, buf, to_read);
220 bytes_remaining -= to_read;
222 sha1_final(md, &ctx);
227 /* Load the streams from a WIM file or reparse point in the NTFS volume into the
228 * WIM lookup table */
229 static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni,
230 char path[], size_t path_len,
231 struct lookup_table *lookup_table,
232 ntfs_volume **ntfs_vol_p,
236 ntfs_attr_search_ctx *actx;
237 u8 attr_hash[SHA1_HASH_SIZE];
238 struct ntfs_location *ntfs_loc;
239 struct lookup_table_entry *lte;
242 /* Get context to search the streams of the NTFS file. */
243 actx = ntfs_attr_get_search_ctx(ni, NULL);
245 ERROR_WITH_ERRNO("Cannot get attribute search "
247 return WIMLIB_ERR_NTFS_3G;
250 /* Capture each data stream or reparse data stream. */
251 while (!ntfs_attr_lookup(type, NULL, 0,
252 CASE_SENSITIVE, 0, NULL, 0, actx))
254 char *stream_name_utf8;
255 size_t stream_name_utf16_len;
257 /* Checksum the stream. */
258 ret = ntfs_attr_sha1sum(ni, actx->attr, attr_hash);
262 /* Make a lookup table entry for the stream, or use an existing
263 * one if there's already an identical stream. */
264 lte = __lookup_resource(lookup_table, attr_hash);
265 ret = WIMLIB_ERR_NOMEM;
269 struct ntfs_location *ntfs_loc;
272 ntfs_loc = CALLOC(1, sizeof(*ntfs_loc));
275 ntfs_loc->ntfs_vol_p = ntfs_vol_p;
276 ntfs_loc->path_utf8 = MALLOC(path_len + 1);
277 if (!ntfs_loc->path_utf8)
278 goto out_free_ntfs_loc;
279 memcpy(ntfs_loc->path_utf8, path, path_len + 1);
280 ntfs_loc->stream_name_utf16 = MALLOC(actx->attr->name_length * 2);
281 if (!ntfs_loc->stream_name_utf16)
282 goto out_free_ntfs_loc;
283 memcpy(ntfs_loc->stream_name_utf16,
284 attr_record_name(actx->attr),
285 actx->attr->name_length * 2);
287 ntfs_loc->stream_name_utf16_num_chars = actx->attr->name_length;
288 lte = new_lookup_table_entry();
290 goto out_free_ntfs_loc;
291 lte->ntfs_loc = ntfs_loc;
292 lte->resource_location = RESOURCE_IN_NTFS_VOLUME;
293 lte->resource_entry.original_size = actx->attr->data_size;
294 lte->resource_entry.size = actx->attr->data_size;
295 copy_hash(lte->hash, attr_hash);
296 lookup_table_insert(lookup_table, lte);
298 if (actx->attr->name_length == 0) {
299 wimlib_assert(!dentry->lte);
302 struct ads_entry *new_ads_entry;
303 stream_name_utf8 = utf16_to_utf8((const u8*)attr_record_name(actx->attr),
304 actx->attr->name_length,
305 &stream_name_utf16_len);
306 if (!stream_name_utf8)
308 FREE(stream_name_utf8);
309 new_ads_entry = dentry_add_ads(dentry, stream_name_utf8);
313 new_ads_entry->lte = lte;
319 free_lookup_table_entry(lte);
322 FREE(ntfs_loc->path_utf8);
323 FREE(ntfs_loc->stream_name_utf16);
327 ntfs_attr_put_search_ctx(actx);
332 struct dentry *dentry;
336 struct lookup_table *lookup_table;
337 struct sd_set *sd_set;
338 ntfs_volume **ntfs_vol_p;
341 static int __build_dentry_tree_ntfs(struct dentry *dentry, ntfs_inode *ni,
342 char path[], size_t path_len,
343 struct lookup_table *lookup_table,
344 struct sd_set *sd_set,
345 ntfs_volume **ntfs_vol_p);
348 static int filldir(void *dirent, const ntfschar *name,
349 const int name_len, const int name_type, const s64 pos,
350 const MFT_REF mref, const unsigned dt_type)
352 struct readdir_ctx *ctx;
353 size_t utf8_name_len;
355 struct dentry *child;
361 utf8_name = utf16_to_utf8((const u8*)name, name_len * 2,
368 ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref);
370 ERROR_WITH_ERRNO("Failed to open NTFS inode");
373 child = new_dentry(utf8_name);
377 memcpy(ctx->path + ctx->path_len, utf8_name, utf8_name_len + 1);
378 path_len = ctx->path_len + utf8_name_len;
379 ret = __build_dentry_tree_ntfs(child, ni, ctx->path, path_len,
380 ctx->lookup_table, ctx->sd_set,
382 link_dentry(child, ctx->dentry);
384 ntfs_inode_close(ni);
391 /* Recursively build a WIM dentry tree corresponding to a NTFS volume.
392 * At the same time, update the WIM lookup table with lookup table entries for
393 * the NTFS streams, and build an array of security descriptors.
395 static int __build_dentry_tree_ntfs(struct dentry *dentry, ntfs_inode *ni,
396 char path[], size_t path_len,
397 struct lookup_table *lookup_table,
398 struct sd_set *sd_set,
399 ntfs_volume **ntfs_vol_p)
401 u32 attributes = ntfs_inode_get_attributes(ni);
402 int mrec_flags = ni->mrec->flags;
406 dentry->creation_time = le64_to_cpu(ni->creation_time);
407 dentry->last_write_time = le64_to_cpu(ni->last_data_change_time);
408 dentry->last_access_time = le64_to_cpu(ni->last_access_time);
409 dentry->security_id = le32_to_cpu(ni->security_id);
410 dentry->attributes = le32_to_cpu(attributes);
411 dentry->hard_link = ni->mft_no;
412 dentry->resolved = true;
414 if (attributes & FILE_ATTR_REPARSE_POINT) {
415 /* Junction point, symbolic link, or other reparse point */
416 ret = capture_ntfs_streams(dentry, ni, path, path_len,
417 lookup_table, ntfs_vol_p,
419 } else if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
420 /* Normal directory */
422 struct readdir_ctx ctx = {
426 .path_len = path_len,
427 .lookup_table = lookup_table,
429 .ntfs_vol_p = ntfs_vol_p,
431 ret = ntfs_readdir(ni, &pos, &ctx, filldir);
433 ret = WIMLIB_ERR_NTFS_3G;
436 ret = capture_ntfs_streams(dentry, ni, path, path_len,
437 lookup_table, ntfs_vol_p,
442 ret = ntfs_inode_get_security(ni,
443 OWNER_SECURITY_INFORMATION |
444 GROUP_SECURITY_INFORMATION |
445 DACL_SECURITY_INFORMATION |
446 SACL_SECURITY_INFORMATION,
449 ret = ntfs_inode_get_security(ni,
450 OWNER_SECURITY_INFORMATION |
451 GROUP_SECURITY_INFORMATION |
452 DACL_SECURITY_INFORMATION |
453 SACL_SECURITY_INFORMATION,
454 sd, sd_size, &sd_size);
455 dentry->security_id = sd_set_add_sd(sd_set, sd, sd_size);
456 if (dentry->security_id == -1) {
457 ERROR("Could not allocate security ID");
458 ret = WIMLIB_ERR_NOMEM;
463 static int build_dentry_tree_ntfs(struct dentry *root_dentry,
465 struct lookup_table *lookup_table,
466 struct wim_security_data *sd,
476 ntfs_volume **ntfs_vol_p = extra_arg;
478 vol = ntfs_mount(device, MS_RDONLY);
480 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
482 return WIMLIB_ERR_NTFS_3G;
484 root_ni = ntfs_inode_open(vol, FILE_root);
486 ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
488 ret = WIMLIB_ERR_NTFS_3G;
494 ret = __build_dentry_tree_ntfs(root_dentry, root_ni, path, 1,
495 lookup_table, &tree, ntfs_vol_p);
496 ntfs_inode_close(root_ni);
499 if (ntfs_umount(vol, FALSE) != 0) {
500 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'", device);
502 ret = WIMLIB_ERR_NTFS_3G;
507 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
510 const char *description,
511 const char *flags_element,
514 if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
515 ERROR("Cannot dereference files when capturing directly from NTFS");
516 return WIMLIB_ERR_INVALID_PARAM;
518 return do_add_image(w, device, name, description, flags_element, flags,
519 build_dentry_tree_ntfs,
523 #else /* WITH_NTFS_3G */
524 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
527 const char *description,
528 const char *flags_element,
531 ERROR("wimlib was compiled without support for NTFS-3g, so");
532 ERROR("we cannot capture a WIM image directly from a NTFS volume");
533 return WIMLIB_ERR_UNSUPPORTED;
535 #endif /* WITH_NTFS_3G */