From 9b1647d673092a6c53a90c1d89831a5131660b9d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 3 Sep 2012 15:39:03 -0500 Subject: [PATCH] inode updates (IN PROGRESS) --- src/dentry.c | 168 +++++----- src/dentry.h | 92 +++--- src/hardlink.c | 6 - src/join.c | 1 - src/list.h | 15 - src/lookup_table.c | 74 +++-- src/lookup_table.h | 24 +- src/mount.c | 744 ++++++++++++++++----------------------------- 8 files changed, 451 insertions(+), 673 deletions(-) diff --git a/src/dentry.c b/src/dentry.c index 043ceade..a9d13f69 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -131,7 +131,8 @@ void inode_update_all_timestamps(struct inode *inode) /* Returns the alternate data stream entry belonging to @inode that has the * stream name @stream_name. */ struct ads_entry *inode_get_ads_entry(struct inode *inode, - const char *stream_name) + const char *stream_name, + u16 *idx_ret) { size_t stream_name_len; if (!stream_name) @@ -142,7 +143,11 @@ struct ads_entry *inode_get_ads_entry(struct inode *inode, do { if (ads_entry_has_name(inode->ads_entries[i], stream_name, stream_name_len)) + { + if (idx_ret) + *idx_ret = i; return inode->ads_entries[i]; + } } while (++i != inode->num_ads); } return NULL; @@ -154,8 +159,6 @@ static struct ads_entry *new_ads_entry(const char *name) struct ads_entry *ads_entry = CALLOC(1, sizeof(struct ads_entry)); if (!ads_entry) return NULL; - INIT_LIST_HEAD(&ads_entry->lte_group_list.list); - ads_entry->lte_group_list.type = STREAM_TYPE_ADS; if (name && *name) { if (change_ads_name(ads_entry, name)) { FREE(ads_entry); @@ -175,7 +178,7 @@ struct ads_entry *inode_add_ads(struct inode *inode, const char *stream_name) struct ads_entry **ads_entries; struct ads_entry *new_entry; - if (inode->num_ads == 0xffff) { + if (inode->num_ads >= 0xfffe) { ERROR("Too many alternate data streams in one inode!"); return NULL; } @@ -212,7 +215,7 @@ int for_dentry_in_tree(struct dentry *root, if (ret != 0) return ret; - child = root->children; + child = root->inode->children; if (!child) return 0; @@ -222,7 +225,7 @@ int for_dentry_in_tree(struct dentry *root, if (ret != 0) return ret; child = child->next; - } while (child != root->children); + } while (child != root->inode->children); return 0; } @@ -237,7 +240,7 @@ int for_dentry_in_tree_depth(struct dentry *root, struct dentry *child; struct dentry *next; - child = root->children; + child = root->inode->children; if (child) { do { next = child->next; @@ -245,7 +248,7 @@ int for_dentry_in_tree_depth(struct dentry *root, if (ret != 0) return ret; child = next; - } while (child != root->children); + } while (child != root->inode->children); } return visitor(root, arg); } @@ -311,7 +314,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p) { struct dentry *child; - child = dentry->children; + child = dentry->inode->children; dentry->subdir_offset = *subdir_offset_p; if (child) { @@ -320,7 +323,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p) do { *subdir_offset_p += dentry_correct_total_length(child); child = child->next; - } while (child != dentry->children); + } while (child != dentry->inode->children); /* End-of-directory dentry on disk. */ *subdir_offset_p += 8; @@ -330,7 +333,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p) do { calculate_subdir_offsets(child, subdir_offset_p); child = child->next; - } while (child != dentry->children); + } while (child != dentry->inode->children); } else { /* On disk, childless directories have a valid subdir_offset * that points to an 8-byte end-of-directory dentry. Regular @@ -351,14 +354,14 @@ struct dentry *get_dentry_child_with_name(const struct dentry *dentry, struct dentry *child; size_t name_len; - child = dentry->children; + child = dentry->inode->children; if (child) { name_len = strlen(name); do { if (dentry_has_name(child, name, name_len)) return child; child = child->next; - } while (child != dentry->children); + } while (child != dentry->inode->children); } return NULL; } @@ -375,14 +378,14 @@ static struct dentry *get_dentry_relative_path(struct dentry *cur_dir, if (*path == '\0') return cur_dir; - child = cur_dir->children; + child = cur_dir->inode->children; if (child) { new_path = path_next_part(path, &base_len); do { if (dentry_has_name(child, path, base_len)) return get_dentry_relative_path(child, new_path); child = child->next; - } while (child != cur_dir->children); + } while (child != cur_dir->inode->children); } return NULL; } @@ -397,6 +400,16 @@ struct dentry *get_dentry(WIMStruct *w, const char *path) return get_dentry_relative_path(root, path); } +struct inode *wim_pathname_to_inode(WIMStruct *w, const char *path) +{ + struct dentry *dentry; + dentry = get_dentry(w, path); + if (!dentry) + return NULL; + else + return dentry->inode; +} + /* Returns the dentry that corresponds to the parent directory of @path, or NULL * if the dentry is not found. */ struct dentry *get_parent_dentry(WIMStruct *w, const char *path) @@ -591,8 +604,7 @@ struct dentry *new_dentry_with_inode(const char *name) if (dentry) { dentry->inode = new_inode(); if (dentry->inode) { - list_add(&dentry->inode_dentry_list, - &dentry->inode->dentry_list); + inode_add_dentry(dentry, dentry->inode); } else { free_dentry(dentry); dentry = NULL; @@ -601,7 +613,7 @@ struct dentry *new_dentry_with_inode(const char *name) return dentry; } -static void free_ads_entry(struct ads_entry *entry) +void free_ads_entry(struct ads_entry *entry) { if (entry) { FREE(entry->stream_name); @@ -610,53 +622,7 @@ static void free_ads_entry(struct ads_entry *entry) } } - -#ifdef WITH_FUSE -/* Remove an alternate data stream from a dentry. - * - * The corresponding lookup table entry for the stream is NOT changed. - * - * @dentry: The dentry - * @ads_entry: The alternate data stream entry (it MUST be one of the - * ads_entry's in the array dentry->ads_entries). - */ -void dentry_remove_ads(struct dentry *dentry, struct ads_entry *ads_entry) -{ - u16 idx; - u16 following; - - wimlib_assert(dentry->num_ads); - idx = ads_entry - dentry->ads_entries; - wimlib_assert(idx < dentry->num_ads); - following = dentry->num_ads - idx - 1; - - destroy_ads_entry(ads_entry); - memcpy(ads_entry, ads_entry + 1, following * sizeof(struct ads_entry)); - - /* We moved the ADS entries. Adjust the stream lists. */ - for (u16 i = 0; i < following; i++) { - struct list_head *cur = &ads_entry[i].lte_group_list.list; - struct list_head *prev = cur->prev; - struct list_head *next; - if ((u8*)prev >= (u8*)(ads_entry + 1) - && (u8*)prev < (u8*)(ads_entry + following + 1)) { - cur->prev = (struct list_head*)((u8*)prev - sizeof(struct ads_entry)); - } else { - prev->next = cur; - } - next = cur->next; - if ((u8*)next >= (u8*)(ads_entry + 1) - && (u8*)next < (u8*)(ads_entry + following + 1)) { - cur->next = (struct list_head*)((u8*)next - sizeof(struct ads_entry)); - } else { - next->prev = cur; - } - } - dentry->num_ads--; -} -#endif - -static void inode_free_ads_entries(struct inode *inode) +void inode_free_ads_entries(struct inode *inode) { if (inode->ads_entries) { for (u16 i = 0; i < inode->num_ads; i++) @@ -665,36 +631,66 @@ static void inode_free_ads_entries(struct inode *inode) } } +/* Frees an inode. */ void free_inode(struct inode *inode) { if (inode) { inode_free_ads_entries(inode); + #ifdef WITH_FUSE + wimlib_assert(inode->num_opened_fds == 0); + FREE(inode->fds); + #endif FREE(inode); } } -void put_inode(struct inode *inode) +/* Decrements link count on an inode and frees it if the link count reaches 0. + * */ +struct inode *put_inode(struct inode *inode) { if (inode) { wimlib_assert(inode->link_count); - if (--inode->link_count == 0) - free_inode(inode); + if (--inode->link_count == 0) { + #ifdef WITH_FUSE + if (inode->num_opened_fds == 0) + #endif + { + free_inode(inode); + inode = NULL; + } + } } + return inode; } -/* Frees a WIM dentry. */ -void free_dentry(struct dentry *dentry) +/* Frees a WIM dentry. + * + * The inode is freed only if its link count is decremented to 0. + */ +struct inode *free_dentry(struct dentry *dentry) { wimlib_assert(dentry); + struct inode *inode; FREE(dentry->file_name); FREE(dentry->file_name_utf8); FREE(dentry->short_name); FREE(dentry->full_path_utf8); - put_inode(dentry->inode); + inode = put_inode(dentry->inode); FREE(dentry); + return inode; } +void put_dentry(struct dentry *dentry) +{ + wimlib_assert(dentry); + wimlib_assert(dentry->refcnt); + + if (--dentry->refcnt == 0) + free_dentry(dentry); +} + +#if 0 /* Partically clones a dentry. * * Beware: @@ -716,6 +712,7 @@ struct dentry *clone_dentry(struct dentry *old) new->short_name_len = 0; return new; } +#endif /* * This function is passed as an argument to for_dentry_in_tree_depth() in order @@ -774,36 +771,35 @@ void link_dentry(struct dentry *dentry, struct dentry *parent) { wimlib_assert(dentry_is_directory(parent)); dentry->parent = parent; - if (parent->children) { + if (parent->inode->children) { /* Not an only child; link to siblings. */ - dentry->next = parent->children; - dentry->prev = parent->children->prev; + dentry->next = parent->inode->children; + dentry->prev = parent->inode->children->prev; dentry->next->prev = dentry; dentry->prev->next = dentry; } else { /* Only child; link to parent. */ - parent->children = dentry; + parent->inode->children = dentry; dentry->next = dentry; dentry->prev = dentry; } } -/* Unlink a dentry from the directory tree. +/* + * Unlink a dentry from the directory tree. * - * Note: This merely removes it from the in-memory tree structure. See - * remove_dentry() in mount.c for a function implemented on top of this one that - * frees the dentry and implements reference counting for the lookup table - * entries. */ + * Note: This merely removes it from the in-memory tree structure. + */ void unlink_dentry(struct dentry *dentry) { if (dentry_is_root(dentry)) return; if (dentry_is_only_child(dentry)) { - dentry->parent->children = NULL; + dentry->parent->inode->children = NULL; } else { if (dentry_is_first_sibling(dentry)) - dentry->parent->children = dentry->next; + dentry->parent->inode->children = dentry->next; dentry->next->prev = dentry->prev; dentry->prev->next = dentry->next; } @@ -1521,12 +1517,12 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p) * recursively writing the directory trees rooted at each of the child * dentries, since the on-disk dentries for a dentry's children are * always located at consecutive positions in the metadata resource! */ - child = parent->children; + child = parent->inode->children; if (child) { do { p = write_dentry(child, p); child = child->next; - } while (child != parent->children); + } while (child != parent->inode->children); } /* write end of directory entry */ @@ -1537,7 +1533,7 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p) do { p = write_dentry_tree_recursive(child, p); child = child->next; - } while (child != parent->children); + } while (child != parent->inode->children); } return p; } @@ -1658,6 +1654,6 @@ int read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len, prev_child->next = first_child; first_child->prev = prev_child; } - dentry->children = first_child; + dentry->inode->children = first_child; return ret; } diff --git a/src/dentry.h b/src/dentry.h index 355c492a..28d5ab33 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -80,11 +80,9 @@ struct ads_entry { /* Stream name (UTF-8) */ char *stream_name_utf8; - /* Doubly linked list of streams that share the same lookup table entry */ - struct stream_list_head lte_group_list; - - /* Containing inode */ - struct inode *inode; +#ifdef WITH_FUSE + u32 stream_id; +#endif }; /* Returns the total length of a WIM alternate data stream entry on-disk, @@ -115,6 +113,8 @@ static inline bool ads_entries_have_same_name(const struct ads_entry *entry_1, entry_1->stream_name_len) == 0; } +struct wimlib_fd; + struct inode { /* Timestamps for the inode. The timestamps are the number of * 100-nanosecond intervals that have elapsed since 12:00 A.M., January @@ -169,15 +169,24 @@ struct inode { u64 ino; struct list_head dentry_list; - union { - struct stream_list_head lte_group_list; - struct hlist_node hlist; - }; + struct hlist_node hlist; char *extracted_file; + + struct dentry *children; + +#ifdef WITH_FUSE + u16 num_opened_fds; + u16 num_allocated_fds; + struct wimlib_fd **fds; + u32 next_stream_id; +#endif }; #define inode_for_each_dentry(dentry, inode) \ - list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) + list_for_each_entry((dentry), &(inode)->dentry_list, inode_dentry_list) + +#define inode_add_dentry(dentry, inode) \ + list_add(&(dentry)->inode_dentry_list, &(inode)->dentry_list) /* * In-memory structure for a WIM directory entry (dentry). There is a directory @@ -203,6 +212,9 @@ struct inode { * conflicting fields to split up the hard link groups. */ struct dentry { + /* The inode for this dentry */ + struct inode *inode; + /* The parent of this directory entry. */ struct dentry *parent; @@ -210,11 +222,6 @@ struct dentry { struct dentry *next; struct dentry *prev; - /* Pointer to a child of this directory entry. */ - struct dentry *children; - - struct inode *inode; - /* * Size of directory entry on disk, in bytes. Typical size is around * 104 to 120 bytes. @@ -276,15 +283,9 @@ struct dentry { char *full_path_utf8; u32 full_path_utf8_len; - union { - /* Number of references to the dentry tree itself, as in multiple - * WIMStructs */ - u32 refcnt; - - /* Number of times this dentry has been opened (only for - * directories!) */ - u32 num_times_opened; - }; + /* Number of references to the dentry tree itself, as in multiple + * WIMStructs */ + u32 refcnt; /* List of dentries in the hard link set */ struct list_head inode_dentry_list; @@ -292,13 +293,12 @@ struct dentry { }; -extern struct ads_entry *dentry_get_ads_entry(struct dentry *dentry, - const char *stream_name); - -extern struct ads_entry *dentry_add_ads(struct dentry *dentry, - const char *stream_name); +extern struct ads_entry *inode_get_ads_entry(struct inode *inode, + const char *stream_name, + u16 *idx_ret); -extern void dentry_remove_ads(struct dentry *dentry, struct ads_entry *entry); +extern struct ads_entry *inode_add_ads(struct inode *dentry, + const char *stream_name); extern const char *path_stream_name(const char *path); @@ -330,6 +330,7 @@ extern int print_dentry(struct dentry *dentry, void *lookup_table); extern int print_dentry_full_path(struct dentry *entry, void *ignore); extern struct dentry *get_dentry(struct WIMStruct *w, const char *path); +extern struct inode *wim_pathname_to_inode(WIMStruct *w, const char *path); extern struct dentry *get_parent_dentry(struct WIMStruct *w, const char *path); extern struct dentry *get_dentry_child_with_name(const struct dentry *dentry, const char *name); @@ -340,12 +341,13 @@ extern struct inode *new_inode(); extern struct inode *new_timeless_inode(); extern struct dentry *new_dentry_with_inode(const char *name); -extern void dentry_free_ads_entries(struct dentry *dentry); -extern void free_dentry(struct dentry *dentry); +extern void free_ads_entry(struct ads_entry *entry); +extern void inode_free_ads_entries(struct inode *inode); +extern struct inode *free_dentry(struct dentry *dentry); extern void free_inode(struct inode *inode); -extern void put_inode(struct inode *inode); -extern void put_dentry(struct dentry *dentry); +extern struct inode *put_inode(struct inode *inode); extern struct dentry *clone_dentry(struct dentry *old); +extern void put_dentry(struct dentry *dentry); extern void free_dentry_tree(struct dentry *root, struct lookup_table *lookup_table); extern int increment_dentry_refcnt(struct dentry *dentry, void *ignore); @@ -369,16 +371,13 @@ extern int read_dentry_tree(const u8 metadata_resource[], extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p); -/* Return the number of dentries in the hard link group */ -static inline size_t dentry_link_group_size(const struct dentry *dentry) +static inline struct dentry *inode_first_dentry(struct inode *inode) { - const struct list_head *cur; - size_t size = 0; - list_for_each(cur, &dentry->inode_dentry_list) - size++; - return size; + return container_of(inode->dentry_list.next, struct dentry, + inode_dentry_list); } + static inline bool dentry_is_root(const struct dentry *dentry) { return dentry->parent == dentry; @@ -386,7 +385,7 @@ static inline bool dentry_is_root(const struct dentry *dentry) static inline bool dentry_is_first_sibling(const struct dentry *dentry) { - return dentry_is_root(dentry) || dentry->parent->children == dentry; + return dentry_is_root(dentry) || dentry->parent->inode->children == dentry; } static inline bool dentry_is_only_child(const struct dentry *dentry) @@ -419,14 +418,19 @@ static inline bool dentry_is_symlink(const struct dentry *dentry) return inode_is_symlink(dentry->inode); } +static inline bool inode_is_regular_file(const struct inode *inode) +{ + return !inode_is_directory(inode) && !inode_is_symlink(inode); +} + static inline bool dentry_is_regular_file(const struct dentry *dentry) { - return !dentry_is_directory(dentry) && !dentry_is_symlink(dentry); + return inode_is_regular_file(dentry->inode); } static inline bool dentry_is_empty_directory(const struct dentry *dentry) { - return dentry_is_directory(dentry) && dentry->children == NULL; + return dentry_is_directory(dentry) && dentry->inode->children == NULL; } #endif diff --git a/src/hardlink.c b/src/hardlink.c index 26c9e05d..7aedf8e1 100644 --- a/src/hardlink.c +++ b/src/hardlink.c @@ -75,12 +75,6 @@ static size_t inode_link_count(const struct inode *inode) return size; } -static struct dentry *inode_first_dentry(struct inode *inode) -{ - return container_of(inode->dentry_list.next, struct dentry, - inode_dentry_list); -} - /* * Insert a dentry into the inode table based on its inode * ID. diff --git a/src/join.c b/src/join.c index a1639e36..bf84efa5 100644 --- a/src/join.c +++ b/src/join.c @@ -35,7 +35,6 @@ static int copy_lte_to_table(struct lookup_table_entry *lte, void *table) if (!copy) return WIMLIB_ERR_NOMEM; memcpy(copy, lte, sizeof(struct lookup_table_entry)); - INIT_LIST_HEAD(©->lte_group_list); lookup_table_insert(table, copy); return 0; } diff --git a/src/list.h b/src/list.h index b5717c48..7c0a7443 100644 --- a/src/list.h +++ b/src/list.h @@ -39,21 +39,6 @@ struct hlist_node { struct hlist_node *next, **pprev; }; -/* - * Structure used to create a linked list of streams that share the same lookup - * table entry. This structure may be embedded in either a inode (for the - * un-named data stream) or an ads_entry (for an alternate data stream). The - * @type field indicates which of these structures the stream_list_head is - * embedded in. - */ -struct stream_list_head { - struct list_head list; - enum { - STREAM_TYPE_NORMAL = 0, - STREAM_TYPE_ADS, - } type; -}; - /* * Simple doubly linked list implementation. * diff --git a/src/lookup_table.c b/src/lookup_table.c index d4a1ba42..776ad94b 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -29,6 +29,10 @@ #include "io.h" #include +#ifdef WITH_FUSE +#include +#endif + struct lookup_table *new_lookup_table(size_t capacity) { struct lookup_table *table; @@ -66,7 +70,6 @@ struct lookup_table_entry *new_lookup_table_entry() lte->part_number = 1; lte->refcnt = 1; - INIT_LIST_HEAD(<e->lte_group_list); return lte; } @@ -142,7 +145,14 @@ void lookup_table_insert(struct lookup_table *table, table->num_entries++; } - +static void finalize_lte(struct lookup_table_entry *lte) +{ + #ifdef WITH_FUSE + if (lte->resource_location == RESOURCE_IN_STAGING_FILE) + unlink(lte->staging_file_name); + #endif + free_lookup_table_entry(lte); +} /* Decrements the reference count for the lookup table entry @lte. If its * reference count reaches 0, it is unlinked from the lookup table. If, @@ -159,7 +169,7 @@ lte_decrement_refcnt(struct lookup_table_entry *lte, struct lookup_table *table) if (lte->num_opened_fds == 0) #endif { - free_lookup_table_entry(lte); + finalize_lte(lte); lte = NULL; } } @@ -167,6 +177,23 @@ lte_decrement_refcnt(struct lookup_table_entry *lte, struct lookup_table *table) return lte; } +#ifdef WITH_FUSE +struct lookup_table_entry * +lte_decrement_num_opened_fds(struct lookup_table_entry *lte, + struct lookup_table *table) +{ + if (lte) { + wimlib_assert(lte->num_opened_fds); + if (--lte->num_opened_fds == 0 && lte->refcnt == 0) { + lookup_table_unlink(table, lte); + finalize_lte(lte); + lte = NULL; + } + } + return lte; +} +#endif + /* * Calls a function on all the entries in the lookup table. Stop early and * return nonzero if any call to the function returns nonzero. @@ -429,11 +456,11 @@ int lookup_resource(WIMStruct *w, const char *path, int lookup_flags, struct dentry **dentry_ret, struct lookup_table_entry **lte_ret, - unsigned *stream_idx_ret) + u16 *stream_idx_ret) { struct dentry *dentry; struct lookup_table_entry *lte; - unsigned stream_idx; + u16 stream_idx; const char *stream_name = NULL; struct inode *inode; char *p = NULL; @@ -460,21 +487,21 @@ int lookup_resource(WIMStruct *w, const char *path, && inode_is_directory(inode)) return -EISDIR; - lte = inode->lte; - stream_idx = 0; if (stream_name) { - size_t stream_name_len = strlen(stream_name); - for (u16 i = 0; i < inode->num_ads; i++) { - if (ads_entry_has_name(inode->ads_entries[i], - stream_name, - stream_name_len)) - { - stream_idx = i + 1; - lte = inode->ads_entries[i]->lte; - goto out; - } + struct ads_entry *ads_entry; + u16 ads_idx; + ads_entry = inode_get_ads_entry(inode, stream_name, + &ads_idx); + if (ads_entry) { + stream_idx = ads_idx + 1; + lte = ads_entry->lte; + goto out; + } else { + return -ENOENT; } - return -ENOENT; + } else { + lte = inode->lte; + stream_idx = 0; } out: if (dentry_ret) @@ -492,12 +519,7 @@ static int inode_resolve_ltes(struct inode *inode, struct lookup_table *table) /* Resolve the default file stream */ lte = __lookup_resource(table, inode->hash); - if (lte) - list_add(&inode->lte_group_list.list, <e->lte_group_list); - else - INIT_LIST_HEAD(&inode->lte_group_list.list); inode->lte = lte; - inode->lte_group_list.type = STREAM_TYPE_NORMAL; inode->resolved = true; /* Resolve the alternate data streams */ @@ -505,13 +527,7 @@ static int inode_resolve_ltes(struct inode *inode, struct lookup_table *table) struct ads_entry *cur_entry = inode->ads_entries[i]; lte = __lookup_resource(table, cur_entry->hash); - if (lte) - list_add(&cur_entry->lte_group_list.list, - <e->lte_group_list); - else - INIT_LIST_HEAD(&cur_entry->lte_group_list.list); cur_entry->lte = lte; - cur_entry->lte_group_list.type = STREAM_TYPE_ADS; } return 0; } diff --git a/src/lookup_table.h b/src/lookup_table.h index f0d454a1..d99f4b75 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -154,16 +154,7 @@ struct lookup_table_entry { #endif }; #ifdef WITH_FUSE - /* File descriptors table for this data stream. This is used if the WIM - * is mounted. Basically, each time a file is open()ed, a new file - * descriptor is added here, and each time a file is close()ed, the file - * descriptor is gotten rid of. If the stream is opened for writing, it - * will be extracted to the staging directory and there will be an - * actual native file descriptor associated with each "wimlib file - * descriptor". */ u16 num_opened_fds; - u16 num_allocated_fds; - struct wimlib_fd **fds; #endif /* When a WIM file is written, out_refcnt starts at 0 and is incremented @@ -189,13 +180,6 @@ struct lookup_table_entry { * */ char *extracted_file; - /* Circular linked list of streams that share the same lookup table - * entry. - * - * This list of streams may include streams from different hard link - * sets that happen to be the same. */ - struct list_head lte_group_list; - /* List of lookup table entries that correspond to streams that have * been extracted to the staging directory when modifying a read-write * mounted WIM. */ @@ -244,6 +228,12 @@ static inline void lookup_table_unlink(struct lookup_table *table, extern struct lookup_table_entry * lookup_table_decrement_refcnt(struct lookup_table* table, const u8 hash[]); +#ifdef WITH_FUSE +extern struct lookup_table_entry * +lte_decrement_num_opened_fds(struct lookup_table_entry *lte, + struct lookup_table *table); +#endif + extern struct lookup_table_entry * lte_decrement_refcnt(struct lookup_table_entry *lte, struct lookup_table *table); @@ -261,7 +251,7 @@ __lookup_resource(const struct lookup_table *table, const u8 hash[]); extern int lookup_resource(WIMStruct *w, const char *path, int lookup_flags, struct dentry **dentry_ret, struct lookup_table_entry **lte_ret, - unsigned *stream_idx_ret); + u16 *stream_idx_ret); extern int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore); diff --git a/src/mount.c b/src/mount.c index 530fdf68..b67f40c6 100644 --- a/src/mount.c +++ b/src/mount.c @@ -53,43 +53,13 @@ /* File descriptor to a file open on the WIM filesystem. */ struct wimlib_fd { - /* Index of this file descriptor in the lookup table entry's table of - * file descriptors */ - u16 idx; - - /* Index of the stream we've opened from a dentry */ - u16 stream_idx; - - /* Native file descriptor to the staging file */ - int staging_fd; - - /* Pointer to lookup table entry that has the table of file descriptors - * containing this file descriptor */ + struct inode *inode; struct lookup_table_entry *lte; - - union { - struct dentry *dentry; - struct ads_entry *ads_entry; - }; - - /* Hard link group ID of the dentry containing the stream that was - * opened to produce this file descriptor. - * - * This is always valid even if @dentry is NULL. It can be used to - * identify file descriptors corresponding to the same hard-linked - * stream, even if the specific dentry under which the stream was opened - * has been unlinked. */ - u64 link_group_id; + int staging_fd; + u16 idx; + u32 stream_id; }; -static inline struct dentry *fd_dentry(const struct wimlib_fd *fd) -{ - if (fd->stream_idx == 0) - return fd->dentry; - else - return fd->ads_entry ? fd->ads_entry->dentry : NULL; -} - /* The WIMStruct for the mounted WIM. */ static WIMStruct *w; @@ -110,9 +80,8 @@ static int mount_flags; /* Name of the directory on which the WIM file is mounted. */ static const char *mount_dir; -/* Next hard link group ID to be assigned. These are also used as the inode - * numbers. */ -static u64 next_link_group_id; +/* Next inode number to be assigned. */ +static u64 next_ino; /* List of lookup table entries in the staging directory */ static LIST_HEAD(staging_list); @@ -131,53 +100,73 @@ static inline int flags_writable(int open_flags) return open_flags & (O_RDWR | O_WRONLY); } -/* - * Allocate a file descriptor for a lookup table entry - */ -static int alloc_wimlib_fd(struct lookup_table_entry *lte, +static int alloc_wimlib_fd(struct inode *inode, + u32 stream_id, + struct lookup_table_entry *lte, struct wimlib_fd **fd_ret) { static const u16 fds_per_alloc = 8; static const u16 max_fds = 0xffff; - if (lte->num_opened_fds == lte->num_allocated_fds) { + if (inode->num_opened_fds == inode->num_allocated_fds) { struct wimlib_fd **fds; u16 num_new_fds; - if (lte->num_allocated_fds == max_fds) + if (inode->num_allocated_fds == max_fds) return -EMFILE; - num_new_fds = min(fds_per_alloc, max_fds - lte->num_allocated_fds); + num_new_fds = min(fds_per_alloc, max_fds - inode->num_allocated_fds); - fds = REALLOC(lte->fds, (lte->num_allocated_fds + num_new_fds) * - sizeof(lte->fds[0])); + fds = REALLOC(inode->fds, (inode->num_allocated_fds + num_new_fds) * + sizeof(inode->fds[0])); if (!fds) return -ENOMEM; - memset(&fds[lte->num_allocated_fds], 0, + memset(&fds[inode->num_allocated_fds], 0, num_new_fds * sizeof(fds[0])); - lte->fds = fds; - lte->num_allocated_fds += num_new_fds; + inode->fds = fds; + inode->num_allocated_fds += num_new_fds; } for (u16 i = 0; ; i++) { - if (!lte->fds[i]) { + if (!inode->fds[i]) { struct wimlib_fd *fd = CALLOC(1, sizeof(*fd)); if (!fd) return -ENOMEM; + fd->inode = inode; + fd->lte = lte; fd->staging_fd = -1; fd->idx = i; - fd->lte = lte; - lte->fds[i] = fd; - lte->num_opened_fds++; + fd->stream_id = stream_id; *fd_ret = fd; + inode->fds[i] = fd; + inode->num_opened_fds++; + lte->num_opened_fds++; return 0; } } } -static int close_wimlib_fd(struct wimlib_fd *fd) +static void inode_put_fd(struct inode *inode, struct wimlib_fd *fd) { - struct lookup_table_entry *lte = fd->lte; + wimlib_assert(fd); + wimlib_assert(inode); + wimlib_assert(fd->inode == inode); + wimlib_assert(inode->num_opened_fds); + wimlib_assert(fd->idx < inode->num_opened_fds); + wimlib_assert(inode->fds[fd->idx] == fd); + + inode->fds[fd->idx] = NULL; + FREE(fd); + if (--inode->num_opened_fds == 0 && inode->link_count == 0) + free_inode(inode); +} + +static int lte_put_fd(struct lookup_table_entry *lte, struct wimlib_fd *fd) +{ + wimlib_assert(fd); + wimlib_assert(fd->lte == lte); + + if (!lte) /* Empty stream with no lookup table entry */ + return 0; - wimlib_assert(lte); wimlib_assert(lte->num_opened_fds); if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { @@ -186,121 +175,92 @@ static int close_wimlib_fd(struct wimlib_fd *fd) if (close(fd->staging_fd) != 0) return -errno; } - if (--lte->num_opened_fds == 0 && lte->refcnt == 0) { - if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { - wimlib_assert(lte->staging_file_name); - unlink(lte->staging_file_name); - } - free_lookup_table_entry(lte); - } - wimlib_assert(lte->fds[fd->idx] == fd); - lte->fds[fd->idx] = NULL; - FREE(fd); + lte_decrement_num_opened_fds(lte, w->lookup_table); + return 0; +} + +static int close_wimlib_fd(struct wimlib_fd *fd) +{ + int ret; + ret = lte_put_fd(fd->lte, fd); + if (ret != 0) + return ret; + + inode_put_fd(fd->inode, fd); return 0; } -/* Remove a dentry and all its alternate file streams */ +/* Remove a dentry; i.e. remove a reference to the inode. + * + * If there are no remaining references to the inode either through detnries or + * open file descriptors, the inode is freed. + * + * All lookup table entries referenced by the inode have their reference count + * decremented. If a lookup table entry has no open file descriptors and no + * references remaining, it is freed, and the staging file is unlinked. + * + * Otherwise, the inode is not removed, but the dentry is unlinked and freed. */ static void remove_dentry(struct dentry *dentry, struct lookup_table *lookup_table) { - wimlib_assert(dentry); - wimlib_assert(dentry->resolved); + struct inode *inode = dentry->inode; + struct lookup_table_entry *lte; + unsigned i; - if (dentry->ads_entries_status == ADS_ENTRIES_OWNER) { - struct dentry *new_owner; - list_for_each_entry(new_owner, &dentry->link_group_list, - link_group_list) - { - if (new_owner->ads_entries_status == ADS_ENTRIES_USER) { - new_owner->ads_entries_status = ADS_ENTRIES_OWNER; - for (u16 i = 0; i < dentry->num_ads; i++) - dentry->ads_entries[i]->dentry = new_owner; - break; - } - } - dentry->ads_entries_status = ADS_ENTRIES_USER; - } - list_del(&dentry->link_group_list); - list_del(&dentry->lte_group_list); - - u16 i = 0; - struct lookup_table_entry *lte = dentry->lte; - while (1) { - lte = lte_decrement_refcnt(lte, lookup_table); - if (lte && lte->num_opened_fds) - for (u16 j = 0; j < lte->num_allocated_fds; j++) - if (lte->fds[j] && - lte->fds[j]->link_group_id == dentry->link_group_id - && ((lte->fds[j].stream_idx == 0 && - lte->fds[j].dentry == dentry) || - (lte->fds[j].stream_idx && - lte->fds[j].ads_entry && - lte->fds[j].ads_entry->dentry == dentry))) - lte->fds[j].dentry = NULL; - if (i == dentry->num_ads) - break; - lte = dentry->ads_entries[i].lte; - i++; + for (i = 0; <= inode->num_ads; i++) { + lte = inode_stream_lte_resolved(inode, i); + if (lte) + lte_decrement_refcnt(lte, lookup_table); } - unlink_dentry(dentry); - free_dentry(dentry); + put_dentry(dentry); } -static void remove_ads(struct dentry *dentry, - struct ads_entry *ads_entry, - struct lookup_table *lookup_table) +/* Remove an alternate data stream from the inode */ +static void inode_remove_ads(struct inode *inode, u16 idx, + struct lookup_table *lookup_table) { + struct ads_entry *ads_entry; struct lookup_table_entry *lte; - wimlib_assert(dentry->resolved); - wimlib_assert(ads_entry - dentry->ads_entries < dentry->num_ads); + ads_entry = inode->ads_entries[idx]; + + wimlib_assert(ads_entry); + + lte = ads_entry->lte; - lte = lte_decrement_refcnt(ads_entry->lte, lookup_table); if (lte) - for (u16 i = 0; i < lte->num_allocated_fds; i++) - if (lte->fds[i] && lte->fds[i]->dentry == dentry) - lte->fds[i]->dentry = NULL; - - /* Fix up file descriptor stream indexes */ - for (u16 i = ads_entry - dentry->ads_entries + 1; i < dentry->num_ads; i++) { - struct lookup_table_entry *lte = ads_entry[i].lte; - if (lte) { - for (u16 open_fd_idx = 0, fd_idx = 0; - open_fd_idx < lte->num_opened_fds; fd_idx++) - { - if (lte->fds[fd_idx]) { - open_fd_idx++; - if (lte->fds[fd_idx]->dentry == dentry - && lte->fds[fd_idx]->stream_idx > idx) - lte->fds[fd_idx]->stream_id - } - } - } - } - dentry_remove_ads(dentry, ads_entry); + lte_decrement_refcnt(lte, lookup_table); + + free_ads_entry(ads_entry); + + wimlib_assert(inode->num_ads); + memcpy(&inode->ads_entries[idx], + &inode->ads_entries[idx + 1], + (inode->num_ads - idx - 1) * sizeof(inode->ads_entries[0])); + inode->num_ads--; } -/* Transfers file attributes from a struct dentry to a `stat' buffer. */ -int dentry_to_stbuf(const struct dentry *dentry, struct stat *stbuf) +/* Transfers file attributes from a struct inode to a `stat' buffer. + * + * The lookup table entry tells us which stream in the inode we are statting. + * For a named data stream, everything returned is the same as the unnamed data + * stream except possibly the size and block count. */ +int inode_to_stbuf(const struct inode *inode, struct lookup_table_entry *lte, + struct stat *stbuf) { - struct lookup_table_entry *lte; - - if (dentry_is_symlink(dentry)) + if (inode_is_symlink(inode)) stbuf->st_mode = S_IFLNK | 0777; - else if (dentry_is_directory(dentry)) + else if (inode_is_directory(inode)) stbuf->st_mode = S_IFDIR | 0755; else stbuf->st_mode = S_IFREG | 0644; - stbuf->st_ino = (ino_t)dentry->link_group_id; - - stbuf->st_nlink = dentry_link_group_size(dentry); + stbuf->st_ino = (ino_t)inode->ino; + stbuf->st_nlink = inode->link_count; stbuf->st_uid = getuid(); stbuf->st_gid = getgid(); - /* Use the size of the unnamed (default) file stream. */ - lte = dentry_unnamed_lte_resolved(dentry); if (lte) { if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { wimlib_assert(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE); @@ -319,9 +279,9 @@ int dentry_to_stbuf(const struct dentry *dentry, struct stat *stbuf) stbuf->st_size = 0; } - stbuf->st_atime = wim_timestamp_to_unix(dentry->last_access_time); - stbuf->st_mtime = wim_timestamp_to_unix(dentry->last_write_time); - stbuf->st_ctime = wim_timestamp_to_unix(dentry->creation_time); + stbuf->st_atime = wim_timestamp_to_unix(inode->last_access_time); + stbuf->st_mtime = wim_timestamp_to_unix(inode->last_write_time); + stbuf->st_ctime = wim_timestamp_to_unix(inode->creation_time); stbuf->st_blocks = (stbuf->st_size + 511) / 512; return 0; } @@ -381,114 +341,10 @@ static int create_staging_file(char **name_ret, int open_flags) return fd; } -/* - * Removes open file descriptors from a lookup table entry @old_lte where the - * removed file descriptors have opened the corresponding file resource in the - * context of a dentry in the hard link group @link_group and a stream at index - * @stream_idx. These file descriptors are extracted and placed in a new lookup - * table entry, which is returned. - * - * Note we need to examine the link_group_id of each file descriptor and not - * dentry->link_group_id of each file descriptor, since dentry may be NULL in - * the case of an un-linked dentry. - */ -static struct lookup_table_entry * -lte_extract_fds(struct lookup_table_entry *old_lte, u64 link_group_id, - u16 stream_idx) -{ - u16 num_transferred_fds; - struct lookup_table_entry *new_lte; - - new_lte = new_lookup_table_entry(); - if (!new_lte) - return NULL; - - num_transferred_fds = 0; - for (u16 i = 0; i < old_lte->num_allocated_fds; i++) - if (old_lte->fds[i] - && old_lte->fds[i]->link_group_id == link_group_id - && old_lte->fds[i]->stream_idx == stream_idx) - num_transferred_fds++; - DEBUG("Transferring %u file descriptors", - num_transferred_fds); - new_lte->fds = MALLOC(num_transferred_fds * sizeof(new_lte->fds[0])); - if (!new_lte->fds) { - FREE(new_lte); - return NULL; - } - for (u16 i = 0, j = 0; ; i++) { - if (old_lte->fds[i] - && old_lte->fds[i]->link_group_id == link_group_id - && old_lte->fds[i]->stream_idx == stream_idx) { - struct wimlib_fd *fd = old_lte->fds[i]; - old_lte->fds[i] = NULL; - fd->lte = new_lte; - fd->idx = j; - new_lte->fds[j] = fd; - if (++j == num_transferred_fds) - break; - } - } - DEBUG("old_lte: %u fds open; new_lte: %u fds open", - old_lte->num_opened_fds, new_lte->num_opened_fds); - old_lte->num_opened_fds -= num_transferred_fds; - new_lte->num_opened_fds = num_transferred_fds; - new_lte->num_allocated_fds = num_transferred_fds; - return new_lte; -} - -/* - * Transfers an alternate data stream entry to a new lookup table entry - */ -static void lte_transfer_ads_entry(struct lookup_table_entry *new_lte, - struct ads_entry *ads_entry) -{ - list_del(&ads_entry->lte_group_list.list); - list_add(&ads_entry->lte_group_list.list, &new_lte->lte_group_list); - ads_entry->lte = new_lte; -} - -/* - * Transfers a dentry to a new lookup table entry - */ -static void lte_transfer_dentry(struct lookup_table_entry *new_lte, - struct dentry *dentry) -{ - wimlib_assert(dentry->lte_group_list.list.next); - wimlib_assert(new_lte->lte_group_list.next); - list_del(&dentry->lte_group_list.list); - list_add(&dentry->lte_group_list.list, &new_lte->lte_group_list); - dentry->lte = new_lte; -} - -static void lte_transfer_stream_entries(struct lookup_table_entry *new_lte, - struct dentry *dentry, - unsigned stream_idx) -{ - INIT_LIST_HEAD(&new_lte->lte_group_list); - if (stream_idx == 0) { - struct list_head *pos = &dentry->link_group_list; - do { - struct dentry *d; - d = container_of(pos, struct dentry, link_group_list); - wimlib_assert(d->link_group_id == dentry->link_group_id); - lte_transfer_dentry(new_lte, d); - pos = pos->next; - } while (pos != &dentry->link_group_list); - } else { - struct ads_entry *ads_entry; - wimlib_assert(stream_idx <= dentry->num_ads); - ads_entry = &dentry->ads_entries[stream_idx - 1]; - lte_transfer_ads_entry(new_lte, ads_entry); - } -} - /* * Extract a WIM resource to the staging directory. * - * @dentry, @stream_idx: The stream on whose behalf we are modifying the lookup - * table entry (these may be more streams than this that reference the lookup - * table entry) + * @inode: Inode that contains the stream we are extracting * * @lte: Pointer to pointer to the lookup table entry for the stream we need to * extract, or NULL if there was no lookup table entry present for the stream @@ -496,8 +352,8 @@ static void lte_transfer_stream_entries(struct lookup_table_entry *new_lte, * @size: Number of bytes of the stream we want to extract (this supports the * wimfs_truncate() function). */ -static int extract_resource_to_staging_dir(struct dentry *dentry, - unsigned stream_idx, +static int extract_resource_to_staging_dir(struct inode *inode, + u32 stream_id, struct lookup_table_entry **lte, off_t size) { @@ -505,25 +361,6 @@ static int extract_resource_to_staging_dir(struct dentry *dentry, int ret; int fd; struct lookup_table_entry *old_lte, *new_lte; - size_t link_group_size; - - /* - * We need to: - * - Create a staging file for the WIM resource - * - Extract the resource to it - * - Create a new lte for the file resource - * - Transfer fds from the old lte to the new lte, but only if they share the - * same hard link group as this dentry. If there is no old lte, then this - * step does not need to be done - * - Transfer stream entries from the old lte's list to the new lte's list. If - * there is no old lte, we instead transfer entries for the hard link group. - * - * Note: *lte is permitted to be NULL, in which case there is no old - * lookup table entry. - */ - - DEBUG("Extracting dentry `%s' stream %u to staging directory", - dentry->full_path_utf8, stream_idx); old_lte = *lte; fd = create_staging_file(&staging_file_name, O_WRONLY); @@ -543,11 +380,9 @@ static int extract_resource_to_staging_dir(struct dentry *dentry, goto out_delete_staging_file; } - link_group_size = dentry_link_group_size(dentry); - if (old_lte) { wimlib_assert(old_lte->resource_location == RESOURCE_IN_WIM); - if (link_group_size == old_lte->refcnt) { + if (inode->link_count == old_lte->refcnt) { /* The reference count of the existing lookup table * entry is the same as the size of the hard link group * associated with the dentry; therefore, ALL the @@ -560,44 +395,40 @@ static int extract_resource_to_staging_dir(struct dentry *dentry, new_lte = old_lte; } else { DEBUG("Splitting lookup table entry " - "(link_group_size = %zu, lte refcnt = %u)", - link_group_size, old_lte->refcnt); - /* The stream we are going to change does not account - * for all the references to the lookup table entry. - * The other references to the lookup table entry may be - * from different hard link groups or from other streams - * in the same hard link group. - */ + "(inode->link_count = %zu, lte refcnt = %u)", + inode->link_count, old_lte->refcnt); - wimlib_assert(old_lte->refcnt > link_group_size); + wimlib_assert(old_lte->refcnt > inode->link_count); - /* First, find the old lookup table entry's file - * descriptors that were opened in the context of the - * stream we're going to change, and allocate a new - * lookup table entry containing those file descriptors. - * */ - new_lte = lte_extract_fds(old_lte, link_group_id, - stream_idx); + new_lte = new_lookup_table_entry(); if (!new_lte) { ret = -ENOMEM; goto out_delete_staging_file; } - - lte_transfer_stream_entries(new_lte, dentry, stream_idx); - old_lte->refcnt -= link_group_size; - } + old_lte->refcnt -= inode->link_count; + + for (u16 i = 0, j = 0; j < inode->num_opened_fds; i++) { + if (inode->fds[i]) { + if (inode->fds[i]->stream_id == stream_id) { + wimlib_assert(inode->fds[i]->lte == old_lte); + inode->fds[i]->lte = new_lte; + } + j++; + } + } + } } else { /* No old_lte was supplied, so the resource had no lookup table * entry before (it must be an empty resource) */ + wimlib_assert(inode->num_opened_fds == 0); new_lte = new_lookup_table_entry(); if (!new_lte) { ret = -ENOMEM; goto out_delete_staging_file; } - lte_transfer_stream_entries(new_lte, dentry, stream_idx); } new_lte->resource_entry.original_size = size; - new_lte->refcnt = link_group_size; + new_lte->refcnt = inode->link_count; random_hash(new_lte->hash); new_lte->staging_file_name = staging_file_name; new_lte->resource_location = RESOURCE_IN_STAGING_FILE; @@ -842,6 +673,7 @@ static int wimfs_access(const char *path, int mask) return 0; } +#if 0 /* Closes the staging file descriptor associated with the lookup table entry, if * it is opened. */ static int close_lte_fds(struct lookup_table_entry *lte) @@ -883,6 +715,7 @@ static void lte_list_change_lte_ptr(struct lookup_table_entry *lte, } } } +#endif static int update_lte_of_staging_file(struct lookup_table_entry *lte, @@ -907,10 +740,14 @@ static int update_lte_of_staging_file(struct lookup_table_entry *lte, if (duplicate_lte) { /* Merge duplicate lookup table entries */ +#if 0 lte_list_change_lte_ptr(lte, duplicate_lte); +#endif duplicate_lte->refcnt += lte->refcnt; +#if 0 list_splice(<e->lte_group_list, &duplicate_lte->lte_group_list); +#endif free_lookup_table_entry(lte); } else { @@ -936,12 +773,14 @@ static int rebuild_wim(WIMStruct *w, bool check_integrity) struct lookup_table_entry *lte, *tmp; /* Close all the staging file descriptors. */ +#if 0 DEBUG("Closing all staging file descriptors."); list_for_each_entry(lte, &staging_list, staging_list) { ret = close_lte_fds(lte); if (ret != 0) return ret; } +#endif /* Calculate SHA1 checksums for all staging files, and merge unnecessary * lookup table entries. */ @@ -1074,14 +913,15 @@ static int wimfs_ftruncate(const char *path, off_t size, static int wimfs_getattr(const char *path, struct stat *stbuf) { struct dentry *dentry; + struct lookup_table_entry *lte; int ret; ret = lookup_resource(w, path, get_lookup_flags() | LOOKUP_FLAG_DIRECTORY_OK, - &dentry, NULL, NULL); + &dentry, <e, NULL); if (ret != 0) return ret; - return dentry_to_stbuf(dentry, stbuf); + return inode_to_stbuf(dentry->inode, lte, stbuf); } #ifdef ENABLE_XATTR @@ -1105,7 +945,7 @@ static int wimfs_getxattr(const char *path, const char *name, char *value, dentry = get_dentry(w, path); if (!dentry) return -ENOENT; - ads_entry = dentry_get_ads_entry(dentry, name); + ads_entry = inode_get_ads_entry(dentry->inode, name, NULL); if (!ads_entry) return -ENOATTR; @@ -1128,11 +968,17 @@ static int wimfs_link(const char *to, const char *from) { struct dentry *to_dentry, *from_dentry, *from_dentry_parent; const char *link_name; + struct inode *inode; + unsigned i; + struct lookup_table_entry *lte; to_dentry = get_dentry(w, to); if (!to_dentry) return -ENOENT; - if (!dentry_is_regular_file(to_dentry)) + + inode = to_dentry->inode; + + if (!inode_is_regular_file(inode)) return -EPERM; from_dentry_parent = get_parent_dentry(w, from); @@ -1144,32 +990,20 @@ static int wimfs_link(const char *to, const char *from) link_name = path_basename(from); if (get_dentry_child_with_name(from_dentry_parent, link_name)) return -EEXIST; - from_dentry = clone_dentry(to_dentry); + from_dentry = new_dentry(link_name); if (!from_dentry) return -ENOMEM; - if (change_dentry_name(from_dentry, link_name) != 0) { - FREE(from_dentry); - return -ENOMEM; - } - /* Add the new dentry to the dentry list for the link group */ - list_add(&from_dentry->link_group_list, &to_dentry->link_group_list); - /* Increment reference counts for the unnamed file stream and all - * alternate data streams. */ - if (from_dentry->lte) { - list_add(&from_dentry->lte_group_list.list, - &to_dentry->lte_group_list.list); - from_dentry->lte->refcnt++; - } - for (u16 i = 0; i < from_dentry->num_ads; i++) { - struct ads_entry *ads_entry = &from_dentry->ads_entries[i]; - if (ads_entry->lte) - ads_entry->lte->refcnt++; - } + inode_add_dentry(from_dentry, inode); + from_dentry->inode = inode; + inode->link_count++; - /* The ADS entries are owned by another dentry. */ - from_dentry->ads_entries_status = ADS_ENTRIES_USER; + for (i = 0; i <= inode->num_ads; i++) { + lte = inode_stream_lte_resolved(inode, i); + if (lte) + lte->refcnt++; + } link_dentry(from_dentry, from_dentry_parent); return 0; @@ -1183,6 +1017,7 @@ static int wimfs_listxattr(const char *path, char *list, size_t size) char *p = list; size_t needed_size; unsigned i; + struct inode *inode; if (!(mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; @@ -1191,18 +1026,20 @@ static int wimfs_listxattr(const char *path, char *list, size_t size) ret = lookup_resource(w, path, get_lookup_flags(), &dentry, NULL, NULL); if (ret != 0) return ret; + inode = dentry->inode; + if (size == 0) { needed_size = 0; - for (i = 0; i < dentry->num_ads; i++) - needed_size += dentry->ads_entries[i].stream_name_utf8_len + 6; + for (i = 0; i < inode->num_ads; i++) + needed_size += inode->ads_entries[i]->stream_name_utf8_len + 6; return needed_size; } else { - for (i = 0; i < dentry->num_ads; i++) { - needed_size = dentry->ads_entries[i].stream_name_utf8_len + 6; + for (i = 0; i < inode->num_ads; i++) { + needed_size = inode->ads_entries[i]->stream_name_utf8_len + 6; if (needed_size > size) return -ERANGE; p += sprintf(p, "user.%s", - dentry->ads_entries[i].stream_name_utf8) + 1; + inode->ads_entries[i]->stream_name_utf8) + 1; size -= needed_size; } return p - list; @@ -1231,10 +1068,10 @@ static int wimfs_mkdir(const char *path, mode_t mode) if (get_dentry_child_with_name(parent, basename)) return -EEXIST; - newdir = new_dentry(basename); - newdir->attributes |= FILE_ATTRIBUTE_DIRECTORY; - newdir->resolved = true; - newdir->link_group_id = next_link_group_id++; + newdir = new_dentry_with_inode(basename); + newdir->inode->attributes |= FILE_ATTRIBUTE_DIRECTORY; + newdir->inode->resolved = true; + newdir->inode->ino = next_ino++; link_dentry(newdir, parent); return 0; } @@ -1257,9 +1094,9 @@ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev) dentry = get_dentry(w, path); if (!dentry || !dentry_is_regular_file(dentry)) return -ENOENT; - if (dentry_get_ads_entry(dentry, stream_name)) + if (inode_get_ads_entry(dentry->inode, stream_name, NULL)) return -EEXIST; - new_entry = dentry_add_ads(dentry, stream_name); + new_entry = inode_add_ads(dentry->inode, stream_name); if (!new_entry) return -ENOENT; } else { @@ -1280,13 +1117,11 @@ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev) if (get_dentry_child_with_name(parent, path)) return -EEXIST; - dentry = new_dentry(basename); + dentry = new_dentry_with_inode(basename); if (!dentry) return -ENOMEM; - dentry->resolved = true; - dentry->link_group_id = next_link_group_id++; - dentry->lte_group_list.type = STREAM_TYPE_NORMAL; - INIT_LIST_HEAD(&dentry->lte_group_list.list); + dentry->inode->resolved = true; + dentry->inode->ino = next_ino++; link_dentry(dentry, parent); } return 0; @@ -1300,52 +1135,41 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi) struct lookup_table_entry *lte; int ret; struct wimlib_fd *fd; - unsigned stream_idx; + struct inode *inode; + u16 stream_idx; + u32 stream_id; ret = lookup_resource(w, path, get_lookup_flags(), &dentry, <e, &stream_idx); if (ret != 0) return ret; - if (!lte) { - /* Empty file with no lookup-table entry. This is fine if it's - * a read-only filesystem. Otherwise we need to create a lookup - * table entry so that we can keep track of the file descriptors - * (this is important in case someone opens the file for - * writing) */ - if (!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) { - fi->fh = 0; - return 0; - } + inode = dentry->inode; - ret = extract_resource_to_staging_dir(dentry, stream_idx, - <e, 0); + if (stream_idx == 0) + stream_id = 0; + else + stream_id = inode->ads_entries[stream_idx - 1]->stream_id; + + /* The file resource may be in the staging directory (read-write mounts + * only) or in the WIM. If it's in the staging directory, we need to + * open a native file descriptor for the corresponding file. Otherwise, + * we can read the file resource directly from the WIM file if we are + * opening it read-only, but we need to extract the resource to the + * staging directory if we are opening it writable. */ + if (flags_writable(fi->flags) && + lte->resource_location != RESOURCE_IN_STAGING_FILE) { + ret = extract_resource_to_staging_dir(inode, stream_id, + <e, + wim_resource_size(lte)); if (ret != 0) return ret; } - ret = alloc_wimlib_fd(lte, &fd); + ret = alloc_wimlib_fd(inode, stream_id, lte, &fd); if (ret != 0) return ret; - fd->dentry = dentry; - fd->stream_idx = stream_idx; - fd->link_group_id = dentry->link_group_id; - - /* The file resource may be in the staging directory (read-write - * mounts only) or in the WIM. If it's in the staging - * directory, we need to open a native file descriptor for the - * corresponding file. Otherwise, we can read the file resource - * directly from the WIM file if we are opening it read-only, - * but we need to extract the resource to the staging directory - * if we are opening it writable. */ - if (flags_writable(fi->flags) && - lte->resource_location != RESOURCE_IN_STAGING_FILE) { - ret = extract_resource_to_staging_dir(dentry, stream_idx, <e, - lte->resource_entry.original_size); - if (ret != 0) - return ret; - } if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { fd->staging_fd = open(lte->staging_file_name, fi->flags); if (fd->staging_fd == -1) { @@ -1361,15 +1185,21 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi) static int wimfs_opendir(const char *path, struct fuse_file_info *fi) { struct dentry *dentry; + struct inode *inode; + int ret; + struct wimlib_fd *fd = NULL; dentry = get_dentry(w, path); if (!dentry) return -ENOENT; - if (!dentry_is_directory(dentry)) + + inode = dentry->inode; + + if (!inode_is_directory(inode)) return -ENOTDIR; - dentry->num_times_opened++; - fi->fh = (uintptr_t)dentry; - return 0; + ret = alloc_wimlib_fd(inode, 0, NULL, &fd); + fi->fh = (uintptr_t)fd; + return ret; } @@ -1381,14 +1211,11 @@ static int wimfs_read(const char *path, char *buf, size_t size, { struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh; - if (!fd) { - /* Empty file with no lookup table entry on read-only mounted - * WIM */ - wimlib_assert(!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)); - return 0; - } + if (!fd) + return -EBADF; - wimlib_assert(fd->lte); + if (!fd->lte) /* Empty stream with no lookup table entry */ + return 0; if (fd->lte->resource_location == RESOURCE_IN_STAGING_FILE) { /* Read from staging file */ @@ -1408,14 +1235,12 @@ static int wimfs_read(const char *path, char *buf, size_t size, } else { /* Read from WIM */ - const struct resource_entry *res_entry; + u64 res_size = wim_resource_size(fd->lte); - res_entry = &fd->lte->resource_entry; - - if (offset > res_entry->original_size) + if (offset > res_size) return -EOVERFLOW; - size = min(size, res_entry->original_size - offset); + size = min(size, res_size - offset); if (read_wim_resource(fd->lte, (u8*)buf, size, offset, false) != 0) @@ -1429,15 +1254,20 @@ static int wimfs_read(const char *path, char *buf, size_t size, static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { - struct dentry *parent, *child; - - parent = (struct dentry*)(uintptr_t)fi->fh; - wimlib_assert(parent); - child = parent->children; + struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh; + struct inode *inode; + struct dentry *child; + + if (!fd) + return -EBADF; + + inode = fd->inode; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); + child = inode->children; + if (!child) return 0; @@ -1445,7 +1275,7 @@ static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, if (filler(buf, child->file_name_utf8, NULL, 0)) return 0; child = child->next; - } while (child != parent->children); + } while (child != inode->children); return 0; } @@ -1459,61 +1289,25 @@ static int wimfs_readlink(const char *path, char *buf, size_t buf_len) if (!dentry_is_symlink(dentry)) return -EINVAL; - ret = dentry_readlink(dentry, buf, buf_len, w); + ret = inode_readlink(dentry->inode, buf, buf_len, w); if (ret > 0) ret = 0; return ret; } -/* Creation time, write time, access time */ -static void -dentry_link_group_set_times(struct dentry *dentry, u64 times[3]) -{ - struct dentry *cur = dentry; - do { - if (times[0]) - cur->creation_time = times[0]; - if (times[1]) - cur->last_write_time = times[1]; - if (times[2]) - cur->last_access_time = times[2]; - } while ((cur = container_of(dentry->link_group_list.next, - struct dentry, - link_group_list)) != dentry); -} - -static void -dentry_link_group_update_times(struct dentry *dentry) -{ - u64 now = get_wim_timestamp(); - u64 times[3] = {now, now, now}; - dentry_link_group_set_times(dentry, times); -} - /* Close a file. */ static int wimfs_release(const char *path, struct fuse_file_info *fi) { struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh; - - if (!fd) { - /* Empty file with no lookup table entry on read-only mounted - * WIM */ - wimlib_assert(!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)); - return 0; - } - + wimlib_assert(fd); return close_wimlib_fd(fd); } static int wimfs_releasedir(const char *path, struct fuse_file_info *fi) { - struct dentry *dentry = (struct dentry *)(uintptr_t)fi->fh; - - wimlib_assert(dentry); - wimlib_assert(dentry->num_times_opened); - if (--dentry->num_times_opened == 0) - free_dentry(dentry); - return 0; + struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh; + wimlib_assert(fd); + return close_wimlib_fd(fd); } #ifdef ENABLE_XATTR @@ -1522,6 +1316,7 @@ static int wimfs_removexattr(const char *path, const char *name) { struct dentry *dentry; struct ads_entry *ads_entry; + u16 ads_idx; if (!(mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; @@ -1533,10 +1328,10 @@ static int wimfs_removexattr(const char *path, const char *name) if (!dentry) return -ENOENT; - ads_entry = dentry_get_ads_entry(dentry, name); + ads_entry = inode_get_ads_entry(dentry->inode, name, &ads_idx); if (!ads_entry) return -ENOATTR; - remove_ads(dentry, ads_entry, w->lookup_table); + inode_remove_ads(dentry->inode, ads_idx, w->lookup_table); return 0; } #endif @@ -1582,7 +1377,7 @@ static int wimfs_rename(const char *from, const char *to) * directory */ if (!dentry_is_directory(dst)) return -ENOTDIR; - if (dst->children != NULL) + if (dst->inode->children != NULL) return -ENOTEMPTY; } parent_of_dst = dst->parent; @@ -1621,9 +1416,7 @@ static int wimfs_rmdir(const char *path) if (!dentry_is_empty_directory(dentry)) return -ENOTEMPTY; - unlink_dentry(dentry); - if (dentry->num_times_opened == 0) - free_dentry(dentry); + remove_dentry(dentry, w->lookup_table); return 0; } @@ -1637,7 +1430,9 @@ static int wimfs_setxattr(const char *path, const char *name, struct ads_entry *new_ads_entry; struct lookup_table_entry *existing_lte; struct lookup_table_entry *lte; + struct inode *inode; u8 value_hash[SHA1_HASH_SIZE]; + u16 ads_idx; if (!(mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; @@ -1649,16 +1444,18 @@ static int wimfs_setxattr(const char *path, const char *name, dentry = get_dentry(w, path); if (!dentry) return -ENOENT; - existing_ads_entry = dentry_get_ads_entry(dentry, name); + inode = dentry->inode; + + existing_ads_entry = inode_get_ads_entry(inode, name, &ads_idx); if (existing_ads_entry) { if (flags & XATTR_CREATE) return -EEXIST; - remove_ads(dentry, existing_ads_entry, w->lookup_table); + inode_remove_ads(dentry->inode, ads_idx, w->lookup_table); } else { if (flags & XATTR_REPLACE) return -ENOATTR; } - new_ads_entry = dentry_add_ads(dentry, name); + new_ads_entry = inode_add_ads(dentry, name); if (!new_ads_entry) return -ENOMEM; @@ -1765,7 +1562,6 @@ static int wimfs_truncate(const char *path, off_t size) ret = extract_resource_to_staging_dir(dentry, stream_idx, <e, size); } - dentry_link_group_update_times(dentry); return ret; } @@ -1774,8 +1570,10 @@ static int wimfs_unlink(const char *path) { struct dentry *dentry; struct lookup_table_entry *lte; + struct inode *inode; int ret; unsigned stream_idx; + unsigned i; ret = lookup_resource(w, path, get_lookup_flags(), &dentry, <e, &stream_idx); @@ -1783,18 +1581,10 @@ static int wimfs_unlink(const char *path) if (ret != 0) return ret; - if (stream_idx == 0) { - /* We are removing the full dentry including all alternate data - * streams. */ + if (stream_idx == 0) remove_dentry(dentry, w->lookup_table); - } else { - /* We are removing an alternate data stream. */ - remove_ads(dentry, &dentry->ads_entries[stream_idx - 1], - w->lookup_table); - } - /* Beware: The lookup table entry(s) may still be referenced by users - * that have opened the corresponding streams. They are freed later in - * wimfs_release() when the last file user has closed the stream. */ + else + inode_remove_ads(dentry->inode, stream_idx - 1, w->lookup_table); return 0; } @@ -1806,36 +1596,39 @@ static int wimfs_unlink(const char *path) */ static int wimfs_utimens(const char *path, const struct timespec tv[2]) { - struct dentry *dentry = get_dentry(w, path); + struct dentry *dentry; + struct inode *inode; + dentry = get_dentry(w, path); if (!dentry) return -ENOENT; - u64 times[3] = {0, 0, 0}; + inode = dentry->inode; + if (tv[0].tv_nsec != UTIME_OMIT) { if (tv[0].tv_nsec == UTIME_NOW) - times[2] = get_wim_timestamp(); + inode->last_access_time = get_wim_timestamp(); else - times[2] = timespec_to_wim_timestamp(&tv[0]); + inode->last_access_time = timespec_to_wim_timestamp(&tv[0]); } if (tv[1].tv_nsec != UTIME_OMIT) { if (tv[1].tv_nsec == UTIME_NOW) - times[1] = get_wim_timestamp(); + inode->last_write_time = get_wim_timestamp(); else - times[1] = timespec_to_wim_timestamp(&tv[1]); + inode->last_write_time = timespec_to_wim_timestamp(&tv[1]); } - dentry_link_group_set_times(dentry, times); return 0; } #else static int wimfs_utime(const char *path, struct utimbuf *times) { - struct dentry *dentry = get_dentry(w, path); + struct dentry *dentry; + struct inode *inode; + dentry = get_dentry(w, path); if (!dentry) return -ENOENT; - u64 wim_times[3]; - wim_times[0] = 0; - wim_times[1] = unix_timestamp_to_wim(times->modtime); - wim_times[2] = unix_timestamp_to_wim(times->actime); - dentry_link_group_set_times(dentry, wim_times); + inode = dentry->inode; + + inode->last_write_time = unix_timestamp_to_wim(times->modtime); + inode->last_access_time = unix_timestamp_to_wim(times->actime); return 0; } #endif @@ -1848,11 +1641,15 @@ static int wimfs_write(const char *path, const char *buf, size_t size, { struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh; int ret; + u64 now; + + if (!fd) + return -EBADF; - wimlib_assert(fd); wimlib_assert(fd->lte); wimlib_assert(fd->lte->staging_file_name); wimlib_assert(fd->staging_fd != -1); + wimlib_assert(fd->inode); /* Seek to the requested position */ if (lseek(fd->staging_fd, offset, SEEK_SET) == -1) @@ -1863,12 +1660,9 @@ static int wimfs_write(const char *path, const char *buf, size_t size, if (ret == -1) return -errno; - if (fd->dentry) { - u64 now = get_wim_timestamp(); - u64 times[3] = {0, now, now}; - dentry_link_group_set_times(fd->dentry, times); - } - + now = get_wim_timestamp(); + fd->inode->last_write_time = now; + fd->inode->last_access_time = now; return ret; } -- 2.43.0