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);
53 struct wim_security_data *sd;
59 u8 hash[SHA1_HASH_SIZE];
61 struct sd_node *right;
64 static void free_sd_tree(struct sd_node *root)
67 free_sd_tree(root->left);
68 free_sd_tree(root->right);
73 static void insert_sd_node(struct sd_node *new, struct sd_node *root)
75 int cmp = hashes_cmp(root->hash, new->hash);
78 insert_sd_node(new, root->left);
83 insert_sd_node(new, root->right);
91 static int lookup_sd(const u8 hash[SHA1_HASH_SIZE], struct sd_node *node)
96 cmp = hashes_cmp(hash, node->hash);
98 return lookup_sd(hash, node->left);
100 return lookup_sd(hash, node->right);
102 return node->security_id;
105 static int tree_add_sd(struct sd_tree *tree, const u8 *descriptor,
108 u8 hash[SHA1_HASH_SIZE];
114 struct wim_security_data *sd = tree->sd;
115 sha1_buffer(descriptor, size, hash);
117 security_id = lookup_sd(hash, tree->root);
118 if (security_id >= 0)
121 new = MALLOC(sizeof(struct sd_node));
124 descr_copy = MALLOC(size);
127 memcpy(descr_copy, descriptor, size);
128 new->security_id = tree->num_sds++;
131 copy_hash(new->hash, hash);
133 descriptors = REALLOC(sd->descriptors,
134 (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
137 sd->descriptors = descriptors;
138 sizes = REALLOC(sd->sizes,
139 (sd->num_entries + 1) * sizeof(sd->sizes[0]));
143 sd->descriptors[sd->num_entries] = descr_copy;
144 sd->sizes[sd->num_entries] = size;
146 sd->total_length += size + 8;
149 insert_sd_node(tree->root, new);
152 return new->security_id;
161 static int build_sd_tree(struct wim_security_data *sd, struct sd_tree *tree)
164 u32 orig_num_entries = sd->num_entries;
165 u32 orig_total_length = sd->total_length;
171 for (u32 i = 0; i < sd->num_entries; i++) {
172 ret = tree_add_sd(tree, sd->descriptors[i], sd->sizes[i]);
178 sd->num_entries = orig_num_entries;
179 sd->total_length = orig_total_length;
180 free_sd_tree(tree->root);
185 static int ntfs_attr_sha1sum(ntfs_inode *ni, ATTR_RECORD *ar,
186 u8 md[SHA1_HASH_SIZE])
194 na = ntfs_attr_open(ni, ar->type,
195 (ntfschar*)((u8*)ar + le16_to_cpu(ar->name_offset)),
198 ERROR_WITH_ERRNO("Failed to open NTFS attribute");
199 return WIMLIB_ERR_NTFS_3G;
202 bytes_remaining = na->data_size;
205 while (bytes_remaining) {
206 s64 to_read = min(bytes_remaining, sizeof(buf));
207 if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
208 ERROR_WITH_ERRNO("Error reading NTFS attribute");
209 return WIMLIB_ERR_NTFS_3G;
211 sha1_update(&ctx, buf, to_read);
213 bytes_remaining -= to_read;
215 sha1_final(md, &ctx);
220 /* Load a normal file in the NTFS volume into the WIM lookup table */
221 static int capture_normal_ntfs_file(struct dentry *dentry, ntfs_inode *ni,
222 char path[], size_t path_len,
223 struct lookup_table *lookup_table,
224 ntfs_volume **ntfs_vol_p)
227 ntfs_attr_search_ctx *actx;
228 u8 attr_hash[SHA1_HASH_SIZE];
229 struct ntfs_location *ntfs_loc;
230 struct lookup_table_entry *lte;
233 actx = ntfs_attr_get_search_ctx(ni, NULL);
235 ERROR_WITH_ERRNO("Cannot get attribute search "
237 return WIMLIB_ERR_NTFS_3G;
239 while (!ntfs_attr_lookup(AT_DATA, NULL, 0,
240 CASE_SENSITIVE, 0, NULL, 0, actx))
242 ret = ntfs_attr_sha1sum(ni, actx->attr, attr_hash);
245 lte = __lookup_resource(lookup_table, attr_hash);
249 struct ntfs_location *ntfs_loc;
251 ret = WIMLIB_ERR_NOMEM;
253 ntfs_loc = CALLOC(1, sizeof(*ntfs_loc));
257 ntfs_loc->path_utf8 = MALLOC(path_len + 1);
258 if (!ntfs_loc->path_utf8)
260 memcpy(ntfs_loc->path_utf8, path, path_len + 1);
261 ntfs_loc->stream_name_utf16 = MALLOC(actx->attr->name_length * 2);
262 if (!ntfs_loc->stream_name_utf16)
264 memcpy(ntfs_loc->stream_name_utf16,
266 le16_to_cpu(actx->attr->name_offset),
267 actx->attr->name_length * 2);
269 ntfs_loc->stream_name_utf16_num_chars = actx->attr->name_length;
270 lte = new_lookup_table_entry();
273 lte->ntfs_loc = ntfs_loc;
274 lte->resource_location = RESOURCE_IN_NTFS_VOLUME;
275 lte->resource_entry.original_size = actx->attr->data_size;
276 lte->resource_entry.size = actx->attr->data_size;
277 copy_hash(lte->hash, attr_hash);
278 lookup_table_insert(lookup_table, lte);
285 FREE(ntfs_loc->path_utf8);
286 FREE(ntfs_loc->stream_name_utf16);
290 ntfs_attr_put_search_ctx(actx);
294 static int __build_dentry_tree_ntfs(struct dentry *dentry, ntfs_inode *ni,
295 char path[], size_t path_len,
296 struct lookup_table *lookup_table,
297 struct sd_tree *tree,
298 ntfs_volume **ntfs_vol_p)
300 u32 attributes = ntfs_inode_get_attributes(ni);
301 int mrec_flags = ni->mrec->flags;
305 dentry->creation_time = le64_to_cpu(ni->creation_time);
306 dentry->last_write_time = le64_to_cpu(ni->last_data_change_time);
307 dentry->last_access_time = le64_to_cpu(ni->last_access_time);
308 dentry->security_id = le32_to_cpu(ni->security_id);
309 dentry->attributes = le32_to_cpu(attributes);
310 dentry->resolved = true;
312 if (attributes & FILE_ATTR_REPARSE_POINT) {
313 /* Junction point, symbolic link, or other reparse point */
314 } else if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
315 /* Normal directory */
318 ret = capture_normal_ntfs_file(dentry, ni, path, path_len,
319 lookup_table, ntfs_vol_p);
323 ret = ntfs_inode_get_security(ni,
324 OWNER_SECURITY_INFORMATION |
325 GROUP_SECURITY_INFORMATION |
326 DACL_SECURITY_INFORMATION |
327 SACL_SECURITY_INFORMATION,
330 ret = ntfs_inode_get_security(ni,
331 OWNER_SECURITY_INFORMATION |
332 GROUP_SECURITY_INFORMATION |
333 DACL_SECURITY_INFORMATION |
334 SACL_SECURITY_INFORMATION,
335 sd, sd_size, &sd_size);
336 dentry->security_id = tree_add_sd(tree, sd, sd_size);
340 static int build_dentry_tree_ntfs(struct dentry *root_dentry,
342 struct lookup_table *lookup_table,
343 struct wim_security_data *sd,
353 ntfs_volume **ntfs_vol_p = extra_arg;
355 vol = ntfs_mount(device, MS_RDONLY);
357 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
359 return WIMLIB_ERR_NTFS_3G;
361 root_ni = ntfs_inode_open(vol, FILE_root);
363 ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
365 ret = WIMLIB_ERR_NTFS_3G;
371 ret = __build_dentry_tree_ntfs(root_dentry, root_ni, path, 1,
372 lookup_table, &tree, ntfs_vol_p);
373 ntfs_inode_close(root_ni);
376 if (ntfs_umount(vol, FALSE) != 0) {
377 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'", device);
379 ret = WIMLIB_ERR_NTFS_3G;
384 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
387 const char *description,
388 const char *flags_element,
391 if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
392 ERROR("Cannot dereference files when capturing directly from NTFS");
393 return WIMLIB_ERR_INVALID_PARAM;
395 return do_add_image(w, device, name, description, flags_element, flags,
396 build_dentry_tree_ntfs,
400 #else /* WITH_NTFS_3G */
401 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
404 const char *description,
405 const char *flags_element,
408 ERROR("wimlib was compiled without support for NTFS-3g, so");
409 ERROR("we cannot capture a WIM image directly from a NTFS volume");
410 return WIMLIB_ERR_UNSUPPORTED;
412 #endif /* WITH_NTFS_3G */