From 36011d478b941ecaffbbbe98ec82f83916908add Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Aug 2012 01:55:03 -0500 Subject: [PATCH] wimfs_link(), wimfs_symlink(), symlink fixes --- src/dentry.c | 61 +++++++++--------- src/dentry.h | 4 +- src/io.h | 6 +- src/modify.c | 39 +----------- src/mount.c | 94 +++++++++++++++++++++++++++- src/resource.c | 7 +-- src/symlink.c | 141 +++++++++++++++++++++++++++++++----------- src/wimlib_internal.h | 3 + 8 files changed, 239 insertions(+), 116 deletions(-) diff --git a/src/dentry.c b/src/dentry.c index e5362e1b..2ab69484 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -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; -} diff --git a/src/dentry.h b/src/dentry.h index 469946f8..f684a7a0 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -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 */ diff --git a/src/io.h b/src/io.h index 5ab46d22..dc49f5c0 100644 --- 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 */ diff --git a/src/modify.c b/src/modify.c index 947316aa..744ab2d7 100644 --- a/src/modify.c +++ b/src/modify.c @@ -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; diff --git a/src/mount.c b/src/mount.c index 984666e6..18a28558 100644 --- a/src/mount.c +++ b/src/mount.c @@ -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; } diff --git a/src/resource.c b/src/resource.c index e892e6ad..731bd1c0 100644 --- a/src/resource.c +++ b/src/resource.c @@ -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; diff --git a/src/symlink.c b/src/symlink.c index e243a42a..9de1fb50 100644 --- a/src/symlink.c +++ b/src/symlink.c @@ -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 = <e->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; } diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index 86b0716d..d9c77756 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -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(); -- 2.43.0