wimfs_link(), wimfs_symlink(), symlink fixes
authorEric Biggers <ebiggers3@gmail.com>
Sun, 19 Aug 2012 06:55:03 +0000 (01:55 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 19 Aug 2012 06:55:03 +0000 (01:55 -0500)
src/dentry.c
src/dentry.h
src/io.h
src/modify.c
src/mount.c
src/resource.c
src/symlink.c
src/wimlib_internal.h

index e5362e1b23a13f324618bcf8c9d8c238e006ee02..2ab69484e8fbe2eadfbfc0fa28d2bb16f405a2d8 100644 (file)
@@ -99,6 +99,7 @@ void dentry_to_stbuf(const struct dentry *dentry, struct stat *stbuf,
                stbuf->st_size = 0;
 
        stbuf->st_nlink   = dentry_link_group_size(dentry);
+       stbuf->st_ino     = dentry->hard_link;
        stbuf->st_uid     = getuid();
        stbuf->st_gid     = getgid();
        stbuf->st_atime   = ms_timestamp_to_unix(dentry->last_access_time);
@@ -504,7 +505,7 @@ err:
        return NULL;
 }
 
-static void dentry_free_ads_entries(struct dentry *dentry)
+void dentry_free_ads_entries(struct dentry *dentry)
 {
        for (u16 i = 0; i < dentry->num_ads; i++)
                destroy_ads_entry(&dentry->ads_entries[i]);
@@ -524,6 +525,21 @@ void free_dentry(struct dentry *dentry)
        FREE(dentry);
 }
 
+/* clones a dentry.
+ *
+ * Beware:
+ *     - memory for file names is not cloned
+ *     - next, prev, and children pointers and not touched
+ *     - stream entries are not cloned.
+ */
+struct dentry *clone_dentry(struct dentry *old)
+{
+       struct dentry *new = MALLOC(sizeof(struct dentry));
+       if (!new)
+               return NULL;
+       return memcpy(new, old, sizeof(struct dentry));
+}
+
 /* Arguments for do_free_dentry(). */
 struct free_dentry_args {
        struct lookup_table *lookup_table;
@@ -899,13 +915,9 @@ int read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
         */
        if (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
                /* ??? */
-               u32 u1, u2;
-               p = get_u32(p, &u1);
-               /*p += 4;*/
+               p += 4;
                p = get_u32(p, &dentry->reparse_tag);
-               p = get_u32(p, &u2);
-               /*p += 4;*/
-               dentry->hard_link = (u64)(u1) | ((u64)(u2) << 32);
+               p += 4;
        } else {
                p = get_u32(p, &dentry->reparse_tag);
                p = get_u64(p, &dentry->hard_link);
@@ -1043,7 +1055,7 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
 {
        u8 *orig_p = p;
        unsigned padding;
-       memset(p, 0, dentry->length);
+
        p = put_u64(p, dentry->length);
        p = put_u32(p, dentry->attributes);
        p = put_u32(p, dentry->security_id);
@@ -1053,11 +1065,16 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
        p = put_u64(p, dentry->creation_time);
        p = put_u64(p, dentry->last_access_time);
        p = put_u64(p, dentry->last_write_time);
-       memcpy(p, dentry->hash, WIM_HASH_SIZE);
-       p += WIM_HASH_SIZE;
-       p = put_u32(p, dentry->reparse_tag);
-       p = put_u64(p, dentry->hard_link);
-       p = put_u16(p, dentry->num_ads); /*streams */
+       p = put_bytes(p, WIM_HASH_SIZE, dentry->hash);
+       if (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+               p = put_zeroes(p, 4);
+               p = put_u32(p, dentry->reparse_tag);
+               p = put_zeroes(p, 4);
+       } else {
+               p = put_u32(p, dentry->reparse_tag);
+               p = put_u64(p, dentry->hard_link);
+       }
+       p = put_u16(p, dentry->num_ads);
        p = put_u16(p, dentry->short_name_len);
        p = put_u16(p, dentry->file_name_len);
        p = put_bytes(p, dentry->file_name_len, (u8*)dentry->file_name);
@@ -1095,8 +1112,8 @@ u8 *write_dentry_tree(const struct dentry *tree, u8 *p)
                /* write end of directory entry */
                p = put_u64(p, 0);
        } else {
-               /* Nothing to do for a regular file. */
-               if (dentry_is_regular_file(tree))
+               /* Nothing to do for non-directories */
+               if (!dentry_is_directory(tree))
                        return p;
        }
 
@@ -1208,17 +1225,3 @@ int read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len,
        dentry->children = first_child;
        return ret;
 }
-
-int dentry_set_symlink_buf(struct dentry *dentry, const u8 symlink_buf_hash[])
-{
-       struct ads_entry *ads_entries;
-
-       ads_entries = CALLOC(2, sizeof(struct ads_entry));
-       if (!ads_entries)
-               return WIMLIB_ERR_NOMEM;
-       memcpy(ads_entries[1].hash, symlink_buf_hash, WIM_HASH_SIZE);
-       dentry_free_ads_entries(dentry);
-       dentry->num_ads = 2;
-       dentry->ads_entries = ads_entries;
-       return 0;
-}
index 469946f8019b812b4db66d0837b8ebb390bd201a..f684a7a0340f25ff3887e127a5f429a589dbe7c2 100644 (file)
@@ -268,7 +268,9 @@ extern void dentry_update_all_timestamps(struct dentry *dentry);
 extern void init_dentry(struct dentry *dentry, const char *name);
 extern struct dentry *new_dentry(const char *name);
 
+extern void dentry_free_ads_entries(struct dentry *dentry);
 extern void free_dentry(struct dentry *dentry);
+extern struct dentry *clone_dentry(struct dentry *old);
 extern void free_dentry_tree(struct dentry *root,
                             struct lookup_table *lookup_table, 
                             bool lt_decrement_refcnt);
@@ -290,8 +292,6 @@ extern int read_dentry_tree(const u8 metadata_resource[],
 
 extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p);
 
-extern int dentry_set_symlink_buf(struct dentry *dentry,
-                                 const u8 symlink_buf_hash[]);
 
 /* Inline utility functions for WIMDentries */
 
index 5ab46d22c5f8f42262976bb6b68658967568d2f9..dc49f5c08e92887f422e3fce1134dc16d5c4317d 100644 (file)
--- a/src/io.h
+++ b/src/io.h
@@ -110,13 +110,11 @@ static inline const u8 *get_bytes(const u8 *p, size_t num_bytes, void *res)
 
 static inline u8 *put_zeroes(u8 *p, size_t num_bytes)
 {
-       memset(p, 0, num_bytes);
-       return p + num_bytes;
+       return (u8*)memset(p, 0, num_bytes) + num_bytes;
 }
 
 static inline u8 *put_bytes(u8 *p, size_t num_bytes, const u8 *input)
 {
-       memcpy(p, input, num_bytes);
-       return p + num_bytes;
+       return (u8*)memcpy(p, input, num_bytes) + num_bytes;
 }
 #endif /* _WIMLIB_IO_H */
index 947316aa9fb5fe4fb566eebcb564e41710004e20..744ab2d7cdb42b908a80e3a944e095272e5eff8b 100644 (file)
@@ -153,44 +153,7 @@ static int build_dentry_tree(struct dentry *root, const char *root_disk_path,
                }
                deref_name_buf[ret] = '\0';
                DEBUG("Read symlink `%s'", deref_name_buf);
-               void *symlink_buf = make_symlink_reparse_data_buf(deref_name_buf,
-                                                                 &symlink_buf_len);
-               if (!symlink_buf)
-                       return WIMLIB_ERR_NOMEM;
-               DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)",
-                               symlink_buf_len, ret);
-               
-               u8 symlink_buf_hash[WIM_HASH_SIZE];
-               sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash);
-
-               ret = dentry_set_symlink_buf(root, symlink_buf_hash);
-
-               if (ret != 0) {
-                       FREE(symlink_buf);
-                       return ret;
-               }
-               DEBUG("Created symlink buf");
-
-               struct lookup_table_entry *lte;
-               struct lookup_table_entry *existing_lte;
-
-               existing_lte = __lookup_resource(lookup_table, symlink_buf_hash);
-               if (existing_lte) {
-                       existing_lte->refcnt++;
-               } else {
-                       DEBUG("Creating new lookup table entry");
-                       lte = new_lookup_table_entry();
-                       if (!lte) {
-                               FREE(symlink_buf);
-                               return WIMLIB_ERR_NOMEM;
-                       }
-                       lte->symlink_buf = symlink_buf;
-                       lte->resource_entry.original_size = symlink_buf_len;
-                       lte->resource_entry.size = symlink_buf_len;
-                       lte->is_symlink = true;
-                       memcpy(lte->hash, symlink_buf_hash, WIM_HASH_SIZE);
-                       lookup_table_insert(lookup_table, lte);
-               }
+               ret = dentry_set_symlink(root, deref_name_buf, lookup_table);
        } else {
                /* Regular file */
                struct lookup_table_entry *lte;
index 984666e6b6c2c88e35f0fd55e325694a4b0d888a..18a28558181025f715c449668b14a8c51ca7fa3b 100644 (file)
@@ -716,6 +716,14 @@ done:
        close_message_queues();
 }
 
+static int wimfs_fgetattr(const char *path, struct stat *stbuf,
+                         struct fuse_file_info *fi)
+{
+       struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+       dentry_to_stbuf(fd->dentry, stbuf, w->lookup_table);
+       return 0;
+}
+
 static int wimfs_ftruncate(const char *path, off_t size,
                           struct fuse_file_info *fi)
 {
@@ -739,6 +747,45 @@ static int wimfs_getattr(const char *path, struct stat *stbuf)
        return 0;
 }
 
+/* Create a hard link */
+static int wimfs_link(const char *to, const char *from)
+{
+       struct dentry *to_dentry, *from_dentry, *from_dentry_parent;
+       const char *link_name;
+
+       to_dentry = get_dentry(w, to);
+       if (!to_dentry)
+               return -ENOENT;
+       if (!dentry_is_regular_file(to_dentry))
+               return -EPERM;
+
+       from_dentry_parent = get_parent_dentry(w, from);
+       if (!from_dentry_parent)
+               return -ENOENT;
+       if (!dentry_is_directory(from_dentry_parent))
+               return -ENOTDIR;
+
+       link_name = path_basename(from);
+       if (get_dentry_child_with_name(from_dentry_parent, link_name))
+               return -EEXIST;
+       from_dentry = clone_dentry(to_dentry);
+       if (!from_dentry)
+               return -ENOMEM;
+       if (change_dentry_name(from_dentry, link_name) != 0) {
+               FREE(from_dentry);
+               return -ENOMEM;
+       }
+       if (calculate_dentry_full_path(from_dentry, to_dentry) != 0) {
+               FREE(from_dentry->file_name);
+               FREE(from_dentry->file_name_utf8);
+               FREE(from_dentry);
+               return -ENOMEM;
+       }
+       list_add(&from_dentry->link_group_list, &to_dentry->link_group_list);
+       link_dentry(from_dentry, from_dentry_parent);
+       return 0;
+}
+
 /* 
  * Create a directory in the WIM.  
  * @mode is currently ignored.
@@ -1048,6 +1095,46 @@ static int wimfs_rmdir(const char *path)
        return 0;
 }
 
+static int wimfs_symlink(const char *to, const char *from)
+{
+       struct dentry *dentry_parent, *dentry;
+       const char *link_name;
+       
+       dentry_parent = get_parent_dentry(w, from);
+       if (!dentry_parent)
+               return -ENOENT;
+       if (!dentry_is_directory(dentry_parent))
+               return -ENOTDIR;
+
+       link_name = path_basename(from);
+
+       if (get_dentry_child_with_name(dentry_parent, link_name))
+               return -EEXIST;
+       dentry = new_dentry(link_name);
+       if (!dentry)
+               return -ENOMEM;
+
+       if (!change_dentry_name(dentry, link_name)) {
+               FREE(dentry);
+               return -ENOMEM;
+       }
+
+       if (calculate_dentry_full_path(dentry, NULL) != 0)
+               goto out_free_dentry;
+
+       dentry->attributes = FILE_ATTRIBUTE_REPARSE_POINT;
+       dentry->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
+
+       if (dentry_set_symlink(dentry, to, w->lookup_table) != 0)
+               goto out_free_dentry;
+
+       link_dentry(dentry, dentry_parent);
+       return 0;
+out_free_dentry:
+       free_dentry(dentry);
+       return -ENOMEM;
+}
+
 
 /* Reduce the size of a file */
 static int wimfs_truncate(const char *path, off_t size)
@@ -1171,11 +1258,13 @@ static int wimfs_write(const char *path, const char *buf, size_t size,
 }
 
 
-static struct fuse_operations wimfs_oper = {
+static struct fuse_operations wimfs_operations = {
        .access    = wimfs_access,
        .destroy   = wimfs_destroy,
+       .fgetattr  = wimfs_fgetattr,
        .ftruncate = wimfs_ftruncate,
        .getattr   = wimfs_getattr,
+       .link      = wimfs_link,
        .mkdir     = wimfs_mkdir,
        .mknod     = wimfs_mknod,
        .open      = wimfs_open,
@@ -1186,6 +1275,7 @@ static struct fuse_operations wimfs_oper = {
        .release   = wimfs_release,
        .rename    = wimfs_rename,
        .rmdir     = wimfs_rmdir,
+       .symlink   = wimfs_symlink,
        .truncate  = wimfs_truncate,
        .unlink    = wimfs_unlink,
        .utimens   = wimfs_utimens,
@@ -1267,7 +1357,7 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir,
        w = wim;
        mount_flags = flags;
 
-       ret = fuse_main(argc, argv, &wimfs_oper, NULL);
+       ret = fuse_main(argc, argv, &wimfs_operations, NULL);
 
        return (ret == 0) ? 0 : WIMLIB_ERR_FUSE;
 }
index e892e6ada19fa6c27f78f252a5bd592735cffe67..731bd1c0c3ebb258cbd8462b33ae1aaf5456ab79 100644 (file)
@@ -1110,8 +1110,7 @@ static int write_file_resource(WIMStruct *w, const u8 hash[])
        if (!lte)
                return 0;
 
-       /* No need to write file resources twice.  (This indicates file
-        * resources that are part of a hard link set.) */
+       /* No need to write file resources twice. */
        if (++lte->out_refcnt != 1)
                return 0;
 
@@ -1144,8 +1143,8 @@ static int write_file_resource(WIMStruct *w, const u8 hash[])
 
                len = lte->resource_entry.original_size;
 
-               recompress_resource(in_fp, lte->symlink_buf, len, len, 0,
-                                   0, out_fp, out_wim_ctype, &new_size);
+               ret = recompress_resource(NULL, lte->symlink_buf, len, len, 0,
+                                         0, out_fp, out_wim_ctype, &new_size);
                output_res_entry->size = new_size;
                output_res_entry->original_size = len;
                output_res_entry->offset = offset;
index e243a42a01acc73a34896baf3694332ee34250b3..9de1fb50cb2040ebc9995f4ebb7a5b5e7df88625 100644 (file)
@@ -1,6 +1,7 @@
 #include "dentry.h"
 #include "io.h"
 #include "lookup_table.h"
+#include "sha1.h"
 
 /*
  * Find the symlink target of a symbolic link or junction point in the WIM.
@@ -12,7 +13,7 @@
  */
 static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
                                char *buf, size_t buf_len,
-                               bool is_junction_point)
+                               u32 reparse_tag)
 {
        const u8 *p = resource;
        u16 substitute_name_offset;
@@ -25,6 +26,7 @@ static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
        unsigned header_size;
        char *translated_target;
        bool is_absolute;
+       u32 flags;
 
        if (resource_len < 12)
                return -EIO;
@@ -32,13 +34,23 @@ static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
        p = get_u16(p, &substitute_name_len);
        p = get_u16(p, &print_name_offset);
        p = get_u16(p, &print_name_len);
-       if (is_junction_point) {
+       get_u32(p, &flags);
+
+       wimlib_assert(reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+                     reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
+
+       /* I think that some junction points incorrectly get marked as symbolic
+        * links.  So, parse the link buffer as a symlink if the flags seem
+        * plausible. */
+       if (flags <= 1)
+               reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
+
+       if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT) {
                header_size = 8;
        } else {
-               u32 flags;
-               p = get_u32(p, &flags);
                is_absolute = (flags & 1) ? false : true;
                header_size = 12;
+               p += 4;
        }
        if (header_size + substitute_name_offset + substitute_name_len > resource_len)
                return -EIO;
@@ -55,7 +67,7 @@ static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
        }
 
        translated_target = link_target;
-       if (is_junction_point || is_absolute) {
+       if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT || is_absolute) {
                if (link_target_len < 7
                      || memcmp(translated_target, "\\??\\", 4) != 0
                      || translated_target[4] == '\0'
@@ -66,10 +78,13 @@ static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
                }
                translated_target += 4;
                link_target_len -= 4;
+               /* There's a drive letter, so just leave the backslashes since
+                * it won't go anyhwere on UNIX anyway... */
+       } else {
+               for (size_t i = 0; i < link_target_len; i++)
+                       if (translated_target[i] == '\\')
+                               translated_target[i] = '/';
        }
-       for (size_t i = 0; i < link_target_len; i++)
-               if (translated_target[i] == '\\')
-                       translated_target[i] = '/';
 
        memcpy(buf, translated_target, link_target_len + 1);
        ret = link_target_len;
@@ -114,42 +129,25 @@ out:
 ssize_t dentry_readlink(const struct dentry *dentry, char *buf, size_t buf_len,
                        const WIMStruct *w)
 {
-       struct ads_entry *ads;
-       struct lookup_table_entry *entry;
        struct resource_entry *res_entry;
-       bool is_junction_point;
+       struct lookup_table_entry *lte;
+       u16 i = 0;
+       const u8 *hash = dentry->hash;
 
        wimlib_assert(dentry_is_symlink(dentry));
 
-       if (dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK) {
-               is_junction_point = false;
-               /* 
-                * This is of course not actually documented, but what I think is going
-                * on here is that the symlink dentries have 2 alternate data streams;
-                * one is the default data stream, which is not used and is empty, and
-                * one is the symlink buffer data stream, which is confusingly also
-                * unnamed, but isn't empty as it contains the symlink target within the
-                * resource.
-                */
-               if (dentry->num_ads != 2)
+       while (1) {
+               if ((lte = __lookup_resource(w->lookup_table, hash)))
+                       break;
+               if (i == dentry->num_ads)
                        return -EIO;
-               if ((entry = __lookup_resource(w->lookup_table, dentry->ads_entries[0].hash)))
-                       goto do_readlink;
-               if ((entry = __lookup_resource(w->lookup_table, dentry->ads_entries[1].hash)))
-                       goto do_readlink;
-       } else {
-               wimlib_assert(dentry->reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
-
-               is_junction_point = true;
-
-               if ((entry = __lookup_resource(w->lookup_table, dentry->hash)))
-                       goto do_readlink;
+               hash = dentry->ads_entries[i].hash;
+               i++;
        }
-       return -EIO;
-do_readlink:
-       res_entry = &entry->resource_entry;
+       res_entry = &lte->resource_entry;
        if (res_entry->original_size > 10000)
                return -EIO;
+
        char res_buf[res_entry->original_size];
        if (read_full_resource(w->fp, res_entry->size, 
                               res_entry->original_size,
@@ -158,5 +156,74 @@ do_readlink:
                               res_buf) != 0)
                return -EIO;
        return get_symlink_name(res_buf, res_entry->original_size, buf,
-                               buf_len, is_junction_point);
+                               buf_len, dentry->reparse_tag);
+}
+
+static int dentry_set_symlink_buf(struct dentry *dentry,
+                                 const u8 symlink_buf_hash[])
+{
+       struct ads_entry *ads_entries;
+
+       ads_entries = CALLOC(2, sizeof(struct ads_entry));
+       if (!ads_entries)
+               return WIMLIB_ERR_NOMEM;
+       memcpy(ads_entries[1].hash, symlink_buf_hash, WIM_HASH_SIZE);
+       dentry_free_ads_entries(dentry);
+       dentry->num_ads = 2;
+       dentry->ads_entries = ads_entries;
+       return 0;
+}
+
+int dentry_set_symlink(struct dentry *dentry, const char *target,
+                      struct lookup_table *lookup_table)
+
+{
+       int ret;
+       size_t symlink_buf_len;
+       struct lookup_table_entry *lte = NULL, *existing_lte;
+       u8 symlink_buf_hash[WIM_HASH_SIZE];
+       void *symlink_buf;
+       
+       symlink_buf = make_symlink_reparse_data_buf(target, &symlink_buf_len);
+       if (!symlink_buf)
+               return WIMLIB_ERR_NOMEM;
+
+       DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)",
+                       symlink_buf_len, ret);
+       
+       sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash);
+
+       existing_lte = __lookup_resource(lookup_table, symlink_buf_hash);
+
+       if (existing_lte) {
+               existing_lte->refcnt++;
+       } else {
+               DEBUG("Creating new lookup table entry for symlink buf");
+               lte = new_lookup_table_entry();
+               if (!lte) {
+                       ret = WIMLIB_ERR_NOMEM;
+                       goto out_free_symlink_buf;
+               }
+               lte->is_symlink = true;
+               lte->symlink_buf = symlink_buf;
+               lte->resource_entry.original_size = symlink_buf_len;
+               lte->resource_entry.size = symlink_buf_len;
+               memcpy(lte->hash, symlink_buf_hash, WIM_HASH_SIZE);
+       }
+
+       ret = dentry_set_symlink_buf(dentry, symlink_buf_hash);
+
+       if (ret != 0)
+               goto out_free_lte;
+
+       DEBUG("Loaded symlink buf");
+
+       if (!existing_lte)
+               lookup_table_insert(lookup_table, lte);
+       return 0;
+out_free_lte:
+       FREE(lte);
+out_free_symlink_buf:
+       FREE(symlink_buf);
+       return ret;
 }
index 86b0716d0d3b148efb817cf106a87e2fe53eefc2..d9c7775647063c3e3732c71338bd66b302ca874d 100644 (file)
@@ -409,6 +409,9 @@ ssize_t dentry_readlink(const struct dentry *dentry, char *buf, size_t buf_len,
                        const WIMStruct *w);
 extern void *make_symlink_reparse_data_buf(const char *symlink_target,
                                           size_t *len_ret);
+extern int dentry_set_symlink(struct dentry *dentry,
+                             const char *target,
+                             struct lookup_table *lookup_table);
 
 /* wim.c */
 extern WIMStruct *new_wim_struct();