From 038eaccece575d04de8d647bb93773b7f3e38ce4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 Aug 2012 12:21:38 -0500 Subject: [PATCH] Various cleanups --- Makefile.am | 5 +- src/dentry.c | 121 ++++++++++------------------- src/dentry.h | 20 ++--- src/hardlink.c | 173 ++++++++++++++++++++++++++++++++---------- src/list.h | 7 ++ src/lookup_table.c | 33 +++++--- src/lookup_table.h | 27 ++++++- src/mount.c | 119 ++++++++++++++++++++--------- src/resource.c | 1 + src/security.c | 2 +- src/util.c | 4 + src/util.h | 2 +- src/wimlib_internal.h | 2 +- 13 files changed, 327 insertions(+), 189 deletions(-) diff --git a/Makefile.am b/Makefile.am index 69cb81ec..727d8b6c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,7 @@ libwim_la_SOURCES = \ src/integrity.c \ src/io.h \ src/join.c \ + src/list.h \ src/lookup_table.c \ src/lookup_table.h \ src/lz.c \ @@ -88,9 +89,7 @@ EXTRA_DIST = \ COPYING.LIB \ build-aux/strip_fPIC.sh \ debian \ - programs/install.cmd \ - tests/dir \ - tests/dir2 + programs/install.cmd pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = wimlib.pc diff --git a/src/dentry.c b/src/dentry.c index 85a4e64b..50351fb0 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -114,35 +114,44 @@ static void ads_entry_init(struct ads_entry *ads_entry) * on failure. */ struct ads_entry *dentry_add_ads(struct dentry *dentry, const char *stream_name) { - u16 num_ads = dentry->num_ads + 1; + u16 num_ads; struct ads_entry *ads_entries; struct ads_entry *new_entry; - if (num_ads == 0xffff) + + if (dentry->num_ads == 0xffff) return NULL; - ads_entries = MALLOC(num_ads * sizeof(struct ads_entry)); + num_ads = dentry->num_ads + 1; + ads_entries = REALLOC(dentry->ads_entries, + num_ads * sizeof(struct ads_entry)); if (!ads_entries) return NULL; - - memcpy(ads_entries, dentry->ads_entries, - (num_ads - 1) * sizeof(struct ads_entry)); + if (ads_entries != dentry->ads_entries) { + /* We moved the ADS entries. Adjust the stream lists. */ + for (u16 i = 0; i < dentry->num_ads; i++) { + struct list_head *cur = &ads_entries[i].lte_group_list.list; + cur->prev->next = cur; + cur->next->prev = cur; + } + } + dentry->ads_entries = ads_entries; new_entry = &ads_entries[num_ads - 1]; - if (change_ads_name(new_entry, stream_name) != 0) { - FREE(ads_entries); + if (change_ads_name(new_entry, stream_name) != 0) return NULL; - } - ads_entry_init(new_entry); - - FREE(dentry->ads_entries); - dentry->ads_entries = ads_entries; dentry->num_ads = num_ads; + ads_entry_init(new_entry); return new_entry; } void dentry_remove_ads(struct dentry *dentry, struct ads_entry *ads_entry) { - u16 idx = ads_entry - dentry->ads_entries; - u16 following = dentry->num_ads - idx - 1; + 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)); @@ -465,7 +474,7 @@ static inline void dentry_common_init(struct dentry *dentry) memset(dentry, 0, sizeof(struct dentry)); dentry->refcnt = 1; dentry->security_id = -1; - dentry->link_group_master_status = GROUP_INDEPENDENT; + dentry->ads_entries_status = ADS_ENTRIES_DEFAULT; dentry->lte_group_list.type = STREAM_TYPE_NORMAL; } @@ -519,88 +528,34 @@ static void __destroy_dentry(struct dentry *dentry) void free_dentry(struct dentry *dentry) { + wimlib_assert(dentry); __destroy_dentry(dentry); - if (dentry->link_group_master_status != GROUP_SLAVE) + if (dentry->ads_entries_status != ADS_ENTRIES_USER) dentry_free_ads_entries(dentry); FREE(dentry); } +/* Like free_dentry(), but assigns a new ADS entries owner if this dentry was + * the previous owner, and also deletes the dentry from its link_group_list */ void put_dentry(struct dentry *dentry) { - if (dentry->link_group_master_status == GROUP_MASTER) { - struct dentry *new_master; - list_for_each_entry(new_master, &dentry->link_group_list, + 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_master->link_group_master_status == GROUP_SLAVE) { - new_master->link_group_master_status = GROUP_MASTER; - dentry->link_group_master_status = GROUP_SLAVE; + if (new_owner->ads_entries_status == ADS_ENTRIES_USER) { + new_owner->ads_entries_status = ADS_ENTRIES_OWNER; break; } } + dentry->ads_entries_status = ADS_ENTRIES_USER; } struct list_head *next; - next = dentry->link_group_list.next; list_del(&dentry->link_group_list); - /*if (next->next == next)*/ - /*container_of(next, struct dentry, link_group_list)->hard_link = 0;*/ free_dentry(dentry); } -static bool dentries_have_same_ads(const struct dentry *d1, - const struct dentry *d2) -{ - /* Verify stream names and hashes are the same */ - for (u16 i = 0; i < d1->num_ads; i++) { - if (strcmp(d1->ads_entries[i].stream_name_utf8, - d2->ads_entries[i].stream_name_utf8) != 0) - return false; - if (memcmp(d1->ads_entries[i].hash, - d2->ads_entries[i].hash, - WIM_HASH_SIZE) != 0) - return false; - } - return true; -} - -/* Share the alternate stream entries between hard-linked dentries. */ -int share_dentry_ads(struct dentry *master, struct dentry *slave) -{ - const char *mismatch_type; - wimlib_assert(master->num_ads == 0 || - master->ads_entries != slave->ads_entries); - if (master->attributes != slave->attributes) { - mismatch_type = "attributes"; - goto mismatch; - } - if (master->attributes & FILE_ATTRIBUTE_DIRECTORY) { - WARNING("`%s' is hard-linked to `%s', which is a directory ", - slave->full_path_utf8, master->full_path_utf8); - return WIMLIB_ERR_INVALID_DENTRY; - } - if (master->security_id != slave->security_id) { - mismatch_type = "security ID"; - goto mismatch; - } - if (memcmp(master->hash, slave->hash, WIM_HASH_SIZE) != 0) { - mismatch_type = "main file resource"; - goto mismatch; - } - if (!dentries_have_same_ads(master, slave)) { - mismatch_type = "Alternate Stream Entries"; - goto mismatch; - } - dentry_free_ads_entries(slave); - slave->ads_entries = master->ads_entries; - slave->link_group_master_status = GROUP_SLAVE; - return 0; -mismatch: - WARNING("Dentries `%s' and `%s' in the same hard-link group but " - "do not share the same %s", - master->full_path_utf8, slave->full_path_utf8, - mismatch_type); - return WIMLIB_ERR_INVALID_DENTRY; -} /* clones a dentry. * @@ -639,11 +594,12 @@ static int do_free_dentry(struct dentry *dentry, void *__args) struct free_dentry_args *args = (struct free_dentry_args*)__args; if (args->lt_decrement_refcnt && !dentry_is_directory(dentry)) { + wimlib_assert(!dentry->resolved); lookup_table_decrement_refcnt(args->lookup_table, dentry->hash); } - wimlib_assert(dentry->refcnt >= 1); + wimlib_assert(dentry->refcnt != 0); if (--dentry->refcnt == 0) free_dentry(dentry); return 0; @@ -683,6 +639,7 @@ int increment_dentry_refcnt(struct dentry *dentry, void *ignore) */ void link_dentry(struct dentry *dentry, struct dentry *parent) { + wimlib_assert(dentry_is_directory(parent)); dentry->parent = parent; if (parent->children) { /* Not an only child; link to siblings. */ @@ -730,6 +687,8 @@ static inline void recalculate_dentry_size(struct dentry *dentry) dentry->length = (dentry->length + 7) & ~7; } +/* Duplicates a UTF-8 name into UTF-8 and UTF-16 strings and returns the strings + * and their lengths in the pointer arguments */ int get_names(char **name_utf16_ret, char **name_utf8_ret, u16 *name_utf16_len_ret, u16 *name_utf8_len_ret, const char *name) diff --git a/src/dentry.h b/src/dentry.h index b6aa966f..03281447 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -77,6 +77,7 @@ 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; }; @@ -204,7 +205,7 @@ struct dentry { union { /* Number of references to the dentry tree itself, as in multiple * WIMStructs */ - int refcnt; + u32 refcnt; /* Number of times this dentry has been opened (only for * directories!) */ @@ -218,16 +219,16 @@ struct dentry { enum { /* This dentry is the owner of its ads_entries, although it may * be in a hard link set */ - GROUP_INDEPENDENT = 0, + ADS_ENTRIES_DEFAULT = 0, /* This dentry is the owner of the ads_entries in the hard link * set */ - GROUP_MASTER, + ADS_ENTRIES_OWNER, /* This dentry shares its ads_entries with a dentry in the hard - * link set that has GROUP_MASTER set. */ - GROUP_SLAVE - } link_group_master_status; + * link set that has ADS_ENTRIES_OWNER set. */ + ADS_ENTRIES_USER + } ads_entries_status; /* List of dentries in the hard link set */ @@ -266,10 +267,12 @@ dentry_lte(const struct dentry *dentry) return dentry->lte; } +/* Return the number of dentries in the hard link group */ static inline size_t dentry_link_group_size(const struct dentry *dentry) { const struct list_head *cur = &dentry->link_group_list; size_t size = 0; + wimlib_assert(cur != NULL); do { size++; cur = cur->next; @@ -324,8 +327,6 @@ 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 void put_dentry(struct dentry *dentry); -extern int share_dentry_ads(struct dentry *master, - struct dentry *slave); extern struct dentry *clone_dentry(struct dentry *old); extern void free_dentry_tree(struct dentry *root, struct lookup_table *lookup_table, @@ -349,8 +350,7 @@ extern int read_dentry_tree(const u8 metadata_resource[], extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p); -/* Inline utility functions for WIMDentries */ - +/* Inline utility functions for dentries */ static inline bool dentry_is_root(const struct dentry *dentry) { diff --git a/src/hardlink.c b/src/hardlink.c index 8067020b..20faa555 100644 --- a/src/hardlink.c +++ b/src/hardlink.c @@ -3,12 +3,16 @@ #include "list.h" #include "lookup_table.h" +/* Hard link group; it's identified by its hard link group ID and consists of a + * circularly linked list of dentries. */ struct link_group { u64 link_group_id; struct link_group *next; struct list_head *dentry_list; }; +/* Hash table to find hard link groups, identified by their hard link group ID. + * */ struct link_group_table { struct link_group **array; u64 num_entries; @@ -16,8 +20,7 @@ struct link_group_table { struct link_group *singles; }; -#include - +/* Returns pointer to a new link group table having the specified capacity */ struct link_group_table *new_link_group_table(u64 capacity) { struct link_group_table *table; @@ -42,14 +45,19 @@ err: return NULL; } -/* Insert a dentry into the hard link group table based on its hard link group +/* + * Insert a dentry into the hard link group table based on its hard link group * ID. * * If there is already a dentry in the table having the same hard link group ID, * we link the dentries together in a circular list. * - * If the hard link group ID is 0, this is a no-op and the dentry is not - * inserted. + * If the hard link group ID is 0, this indicates a dentry that's in a hard link + * group by itself (has a link count of 1). We can't insert it into the hash + * table itself because we don't know what hard link group IDs are available to + * give it (this could be kept track of but would be more difficult). Instead + * we keep a linked list of the single dentries, and assign them hard link group + * IDs later. */ int link_group_table_insert(struct dentry *dentry, struct link_group_table *table) { @@ -68,39 +76,46 @@ int link_group_table_insert(struct dentry *dentry, struct link_group_table *tabl table->singles = group; INIT_LIST_HEAD(&dentry->link_group_list); group->dentry_list = &dentry->link_group_list; - return 0; - } + } else { + /* Hard link group that may should multiple dentries (the code + * will work even if the group actually contains only 1 dentry + * though) */ - /* Try adding to existing hard link group */ - pos = dentry->hard_link % table->capacity; - group = table->array[pos]; - while (group) { - if (group->link_group_id == dentry->hard_link) { - list_add(&dentry->link_group_list, group->dentry_list); - return 0; + /* Try adding to existing hard link group */ + pos = dentry->hard_link % table->capacity; + group = table->array[pos]; + while (group) { + if (group->link_group_id == dentry->hard_link) { + list_add(&dentry->link_group_list, + group->dentry_list); + return 0; + } + group = group->next; } - group = group->next; - } - /* Add new hard link group to the table */ + /* Add new hard link group to the table */ - group = MALLOC(sizeof(struct link_group)); - if (!group) - return WIMLIB_ERR_NOMEM; - group->link_group_id = dentry->hard_link; - group->next = table->array[pos]; - INIT_LIST_HEAD(&dentry->link_group_list); - group->dentry_list = &dentry->link_group_list; - table->array[pos] = group; + group = MALLOC(sizeof(struct link_group)); + if (!group) + return WIMLIB_ERR_NOMEM; + group->link_group_id = dentry->hard_link; + group->next = table->array[pos]; + INIT_LIST_HEAD(&dentry->link_group_list); + group->dentry_list = &dentry->link_group_list; + table->array[pos] = group; - /* XXX Make the table grow when too many entries have been inserted. */ - table->num_entries++; + /* XXX Make the table grow when too many entries have been + * inserted. */ + table->num_entries++; + } return 0; } /* Frees a link group table. */ void free_link_group_table(struct link_group_table *table) { + struct link_group *single, *next; + if (!table) return; if (table->array) { @@ -115,6 +130,13 @@ void free_link_group_table(struct link_group_table *table) } FREE(table->array); } + single = table->singles; + while (single) { + next = single->next; + FREE(single); + single = next; + } + FREE(table); } @@ -123,6 +145,9 @@ void free_link_group_table(struct link_group_table *table) u64 assign_link_groups(struct link_group_table *table) { DEBUG("Assigning link groups"); + + /* Assign consecutive link group IDs to each link group in the hash + * table */ u64 id = 1; for (u64 i = 0; i < table->capacity; i++) { struct link_group *group = table->array[i]; @@ -142,7 +167,8 @@ u64 assign_link_groups(struct link_group_table *table) group = group->next; } } - /* Singles */ + /* Assign link group IDs to the link groups that previously had link + * group IDs of 0, and insert them into the hash table */ struct link_group *single = table->singles; while (single) { struct dentry *dentry; @@ -166,41 +192,108 @@ u64 assign_link_groups(struct link_group_table *table) return id; } +static bool dentries_have_same_ads(const struct dentry *d1, + const struct dentry *d2) +{ + /* Verify stream names and hashes are the same */ + for (u16 i = 0; i < d1->num_ads; i++) { + if (strcmp(d1->ads_entries[i].stream_name_utf8, + d2->ads_entries[i].stream_name_utf8) != 0) + return false; + if (memcmp(d1->ads_entries[i].hash, + d2->ads_entries[i].hash, + WIM_HASH_SIZE) != 0) + return false; + } + return true; +} + +/* Share the alternate stream entries between hard-linked dentries. */ +static int share_dentry_ads(struct dentry *owner, struct dentry *user) +{ + const char *mismatch_type; + wimlib_assert(owner->num_ads == 0 || + owner->ads_entries != user->ads_entries); + if (owner->attributes != user->attributes) { + mismatch_type = "attributes"; + goto mismatch; + } + if (owner->attributes & FILE_ATTRIBUTE_DIRECTORY) { + WARNING("`%s' is hard-linked to `%s', which is a directory ", + user->full_path_utf8, owner->full_path_utf8); + return WIMLIB_ERR_INVALID_DENTRY; + } + if (owner->security_id != user->security_id) { + mismatch_type = "security ID"; + goto mismatch; + } + if (memcmp(owner->hash, user->hash, WIM_HASH_SIZE) != 0) { + mismatch_type = "main file resource"; + goto mismatch; + } + if (!dentries_have_same_ads(owner, user)) { + mismatch_type = "Alternate Stream Entries"; + goto mismatch; + } + dentry_free_ads_entries(user); + user->ads_entries = owner->ads_entries; + user->ads_entries_status = ADS_ENTRIES_USER; + return 0; +mismatch: + WARNING("Dentries `%s' and `%s' in the same hard-link group but " + "do not share the same %s", + owner->full_path_utf8, user->full_path_utf8, + mismatch_type); + return WIMLIB_ERR_INVALID_DENTRY; +} + static int link_group_free_duplicate_data(struct link_group *group, struct link_group **bad_links) { - struct dentry *master, *slave, *tmp; + struct dentry *owner, *user, *tmp; - master = container_of(group->dentry_list, struct dentry, + owner = container_of(group->dentry_list, struct dentry, link_group_list); - master->link_group_master_status = GROUP_MASTER; + owner->ads_entries_status = ADS_ENTRIES_OWNER; - list_for_each_entry_safe(slave, tmp, group->dentry_list, + list_for_each_entry_safe(user, tmp, group->dentry_list, link_group_list) { - /* I would it to be an error if two dentries are the same hard - * link group but have irreconcilable differences such as - * different file permissions, but unfortunately some of M$'s + /* I would like it to be an error if two dentries are in the + * same hard link group but have irreconcilable differences such + * as different file permissions, but unfortunately some of M$'s * WIMs contain many instances of this error. This problem is * worked around here by splitting each offending dentry off * into its own hard link group. */ - if (share_dentry_ads(master, slave) != 0) { + if (share_dentry_ads(owner, user) != 0) { struct link_group *single; single = MALLOC(sizeof(struct link_group)); if (!single) return WIMLIB_ERR_NOMEM; - list_del(&slave->link_group_list); - INIT_LIST_HEAD(&slave->link_group_list); + list_del(&user->link_group_list); + INIT_LIST_HEAD(&user->link_group_list); single->link_group_id = 0; single->next = *bad_links; - single->dentry_list = &slave->link_group_list; + single->dentry_list = &user->link_group_list; *bad_links = single; - slave->link_group_master_status = GROUP_INDEPENDENT; + user->ads_entries_status = ADS_ENTRIES_OWNER; } } return 0; } +/* + * Goes through each link group and shares the ads_entries (Alternate Data + * Stream entries) field of each dentry between members of a hard link group. + * + * In the process, the dentries in each link group are checked for consistency. + * If they contain data features that indicate they cannot really be in the same + * hard link group, this should be an error, but in reality this case needs to + * be handled, so we split the dentries into different hard link groups. + * + * One of the dentries in the group is arbitrarily assigned the role of "owner" + * (ADS_ENTRIES_OWNER), while the others are "users" (ADS_ENTRIES_USER). + */ int link_groups_free_duplicate_data(struct link_group_table *table) { for (u64 i = 0; i < table->capacity; i++) { diff --git a/src/list.h b/src/list.h index c34706fb..71ba09f7 100644 --- a/src/list.h +++ b/src/list.h @@ -34,6 +34,13 @@ 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 dentry (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 { diff --git a/src/lookup_table.c b/src/lookup_table.c index 38b9947b..9edde937 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -383,7 +383,12 @@ __lookup_resource(const struct lookup_table *lookup_table, const u8 hash[]) return NULL; } -/* Only for resolved lte's */ +/* + * Finds the dentry, lookup table entry, and stream index for a WIM file stream, + * given a path name. + * + * This is only for pre-resolved dentries. + */ int lookup_resource(WIMStruct *w, const char *path, int lookup_flags, struct dentry **dentry_ret, @@ -431,13 +436,23 @@ out: return 0; } -/* Resolve a dentry's lookup table entries */ -static int dentry_resolve_ltes(struct dentry *dentry, void *__table) +/* Resolve a dentry's lookup table entries + * + * This replaces the SHA1 hash fields (which are used to lookup an entry in the + * lookup table) with pointers directly to the lookup table entries. A circular + * linked list of streams sharing the same lookup table entry is created. + * + * This function always succeeds; unresolved lookup table entries are given a + * NULL pointer. + */ +int dentry_resolve_ltes(struct dentry *dentry, void *__table) { struct lookup_table *table = __table; struct lookup_table_entry *lte; - /* Default file stream */ + wimlib_assert(!dentry->resolved); + + /* Resolve the default file stream */ lte = __lookup_resource(table, dentry->hash); if (lte) list_add(&dentry->lte_group_list.list, <e->lte_group_list); @@ -447,8 +462,8 @@ static int dentry_resolve_ltes(struct dentry *dentry, void *__table) dentry->lte_group_list.type = STREAM_TYPE_NORMAL; dentry->resolved = true; - /* Alternate data streams */ - if (dentry->link_group_master_status != GROUP_SLAVE) { + /* Resolve the alternate data streams */ + if (dentry->ads_entries_status != ADS_ENTRIES_USER) { for (u16 i = 0; i < dentry->num_ads; i++) { struct ads_entry *cur_entry = &dentry->ads_entries[i]; @@ -464,9 +479,3 @@ static int dentry_resolve_ltes(struct dentry *dentry, void *__table) } return 0; } - -/* Resolve all the lookup table entries of a dentry tree */ -void resolve_lookup_table_entries(struct dentry *root, struct lookup_table *table) -{ - for_dentry_in_tree(root, dentry_resolve_ltes, table); -} diff --git a/src/lookup_table.h b/src/lookup_table.h index 4ec44cd3..7d8474f6 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -24,7 +24,14 @@ struct lookup_table { struct wimlib_fd; -/* An entry in the lookup table in the WIM file. */ +/* + * An entry in the lookup table in the WIM file. + * + * It is used to find data streams for files in the WIM. + * + * The lookup_table_entry for a given dentry in the WIM is found using the SHA1 + * message digest field. + */ struct lookup_table_entry { /* The next struct lookup_table_entry in the hash bucket. NULL if this is the @@ -83,7 +90,10 @@ struct lookup_table_entry { /* Compression type used in other WIM. */ int other_wim_ctype; }; - struct { + + struct { /* Used for wimlib_mount */ + + /* File descriptors table for this data stream */ struct wimlib_fd **fds; u16 num_allocated_fds; u16 num_opened_fds; @@ -102,7 +112,17 @@ struct lookup_table_entry { * file resource when written to the output file. */ u32 out_refcnt; struct resource_entry output_resource_entry; + + /* 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. */ struct list_head staging_list; }; @@ -149,8 +169,7 @@ extern int write_lookup_table_entry(struct lookup_table_entry *lte, void *__out) extern void free_lookup_table_entry(struct lookup_table_entry *lte); -extern void resolve_lookup_table_entries(struct dentry *root, - struct lookup_table *table); +extern int dentry_resolve_ltes(struct dentry *dentry, void *__table); /* Writes the lookup table to the output file. */ static inline int write_lookup_table(struct lookup_table *table, FILE *out) diff --git a/src/mount.c b/src/mount.c index 286eadbd..68909618 100644 --- a/src/mount.c +++ b/src/mount.c @@ -88,11 +88,15 @@ static inline int get_lookup_flags() return 0; } +/* Returns nonzero if write permission is requested on the file open flags */ 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, struct wimlib_fd **fd_ret) { @@ -107,13 +111,12 @@ static int alloc_wimlib_fd(struct lookup_table_entry *lte, return -EMFILE; num_new_fds = min(fds_per_alloc, max_fds - lte->num_allocated_fds); - fds = CALLOC(lte->num_allocated_fds + num_new_fds, - sizeof(lte->fds[0])); + fds = REALLOC(lte->fds, (lte->num_allocated_fds + num_new_fds) * + sizeof(lte->fds[0])); if (!fds) return -ENOMEM; - memcpy(fds, lte->fds, - lte->num_allocated_fds * sizeof(lte->fds[0])); - FREE(lte->fds); + memset(&fds[lte->num_allocated_fds], 0, + num_new_fds * sizeof(fds[0])); lte->fds = fds; lte->num_allocated_fds += num_new_fds; } @@ -150,26 +153,30 @@ static int close_wimlib_fd(struct wimlib_fd *fd) unlink(lte->staging_file_name); free_lookup_table_entry(lte); } + wimlib_assert(lte->fds[fd->idx] == fd); lte->fds[fd->idx] = NULL; FREE(fd); return 0; } +/* Remove a dentry and all its alternate file streams */ static void remove_dentry(struct dentry *dentry, struct lookup_table *lookup_table) { - const u8 *hash = dentry->hash; + wimlib_assert(dentry); + wimlib_assert(dentry->resolved); + + struct lookup_table_entry *lte = dentry->lte; u16 i = 0; - struct lookup_table_entry *lte; while (1) { - lte = lookup_table_decrement_refcnt(lookup_table, hash); + lte = lte_decrement_refcnt(lte, lookup_table); if (lte && lte->num_opened_fds) for (u16 i = 0; i < lte->num_allocated_fds; i++) if (lte->fds[i] && lte->fds[i]->dentry == dentry) lte->fds[i]->dentry = NULL; if (i == dentry->num_ads) break; - hash = dentry->ads_entries[i].hash; + lte = dentry->ads_entries[i].lte; i++; } @@ -271,10 +278,12 @@ 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 +/* + * Removes open file descriptors from a lookup table entry @old_lte where the * file descriptors have opened the corresponding file resource in the context * of the hard link group @link_group; these file descriptors are extracted and - * placed in a new lookup table entry, which is returned. */ + * placed in a new lookup table entry, which is returned. + */ static struct lookup_table_entry * lte_extract_fds(struct lookup_table_entry *old_lte, u64 link_group) { @@ -288,7 +297,7 @@ lte_extract_fds(struct lookup_table_entry *old_lte, u64 link_group) num_transferred_fds = 0; for (u16 i = 0; i < old_lte->num_allocated_fds; i++) - if (old_lte->fds[i] && + if (old_lte->fds[i] && old_lte->fds[i]->dentry && old_lte->fds[i]->dentry->hard_link == link_group) num_transferred_fds++; DEBUG("Transferring %u file descriptors", @@ -299,7 +308,7 @@ lte_extract_fds(struct lookup_table_entry *old_lte, u64 link_group) return NULL; } for (u16 i = 0, j = 0; ; i++) { - if (old_lte->fds[i] && + if (old_lte->fds[i] && old_lte->fds[i]->dentry && old_lte->fds[i]->dentry->hard_link == link_group) { struct wimlib_fd *fd = old_lte->fds[i]; old_lte->fds[i] = NULL; @@ -335,6 +344,8 @@ static void lte_transfer_ads_entry(struct lookup_table_entry *new_lte, 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; @@ -344,18 +355,19 @@ 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);*/ + 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->hard_link == dentry->hard_link); 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); } @@ -364,17 +376,15 @@ static void lte_transfer_stream_entries(struct lookup_table_entry *new_lte, /* * Extract a WIM resource to the staging directory. * - * 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 - * - Transfer stream entries from the old lte's list to the new lte's list. + * @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) + * + * @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 * - * *lte is permitted to be NULL, in which case there is no old lookup table - * entry. + * @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, @@ -387,6 +397,21 @@ static int extract_resource_to_staging_dir(struct dentry *dentry, 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 resource `%s' to staging directory", dentry->full_path_utf8); old_lte = *lte; @@ -426,12 +451,14 @@ static int extract_resource_to_staging_dir(struct dentry *dentry, * groups that were identical, but now we are changing * one of them) */ - /* XXX The ADS really complicate things here and not + /* XXX + * The ADS really complicate things here and not * everything is going to work correctly yet. For * example it could be the same that a file contains two * file streams that are identical and therefore share * the same lookup table entry despite the fact that the - * streams themselves are not hardlinked. */ + * streams themselves are not hardlinked. + * XXX*/ wimlib_assert(old_lte->refcnt > link_group_size); new_lte = lte_extract_fds(old_lte, dentry->hard_link); @@ -976,7 +1003,7 @@ static int wimfs_link(const char *to, const char *from) } /* The ADS entries are owned by another dentry. */ - from_dentry->link_group_master_status = GROUP_SLAVE; + from_dentry->ads_entries_status = ADS_ENTRIES_USER; link_dentry(from_dentry, from_dentry_parent); return 0; @@ -1159,6 +1186,8 @@ static int wimfs_read(const char *path, char *buf, size_t size, return 0; } + wimlib_assert(fd->lte); + if (fd->lte->staging_file_name) { /* Read from staging file */ @@ -1174,7 +1203,6 @@ static int wimfs_read(const char *path, char *buf, size_t size, return -errno; return ret; } else { - /* Read from WIM */ struct resource_entry *res_entry; @@ -1201,11 +1229,12 @@ static int wimfs_read(const char *path, char *buf, size_t size, /* Fills in the entries of the directory specified by @path using the * FUSE-provided function @filler. */ static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi) { struct dentry *parent, *child; parent = (struct dentry*)fi->fh; + wimlib_assert(parent); child = parent->children; filler(buf, ".", NULL, 0); @@ -1264,6 +1293,7 @@ static int wimfs_releasedir(const char *path, struct fuse_file_info *fi) { struct dentry *dentry = (struct dentry *)fi->fh; + wimlib_assert(dentry); wimlib_assert(dentry->num_times_opened); if (--dentry->num_times_opened == 0) free_dentry(dentry); @@ -1303,6 +1333,8 @@ static int wimfs_rename(const char *from, const char *to) return -ENOMEM; if (dst) { + /* Destination file exists */ + if (src == dst) /* Same file */ return 0; @@ -1321,9 +1353,13 @@ static int wimfs_rename(const char *from, const char *to) parent_of_dst = dst->parent; remove_dentry(dst, w->lookup_table); } else { + /* Destination does not exist */ parent_of_dst = get_parent_dentry(w, to); if (!parent_of_dst) return -ENOENT; + + if (!dentry_is_directory(parent_of_dst)) + return -ENOTDIR; } FREE(src->file_name); @@ -1459,8 +1495,9 @@ static int wimfs_unlink(const char *path) struct ads_entry *ads_entry; ads_entry = &dentry->ads_entries[stream_idx - 1]; - lte_decrement_refcnt(lte, w->lookup_table); - list_del(&ads_entry->lte_group_list.list); + lte = lte_decrement_refcnt(lte, w->lookup_table); + if (lte) + list_del(&ads_entry->lte_group_list.list); dentry_remove_ads(dentry, ads_entry); } /* Beware: The lookup table entry(s) may still be referenced by users @@ -1512,7 +1549,6 @@ static int wimfs_write(const char *path, const char *buf, size_t size, return ret; } - static struct fuse_operations wimfs_operations = { .access = wimfs_access, .destroy = wimfs_destroy, @@ -1568,7 +1604,9 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, next_link_group_id = assign_link_groups(wim->image_metadata[image - 1].lgt); - resolve_lookup_table_entries(wim_root_dentry(wim), wim->lookup_table); + /* Resolve all the lookup table entries of the dentry tree */ + for_dentry_in_tree(wim_root_dentry(wim), dentry_resolve_ltes, + wim->lookup_table); if (flags & WIMLIB_MOUNT_FLAG_READWRITE) wim_get_current_image_metadata(wim)->modified = true; @@ -1593,19 +1631,28 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, argv[argc++] = p; argv[argc++] = "-s"; /* disable multi-threaded operation */ - if (flags & WIMLIB_MOUNT_FLAG_DEBUG) { + if (flags & WIMLIB_MOUNT_FLAG_DEBUG) argv[argc++] = "-d"; - } + + /* + * We provide the use_ino option because we are going to assign inode + * numbers oursides. We've already numbered the hard link groups with + * unique numbers with the assign_link_groups() function, and the static + * variable next_link_group_id is set to the next available link group + * ID that we will assign to new dentries. + */ char optstring[256] = "use_ino"; argv[argc++] = "-o"; argv[argc++] = optstring; if ((flags & WIMLIB_MOUNT_FLAG_READWRITE)) { + /* Read-write mount. Make the staging directory */ make_staging_dir(); if (!staging_dir_name) { FREE(p); return WIMLIB_ERR_MKDIR; } } else { + /* Read-only mount */ strcat(optstring, ",ro"); } argv[argc] = NULL; diff --git a/src/resource.c b/src/resource.c index 48f54804..49de59a4 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1004,6 +1004,7 @@ int read_metadata_resource(FILE *fp, int wim_ctype, struct image_metadata *imd) goto out_free_dentry_tree; DEBUG("Building link group table"); + /* Build hash table that maps hard link group IDs to dentry sets */ lgt = new_link_group_table(9001); if (!lgt) goto out_free_dentry_tree; diff --git a/src/security.c b/src/security.c index b7b333e2..bc44b45a 100644 --- a/src/security.c +++ b/src/security.c @@ -267,7 +267,7 @@ void free_security_data(struct wim_security_data *sd) { if (!sd) return; - wimlib_assert(sd->refcnt >= 1); + wimlib_assert(sd->refcnt != 0); if (--sd->refcnt == 0) { u8 **descriptors = sd->descriptors; u32 num_entries = sd->num_entries; diff --git a/src/util.c b/src/util.c index e14ddd40..c229b5a6 100644 --- a/src/util.c +++ b/src/util.c @@ -428,6 +428,10 @@ const char *path_basename(const char *path) return p + 1; } +/* + * Returns a pointer to the part of @path following the first colon in the last + * path component, or NULL if the last path component does not contain a colon. + */ const char *path_stream_name(const char *path) { const char *base = path_basename(path); diff --git a/src/util.h b/src/util.h index a3984bb3..73f86ed4 100644 --- a/src/util.h +++ b/src/util.h @@ -99,7 +99,7 @@ extern void wimlib_warning(const char *format, ...) #ifdef ENABLE_CUSTOM_MEMORY_ALLOCATOR extern void *(*wimlib_malloc_func)(size_t); extern void (*wimlib_free_func)(void *); -extern void *(*wimlib_realloc)(void *, size_t); +extern void *(*wimlib_realloc_func)(void *, size_t); extern void *wimlib_calloc(size_t nmemb, size_t size); extern char *wimlib_strdup(const char *str); # define MALLOC wimlib_malloc_func diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index 40ff8427..3ebf205c 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -206,7 +206,7 @@ struct wim_security_data { /* keep track of how many WIMs reference this security data (used when * exporting images between WIMs) */ u32 refcnt; -} WIMSecurityData; +}; struct link_group_table; -- 2.43.0