NTFS capture (IN PROGRESS)
authorEric Biggers <ebiggers3@gmail.com>
Sun, 26 Aug 2012 00:57:46 +0000 (19:57 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 26 Aug 2012 00:57:46 +0000 (19:57 -0500)
src/dentry.h
src/endianness.h
src/modify.c
src/ntfs-3g_security.c
src/ntfs-capture.c
src/sha1.h
src/wimlib_internal.h

index 613f6259e95c1b1884c81e6852a4e8e754b6534a..e030acf03b27c3b9487b1d6217cf87ec6bf5ca04 100644 (file)
@@ -134,7 +134,7 @@ struct dentry {
         * entry's child files.  0 if the directory entry has no children. */
        u64 subdir_offset;
 
-       /* Timestamps for the entry.  The timestamps are the number of
+       /* Timestamps for the dentry.  The timestamps are the number of
         * 100-nanosecond intervals that have elapsed since 12:00 A.M., January
         * 1st, 1601, UTC. */
        u64 creation_time;
index ba66b27a27f5c64b6b77b0496a348594b48375f6..e004216790390e01dcbfd96b3540f465836faaa0 100644 (file)
@@ -48,6 +48,10 @@ static inline uint64_t bswap64(uint64_t n)
 #define to_be32(n) (n)
 #define to_be64(n) (n)
 
+#define le16_to_cpu(n) bswap16(n)
+#define le32_to_cpu(n) bswap32(n)
+#define le64_to_cpu(n) bswap64(n)
+
 /* In place */
 #define TO_LE16(n) ((n) = to_le16(n))
 #define TO_LE32(n) ((n) = to_le32(n))
@@ -78,6 +82,10 @@ static inline void array_to_le64(uint64_t *p, uint64_t n)
 #define to_le32(n) (n)
 #define to_le64(n) (n)
 
+#define le16_to_cpu(n) (n)
+#define le32_to_cpu(n) (n)
+#define le64_to_cpu(n) (n)
+
 #define to_be16(n) (bswap16(n))
 #define to_be32(n) (bswap32(n))
 #define to_be64(n) (bswap64(n))
index dcd0b6d353eaf0ce34e4b591dd68b9861e1dcbae..cef29abf2c42de0dcf2a7a5d0fdaaeac0bebc298 100644 (file)
@@ -495,12 +495,11 @@ WIMLIBAPI int wimlib_delete_image(WIMStruct *w, int image)
        return 0;
 }
 
-/*
- * Adds an image to a WIM file from a directory tree on disk.
- */
-WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *dir, 
-                              const char *name, const char *description, 
-                              const char *flags_element, int flags)
+int do_add_image(WIMStruct *w, const char *dir, const char *name,
+                const char *description, const char *flags_element,
+                int flags,
+                int (*capture_tree)(struct dentry *, const char *,
+                                    struct lookup_table *, int))
 {
        struct dentry *root_dentry;
        struct image_metadata *imd;
@@ -530,8 +529,8 @@ WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *dir,
                return WIMLIB_ERR_NOMEM;
 
        DEBUG("Building dentry tree.");
-       ret = build_dentry_tree(root_dentry, dir, w->lookup_table,
-                               flags | WIMLIB_ADD_IMAGE_FLAG_ROOT);
+       ret = (*capture_tree)(root_dentry, dir, w->lookup_table,
+                             flags | WIMLIB_ADD_IMAGE_FLAG_ROOT);
 
        if (ret != 0) {
                ERROR("Failed to build dentry tree for `%s'", dir);
@@ -572,3 +571,14 @@ out_free_dentry_tree:
        free_dentry_tree(root_dentry, w->lookup_table);
        return ret;
 }
+
+/*
+ * Adds an image to a WIM file from a directory tree on disk.
+ */
+WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *dir, 
+                              const char *name, const char *description, 
+                              const char *flags_element, int flags)
+{
+       return do_add_image(w, dir, name, description, flags_element, flags,
+                           build_dentry_tree);
+}
index 22faa9ec72c42cff1892e0fd0d2e92bbda1551a0..a596b28cd8f966bac47bbd0872e6ad28e14278c8 100644 (file)
@@ -4681,6 +4681,28 @@ int ntfs_set_file_security(struct SECURITY_API *scapi,
        return (res);
 }
 #endif
+
+int ntfs_inode_get_security(ntfs_inode *ni, u32 selection, char *buf,
+                           u32 buflen, u32 *psize)
+{
+       char *attr;
+       int res = 0;
+
+       attr = getsecurityattr(ni->vol, ni);
+       if (attr) {
+               if (feedsecurityattr(attr,selection,
+                               buf,buflen,psize)) {
+                       if (test_nino_flag(ni, v3_Extensions)
+                           && ni->security_id)
+                               res = le32_to_cpu(
+                                       ni->security_id);
+                       else
+                               res = -1;
+               }
+               free(attr);
+       }
+       return (res);
+}
 /*
  *             Check the validity of the ACEs in a DACL or SACL
  */
@@ -4955,6 +4977,20 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
 }
 #endif
 
+int ntfs_inode_get_attributes(ntfs_inode *ni)
+{
+       s32 attrib;
+
+       attrib = le32_to_cpu(ni->flags);
+       if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
+               attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
+       else
+               attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
+       if (!attrib)
+               attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
+       return (attrib);
+}
+
 /* 
  * Set attributes of a NTFS file given an inode
  *
index 66b78c0013062a0ce1cd90a49beebf1a9b29108a..2ca4afad9e41630afbc2b0ac2a74fdf030d1406e 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 
+extern int ntfs_inode_get_security(ntfs_inode *ni, u32 selection, char *buf,
+                                  u32 buflen, u32 *psize);
+
+extern int ntfs_inode_get_attributes(ntfs_inode *ni);
+
+struct sd_tree {
+       u32 num_sds;
+       struct wim_security_data *sd;
+       struct sd_node *root;
+};
+
+struct sd_node {
+       int security_id;
+       u8 hash[SHA1_HASH_SIZE];
+       struct sd_node *left;
+       struct sd_node *right;
+};
+
+static void free_sd_tree(struct sd_node *root)
+{
+       if (root) {
+               free_sd_tree(root->left);
+               free_sd_tree(root->right);
+               FREE(root);
+       }
+}
+
+static void insert_sd_node(struct sd_node *new, struct sd_node *root)
+{
+       int cmp = hashes_cmp(root->hash, new->hash);
+       if (cmp < 0) {
+               if (root->left)
+                       insert_sd_node(new, root->left);
+               else 
+                       root->left = new;
+       } else if (cmp > 0) {
+               if (root->right)
+                       insert_sd_node(new, root->right);
+               else 
+                       root->right = new;
+       } else {
+               wimlib_assert(0);
+       }
+}
+
+static int lookup_sd(const u8 hash[SHA1_HASH_SIZE], struct sd_node *node)
+{
+       int cmp;
+       if (!node)
+               return -1;
+       cmp = hashes_cmp(hash, node->hash);
+       if (cmp < 0)
+               return lookup_sd(hash, node->left);
+       else if (cmp > 0)
+               return lookup_sd(hash, node->right);
+       else
+               return node->security_id;
+}
+
+static int tree_add_sd(struct sd_tree *tree, const u8 *descriptor,
+                      size_t size)
+{
+       u8 hash[SHA1_HASH_SIZE];
+       int security_id;
+       struct sd_node *new;
+       u8 **descriptors;
+       u64 *sizes;
+       u8 *descr_copy;
+       struct wim_security_data *sd = tree->sd;
+       sha1_buffer(descriptor, size, hash);
+
+       security_id = lookup_sd(hash, tree->root);
+       if (security_id >= 0)
+               return security_id;
+
+       new = MALLOC(sizeof(struct sd_node));
+       if (!new)
+               return -1;
+       descr_copy = MALLOC(size);
+       if (!descr_copy)
+               goto out_free_node;
+       memcpy(descr_copy, descriptor, size);
+       new->security_id = tree->num_sds++;
+       new->left = NULL;
+       new->right = NULL;
+       copy_hash(new->hash, hash);
+
+       descriptors = REALLOC(sd->descriptors,
+                             (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
+       if (!descriptors)
+               goto out_free_descr;
+       sd->descriptors = descriptors;
+       sizes = REALLOC(sd->sizes,
+                       (sd->num_entries + 1) * sizeof(sd->sizes[0]));
+       if (!sizes)
+               goto out_free_descr;
+       sd->sizes = sizes;
+       sd->descriptors[sd->num_entries] = descr_copy;
+       sd->sizes[sd->num_entries] = size;
+       sd->num_entries++;
+       sd->total_length += size + 8;
+
+       if (tree->root)
+               insert_sd_node(tree->root, new);
+       else
+               tree->root = new;
+       return new->security_id;
+out_free_descr:
+       FREE(descr_copy);
+out_free_node:
+       FREE(new);
+       return -1;
+}
+
+#if 0
+static int build_sd_tree(struct wim_security_data *sd, struct sd_tree *tree)
+{
+       int ret;
+       u32 orig_num_entries = sd->num_entries;
+       u32 orig_total_length = sd->total_length;
+
+       tree->num_sds = 0;
+       tree->sd = sd;
+       tree->root = NULL;
+
+       for (u32 i = 0; i < sd->num_entries; i++) {
+               ret = tree_add_sd(tree, sd->descriptors[i], sd->sizes[i]);
+               if (ret < 0)
+                       goto out_revert;
+       }
+       return 0;
+out_revert:
+       sd->num_entries = orig_num_entries;
+       sd->total_length = orig_total_length;
+       free_sd_tree(tree->root);
+       return ret;
+}
+#endif
+
+static int __build_dentry_tree_ntfs(struct dentry *dentry, ntfs_inode *ni,
+                                   struct lookup_table *lookup_table,
+                                   struct sd_tree *tree)
+{
+       u32 attributes = ntfs_inode_get_attributes(ni);
+       int mrec_flags = ni->mrec->flags;
+       u32 sd_size;
+       int ret;
+
+       dentry->creation_time    = le64_to_cpu(ni->creation_time);
+       dentry->last_write_time  = le64_to_cpu(ni->last_data_change_time);
+       dentry->last_access_time = le64_to_cpu(ni->last_access_time);
+       dentry->security_id      = le32_to_cpu(ni->security_id);
+       dentry->attributes       = le32_to_cpu(attributes);
+
+       if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
+               if (attributes & FILE_ATTR_REPARSE_POINT) {
+                       /* Junction point */
+               } else {
+                       /* Normal directory */
+               }
+       } else {
+               if (attributes & FILE_ATTR_REPARSE_POINT) {
+                       /* Symbolic link or other reparse point */
+               } else {
+                       /* Normal file */
+               }
+       }
+       ret = ntfs_inode_get_security(ni,
+                                     OWNER_SECURITY_INFORMATION |
+                                     GROUP_SECURITY_INFORMATION |
+                                     DACL_SECURITY_INFORMATION  |
+                                     SACL_SECURITY_INFORMATION,
+                                     NULL, 0, &sd_size);
+       u8 sd[sd_size];
+       ret = ntfs_inode_get_security(ni,
+                                     OWNER_SECURITY_INFORMATION |
+                                     GROUP_SECURITY_INFORMATION |
+                                     DACL_SECURITY_INFORMATION  |
+                                     SACL_SECURITY_INFORMATION,
+                                     sd, sd_size, &sd_size);
+       dentry->security_id = tree_add_sd(tree, sd, sd_size);
+
+       return 0;
+}
+
+static int build_dentry_tree_ntfs(struct dentry *root_dentry,
+                                 const char *device,
+                                 struct lookup_table *lookup_table,
+                                 int flags)
+{
+       ntfs_volume *vol;
+       ntfs_inode *root_ni;
+       int ret = 0;
+       struct sd_tree tree;
+       tree.sd = CALLOC(1, sizeof(struct wim_security_data));
+       if (!tree.sd)
+               return WIMLIB_ERR_NOMEM;
+       tree.sd->total_length = 8;
+       tree.root = NULL;
+       
+       vol = ntfs_mount(device, MS_RDONLY);
+       if (!vol) {
+               ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s' read-only",
+                                device);
+               return WIMLIB_ERR_NTFS_3G;
+       }
+       root_ni = ntfs_inode_open(vol, FILE_root);
+       if (!root_ni) {
+               ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
+                                "`%s'", device);
+               ret = WIMLIB_ERR_NTFS_3G;
+               goto out;
+       }
+       ret = __build_dentry_tree_ntfs(root_dentry, root_ni, lookup_table, &tree);
+
+out:
+       if (ntfs_umount(vol, FALSE) != 0) {
+               ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'", device);
+               if (ret == 0)
+                       ret = WIMLIB_ERR_NTFS_3G;
+       }
+       return ret;
+}
 
 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
                                                const char *device,
@@ -51,15 +274,12 @@ WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
                                                const char *flags_element,
                                                int flags)
 {
-       int ret;
-
-       if (!device)
-               return WIMLIB_ERR_INVALID_PARAM;
        if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
                ERROR("Cannot dereference files when capturing directly from NTFS");
                return WIMLIB_ERR_INVALID_PARAM;
        }
-       return 0;
+       return do_add_image(w, device, name, description, flags_element, flags,
+                           build_dentry_tree_ntfs);
 }
 
 #else /* WITH_NTFS_3G */
index c44b9f40f76ce46cd78d7c12eec3c86a17d28498..a5d695d65a633d5e01428d56659b3067ea1e0f89 100644 (file)
@@ -25,6 +25,11 @@ static inline bool hashes_equal(const u8 h1[SHA1_HASH_SIZE],
 {
        return memcmp(h1, h2, SHA1_HASH_SIZE) == 0;
 }
+static inline int hashes_cmp(const u8 h1[SHA1_HASH_SIZE],
+                            const u8 h2[SHA1_HASH_SIZE])
+{
+       return memcmp(h1, h2, SHA1_HASH_SIZE);
+}
 
 /* Prints a hash code field. */
 static inline void print_hash(const u8 hash[SHA1_HASH_SIZE])
index abd0b86c11be76d4f2222ce7732a403671ea0914..4f390629f8881e76f195eab873129d24d77e4f2f 100644 (file)
@@ -334,6 +334,11 @@ extern int check_wim_integrity(WIMStruct *w, int show_progress, int *status);
 /* modify.c */
 extern void destroy_image_metadata(struct image_metadata *imd,
                                   struct lookup_table *lt);
+extern int do_add_image(WIMStruct *w, const char *dir, const char *name,
+                       const char *description, const char *flags_element,
+                       int flags,
+                       int (*capture_tree)(struct dentry *, const char *,
+                                    struct lookup_table *, int));
 
 /* resource.c */
 extern const u8 *get_resource_entry(const u8 *p, struct resource_entry *entry);