From 63a5804943519281f206ca0fefacfe2b99fc9958 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 3 Sep 2012 17:23:49 -0500 Subject: [PATCH] inode updates (IN PROGRESS) --- src/dentry.c | 76 +++++++++++++++++++++--- src/dentry.h | 22 +++++-- src/extract.c | 14 ++--- src/hardlink.c | 17 +++--- src/lookup_table.c | 37 +++++++----- src/lookup_table.h | 6 +- src/modify.c | 10 ++-- src/mount.c | 133 +++++++++++++++++------------------------- src/ntfs-apply.c | 116 ++++++++++++++++++------------------ src/ntfs-capture.c | 32 +++++----- src/resource.c | 6 +- src/wimlib_internal.h | 2 + src/write.c | 2 +- tests/test-imagex | 6 +- 14 files changed, 269 insertions(+), 210 deletions(-) diff --git a/src/dentry.c b/src/dentry.c index a9d13f69..32c83570 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -196,6 +196,9 @@ struct ads_entry *inode_add_ads(struct inode *inode, const char *stream_name) return NULL; inode->num_ads = num_ads; ads_entries[num_ads - 1] = new_entry; +#ifdef WITH_FUSE + new_entry->stream_id = inode->next_stream_id++; +#endif return new_entry; } @@ -912,7 +915,7 @@ void calculate_dir_tree_statistics(struct dentry *root, struct lookup_table *tab stats.file_count = file_count_ret; stats.total_bytes = total_bytes_ret; stats.hard_link_bytes = hard_link_bytes_ret; - for_lookup_table_entry(table, zero_out_refcnts, NULL); + for_lookup_table_entry(table, lte_zero_out_refcnt, NULL); for_dentry_in_tree(root, calculate_dentry_statistics, &stats); } @@ -988,6 +991,10 @@ static int read_ads_entries(const u8 *p, struct inode *inode, ads_entries[i] = cur_entry; + #ifdef WITH_FUSE + ads_entries[i]->stream_id = i + 1; + #endif + /* Read the base stream entry, excluding the stream name. */ if (remaining_size < WIM_ADS_ENTRY_DISK_SIZE) { ERROR("Stream entries go past end of metadata resource"); @@ -1072,6 +1079,9 @@ static int read_ads_entries(const u8 *p, struct inode *inode, remaining_size -= total_length; } inode->ads_entries = ads_entries; +#ifdef WITH_FUSE + inode->next_stream_id = inode->num_ads + 1; +#endif return 0; out_free_ads_entries: for (u16 i = 0; i < num_ads; i++) @@ -1334,25 +1344,23 @@ out_free_inode: return ret; } -/* Run some miscellaneous verifications on a WIM dentry */ -int verify_dentry(struct dentry *dentry, void *wim) +int verify_inode(struct inode *inode, const WIMStruct *w) { - const WIMStruct *w = wim; const struct lookup_table *table = w->lookup_table; const struct wim_security_data *sd = wim_const_security_data(w); - const struct inode *inode = dentry->inode; + const struct dentry *first_dentry = inode_first_dentry(inode); int ret = WIMLIB_ERR_INVALID_DENTRY; /* Check the security ID */ if (inode->security_id < -1) { ERROR("Dentry `%s' has an invalid security ID (%d)", - dentry->full_path_utf8, inode->security_id); + first_dentry->full_path_utf8, inode->security_id); goto out; } if (inode->security_id >= sd->num_entries) { ERROR("Dentry `%s' has an invalid security ID (%d) " "(there are only %u entries in the security table)", - dentry->full_path_utf8, inode->security_id, + first_dentry->full_path_utf8, inode->security_id, sd->num_entries); goto out; } @@ -1368,9 +1376,40 @@ int verify_dentry(struct dentry *dentry, void *wim) lte = __lookup_resource(table, hash); if (!lte && !is_zero_hash(hash)) { ERROR("Could not find lookup table entry for stream " - "%u of dentry `%s'", i, dentry->full_path_utf8); + "%u of dentry `%s'", i, first_dentry->full_path_utf8); goto out; } + if (lte && (lte->real_refcnt += inode->link_count) > lte->refcnt) + { + #ifdef ENABLE_ERROR_MESSAGES + WARNING("The following lookup table entry " + "has a reference count of %u, but", + lte->refcnt); + WARNING("We found %zu references to it", + lte->real_refcnt); + WARNING("(One dentry referencing it is at `%s')", + first_dentry->full_path_utf8); + + print_lookup_table_entry(lte); + #endif + /* Guess what! install.wim for Windows 8 + * contains a stream with 2 dentries referencing + * it, but the lookup table entry has reference + * count of 1. So we will need to handle this + * case and not just make it be an error... I'm + * just setting the reference count to the + * number of references we found. + * (Unfortunately, even after doing this, the + * reference count could be too low if it's also + * referenced in other WIM images) */ + + #if 1 + lte->refcnt = lte->real_refcnt; + WARNING("Fixing reference count"); + #else + goto out; + #endif + } } } @@ -1384,9 +1423,28 @@ int verify_dentry(struct dentry *dentry, void *wim) } if (num_unnamed_streams > 1) { ERROR("Dentry `%s' has multiple (%u) un-named streams", - dentry->full_path_utf8, num_unnamed_streams); + first_dentry->full_path_utf8, num_unnamed_streams); goto out; } + inode->verified = true; + ret = 0; +out: + return ret; +} + + +/* Run some miscellaneous verifications on a WIM dentry */ +int verify_dentry(struct dentry *dentry, void *wim) +{ + const WIMStruct *w = wim; + const struct inode *inode = dentry->inode; + int ret = WIMLIB_ERR_INVALID_DENTRY; + + if (!dentry->inode->verified) { + ret = verify_inode(dentry->inode, w); + if (ret != 0) + goto out; + } /* Cannot have a short name but no long name */ if (dentry->short_name_len && !dentry->file_name_len) { diff --git a/src/dentry.h b/src/dentry.h index 28d5ab33..e2b62946 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -68,7 +68,8 @@ struct ads_entry { struct lookup_table_entry *lte; }; - /* Length of stream name (UTF-16) */ + /* Length of stream name (UTF-16). This is in bytes, not characters, + * and does not include the terminating null character */ u16 stream_name_len; /* Length of stream name (UTF-8) */ @@ -136,7 +137,9 @@ struct inode { * the @lte field is valid, but the @hash field is not valid) * * (This is not an on-disk field.) */ - bool resolved; + u8 resolved : 1; + + u8 verified : 1; u16 num_ads; @@ -289,9 +292,17 @@ struct dentry { /* List of dentries in the hard link set */ struct list_head inode_dentry_list; - struct list_head tmp_list; + union { + struct list_head tmp_list; + bool is_extracted; + }; }; +static inline bool dentry_is_extracted(const struct dentry *dentry) +{ + return dentry->is_extracted; +} + extern struct ads_entry *inode_get_ads_entry(struct inode *inode, const char *stream_name, @@ -330,7 +341,8 @@ 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 inode *wim_pathname_to_inode(struct 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); @@ -373,11 +385,11 @@ extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p); static inline struct dentry *inode_first_dentry(struct inode *inode) { + wimlib_assert(inode->dentry_list.next != &inode->dentry_list); 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; diff --git a/src/extract.c b/src/extract.c index 6e01191b..1dd2f618 100644 --- a/src/extract.c +++ b/src/extract.c @@ -204,14 +204,15 @@ static int extract_regular_file(WIMStruct *w, if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) && lte) { - if (++lte->out_refcnt != 1) + if (lte->extracted_file) { return extract_regular_file_linked(dentry, output_dir, output_path, extract_flags, lte); - FREE(lte->extracted_file); - lte->extracted_file = STRDUP(output_path); - if (!lte->extracted_file) - return WIMLIB_ERR_NOMEM; + } else { + lte->extracted_file = STRDUP(output_path); + if (!lte->extracted_file) + return WIMLIB_ERR_NOMEM; + } } return extract_regular_file_unlinked(w, dentry, output_path, @@ -429,8 +430,7 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, int image, w->lookup_table = joined_tab; } - - for_lookup_table_entry(w->lookup_table, zero_out_refcnts, NULL); + for_lookup_table_entry(w->lookup_table, lte_free_extracted_file, NULL); if (image == WIM_ALL_IMAGES) { flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE; diff --git a/src/hardlink.c b/src/hardlink.c index 7aedf8e1..5c2f20c4 100644 --- a/src/hardlink.c +++ b/src/hardlink.c @@ -142,6 +142,7 @@ int inode_table_insert(struct dentry *dentry, void *__table) * next available inode ID. */ u64 assign_inode_numbers(struct hlist_head *inode_list) { + DEBUG("Assigning inode numbers"); struct inode *inode; struct hlist_node *cur; u64 cur_ino = 1; @@ -153,8 +154,7 @@ u64 assign_inode_numbers(struct hlist_head *inode_list) } -static void -print_inode_dentries(const struct inode *inode) +static void print_inode_dentries(const struct inode *inode) { struct dentry *dentry; list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) @@ -433,13 +433,14 @@ next_dentry_2: } /* - * Goes through each inode and shares the inodes among members of a hard - * inode. + * Goes through each hard link group (dentries sharing the same hard link group + * ID field) that's been inserted into the inode table and shars the `struct + * inode's among members of each hard link group. * - * In the process, the dentries in each inode are checked for consistency. - * If they contain data features that indicate they cannot really be in the same - * inode, this should be an error, but in reality this case needs to - * be handled, so we split the dentries into different inodes. + * In the process, the dentries belonging to each inode are checked for + * consistency. If they contain data features that indicate they cannot really + * correspond to the same inode, this should be an error, but in reality this + * case needs to be handled, so we split the dentries into different inodes. */ int fix_inodes(struct inode_table *table, struct hlist_head *inode_list) { diff --git a/src/lookup_table.c b/src/lookup_table.c index 776ad94b..61cc5a83 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -358,10 +358,22 @@ int write_lookup_table_entry(struct lookup_table_entry *lte, void *__out) } +int lte_zero_real_refcnt(struct lookup_table_entry *lte, void *ignore) +{ + lte->real_refcnt = 0; + return 0; +} + +int lte_zero_out_refcnt(struct lookup_table_entry *lte, void *ignore) +{ + lte->out_refcnt = 0; + return 0; +} -int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore) +int lte_free_extracted_file(struct lookup_table_entry *lte, void *ignone) { - entry->out_refcnt = 0; + FREE(lte->extracted_file); + lte->extracted_file = NULL; return 0; } @@ -513,7 +525,7 @@ out: return 0; } -static int inode_resolve_ltes(struct inode *inode, struct lookup_table *table) +static void inode_resolve_ltes(struct inode *inode, struct lookup_table *table) { struct lookup_table_entry *lte; @@ -529,7 +541,6 @@ static int inode_resolve_ltes(struct inode *inode, struct lookup_table *table) lte = __lookup_resource(table, cur_entry->hash); cur_entry->lte = lte; } - return 0; } /* Resolve a dentry's lookup table entries @@ -543,25 +554,21 @@ static int inode_resolve_ltes(struct inode *inode, struct lookup_table *table) */ int dentry_resolve_ltes(struct dentry *dentry, void *table) { - if (dentry->inode->resolved) - return 0; - else - return inode_resolve_ltes(dentry->inode, table); + if (!dentry->inode->resolved) + inode_resolve_ltes(dentry->inode, table); + return 0; } - - - -/* Return the lookup table entry for the unnamed data stream of a inode, or +/* Return the lookup table entry for the unnamed data stream of an inode, or * NULL if there is none. * * You'd think this would be easier than it actually is, since the unnamed data * stream should be the one referenced from the inode itself. Alas, if there * are named data streams, Microsoft's "imagex.exe" program will put the unnamed - * data stream in one of the alternate data streams instead of inside the - * inode. So we need to check the alternate data streams too. + * data stream in one of the alternate data streams instead of inside the WIM + * dentry itself. So we need to check the alternate data streams too. * - * Also, note that a inode may appear to have than one unnamed stream, but if + * Also, note that a dentry may appear to have than one unnamed stream, but if * the SHA1 message digest is all 0's then the corresponding stream does not * really "count" (this is the case for the inode's own file stream when the * file stream that should be there is actually in one of the alternate stream diff --git a/src/lookup_table.h b/src/lookup_table.h index d99f4b75..193cef03 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -164,6 +164,8 @@ struct lookup_table_entry { * dentries. */ u32 out_refcnt; + u32 real_refcnt; + /* When a WIM file is written, @output_resource_entry is filled * in with the resource entry for the output WIM. This will not * necessarily be the same as the @resource_entry since: @@ -253,7 +255,9 @@ extern int lookup_resource(WIMStruct *w, const char *path, struct lookup_table_entry **lte_ret, u16 *stream_idx_ret); -extern int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore); +extern int lte_zero_out_refcnt(struct lookup_table_entry *entry, void *ignore); +extern int lte_zero_real_refcnt(struct lookup_table_entry *entry, void *ignore); +extern int lte_free_extracted_file(struct lookup_table_entry *lte, void *ignone); extern void print_lookup_table_entry(const struct lookup_table_entry *entry); diff --git a/src/modify.c b/src/modify.c index ae6f98b4..2c92d821 100644 --- a/src/modify.c +++ b/src/modify.c @@ -249,19 +249,21 @@ struct wim_pair { static int add_lte_to_dest_wim(struct dentry *dentry, void *arg) { WIMStruct *src_wim, *dest_wim; + struct inode *inode; src_wim = ((struct wim_pair*)arg)->src_wim; dest_wim = ((struct wim_pair*)arg)->dest_wim; + inode = dentry->inode; - wimlib_assert(!dentry->inode->resolved); + wimlib_assert(!inode->resolved); - for (unsigned i = 0; i < (unsigned)dentry->inode->num_ads + 1; i++) { + for (unsigned i = 0; i <= inode->num_ads; i++) { struct lookup_table_entry *src_lte, *dest_lte; - src_lte = inode_stream_lte_unresolved(dentry->inode, i, + src_lte = inode_stream_lte_unresolved(inode, i, src_wim->lookup_table); if (!src_lte) continue; - dest_lte = inode_stream_lte_unresolved(dentry->inode, i, + dest_lte = inode_stream_lte_unresolved(inode, i, dest_wim->lookup_table); if (dest_lte) { dest_lte->refcnt++; diff --git a/src/mount.c b/src/mount.c index b67f40c6..3c8f2063 100644 --- a/src/mount.c +++ b/src/mount.c @@ -108,6 +108,8 @@ static int alloc_wimlib_fd(struct inode *inode, static const u16 fds_per_alloc = 8; static const u16 max_fds = 0xffff; + DEBUG("Allocate wimlib fd"); + if (inode->num_opened_fds == inode->num_allocated_fds) { struct wimlib_fd **fds; u16 num_new_fds; @@ -138,7 +140,9 @@ static int alloc_wimlib_fd(struct inode *inode, *fd_ret = fd; inode->fds[i] = fd; inode->num_opened_fds++; - lte->num_opened_fds++; + if (lte) + lte->num_opened_fds++; + DEBUG("Allocated fd"); return 0; } } @@ -207,7 +211,7 @@ static void remove_dentry(struct dentry *dentry, struct lookup_table_entry *lte; unsigned i; - for (i = 0; <= inode->num_ads; i++) { + for (i = 0; i <= inode->num_ads; i++) { lte = inode_stream_lte_resolved(inode, i); if (lte) lte_decrement_refcnt(lte, lookup_table); @@ -433,6 +437,13 @@ static int extract_resource_to_staging_dir(struct inode *inode, new_lte->staging_file_name = staging_file_name; new_lte->resource_location = RESOURCE_IN_STAGING_FILE; + if (stream_id == 0) + inode->lte = new_lte; + else + for (u16 i = 0; i < inode->num_ads; i++) + if (inode->ads_entries[i]->stream_id == stream_id) + inode->ads_entries[i]->lte = new_lte; + lookup_table_insert(w->lookup_table, new_lte); list_add(&new_lte->staging_list, &staging_list); *lte = new_lte; @@ -1144,13 +1155,19 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi) if (ret != 0) return ret; + DEBUG("dentry = %p", dentry); + inode = dentry->inode; + DEBUG("stream_idx = %u", stream_idx); + if (stream_idx == 0) stream_id = 0; else stream_id = inode->ads_entries[stream_idx - 1]->stream_id; + DEBUG("stream_id = %u", 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, @@ -1184,17 +1201,13 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi) /* Opens a directory. */ 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) + inode = wim_pathname_to_inode(w, path); + if (!inode) return -ENOENT; - - inode = dentry->inode; - if (!inode_is_directory(inode)) return -ENOTDIR; ret = alloc_wimlib_fd(inode, 0, NULL, &fd); @@ -1425,7 +1438,6 @@ static int wimfs_rmdir(const char *path) static int wimfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { - struct dentry *dentry; struct ads_entry *existing_ads_entry; struct ads_entry *new_ads_entry; struct lookup_table_entry *existing_lte; @@ -1441,21 +1453,20 @@ static int wimfs_setxattr(const char *path, const char *name, return -ENOATTR; name += 5; - dentry = get_dentry(w, path); - if (!dentry) + inode = wim_pathname_to_inode(w, path); + if (!inode) return -ENOENT; - inode = dentry->inode; existing_ads_entry = inode_get_ads_entry(inode, name, &ads_idx); if (existing_ads_entry) { if (flags & XATTR_CREATE) return -EEXIST; - inode_remove_ads(dentry->inode, ads_idx, w->lookup_table); + inode_remove_ads(inode, ads_idx, w->lookup_table); } else { if (flags & XATTR_REPLACE) return -ENOATTR; } - new_ads_entry = inode_add_ads(dentry, name); + new_ads_entry = inode_add_ads(inode, name); if (!new_ads_entry) return -ENOMEM; @@ -1494,7 +1505,7 @@ static int wimfs_symlink(const char *to, const char *from) { struct dentry *dentry_parent, *dentry; const char *link_name; - struct lookup_table_entry *lte; + struct inode *inode; dentry_parent = get_parent_dentry(w, from); if (!dentry_parent) @@ -1506,24 +1517,18 @@ static int wimfs_symlink(const char *to, const char *from) if (get_dentry_child_with_name(dentry_parent, link_name)) return -EEXIST; - dentry = new_dentry(link_name); + dentry = new_dentry_with_inode(link_name); if (!dentry) return -ENOMEM; + inode = dentry->inode; - dentry->attributes = FILE_ATTRIBUTE_REPARSE_POINT; - dentry->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK; - dentry->link_group_id = next_link_group_id++; + inode->attributes = FILE_ATTRIBUTE_REPARSE_POINT; + inode->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK; + inode->ino = next_ino++; - if (dentry_set_symlink(dentry, to, w->lookup_table, <e) != 0) + if (inode_set_symlink(inode, to, w->lookup_table, NULL) != 0) goto out_free_dentry; - wimlib_assert(lte); - - dentry->ads_entries[1].lte_group_list.type = STREAM_TYPE_ADS; - list_add(&dentry->ads_entries[1].lte_group_list.list, - <e->lte_group_list); - wimlib_assert(dentry->resolved); - link_dentry(dentry, dentry_parent); return 0; out_free_dentry: @@ -1538,7 +1543,9 @@ static int wimfs_truncate(const char *path, off_t size) struct dentry *dentry; struct lookup_table_entry *lte; int ret; - unsigned stream_idx; + u16 stream_idx; + u32 stream_id; + struct inode *inode; ret = lookup_resource(w, path, get_lookup_flags(), &dentry, <e, &stream_idx); @@ -1549,6 +1556,13 @@ static int wimfs_truncate(const char *path, off_t size) if (!lte) /* Already a zero-length file */ return 0; + inode = dentry->inode; + + if (stream_idx == 0) + stream_id = 0; + else + stream_id = inode->ads_entries[stream_idx - 1]->stream_id; + if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { wimlib_assert(lte->staging_file_name); ret = truncate(lte->staging_file_name, size); @@ -1559,7 +1573,7 @@ static int wimfs_truncate(const char *path, off_t size) wimlib_assert(lte->resource_location == RESOURCE_IN_WIM); /* File in WIM. Extract it to the staging directory, but only * the first @size bytes of it. */ - ret = extract_resource_to_staging_dir(dentry, stream_idx, + ret = extract_resource_to_staging_dir(inode, stream_id, <e, size); } return ret; @@ -1572,7 +1586,7 @@ static int wimfs_unlink(const char *path) struct lookup_table_entry *lte; struct inode *inode; int ret; - unsigned stream_idx; + u16 stream_idx; unsigned i; ret = lookup_resource(w, path, get_lookup_flags(), &dentry, @@ -1711,46 +1725,6 @@ static struct fuse_operations wimfs_operations = { }; -static int check_lte_refcnt(struct lookup_table_entry *lte, void *ignore) -{ - size_t lte_group_size = 0; - struct list_head *cur; - list_for_each(cur, <e->lte_group_list) - lte_group_size++; - if (lte_group_size > lte->refcnt) { -#ifdef ENABLE_ERROR_MESSAGES - struct dentry *example_dentry; - struct list_head *next; - struct stream_list_head *head; - WARNING("The following lookup table entry has a reference count " - "of %u, but", lte->refcnt); - WARNING("We found %zu references to it", lte_group_size); - next = lte->lte_group_list.next; - head = container_of(next, struct stream_list_head, list); - if (head->type == STREAM_TYPE_NORMAL) { - example_dentry = container_of(head, struct dentry, - lte_group_list); - WARNING("(One dentry referencing it is at `%s')", - example_dentry->full_path_utf8); - } - print_lookup_table_entry(lte); -#endif - /* Guess what! install.wim for Windows 8 contains a stream with - * 2 dentries referencing it, but the lookup table entry has - * reference count of 1. So we will need to handle this case - * and not just make it be an error... I'm just setting the - * reference count to the number of references we found. */ - - #if 1 - lte->refcnt = lte_group_size; - WARNING("Fixing reference count"); - #else - return WIMLIB_ERR_INVALID_DENTRY; - #endif - } - return 0; -} - /* Mounts a WIM file. */ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags, WIMStruct **additional_swms, @@ -1761,6 +1735,7 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, int ret; char *p; struct lookup_table *joined_tab, *wim_tab_save; + struct image_metadata *imd; DEBUG("Mount: wim = %p, image = %d, dir = %s, flags = %d, ", wim, image, dir, flags); @@ -1787,23 +1762,21 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, if (ret != 0) goto out; + imd = &wim->image_metadata[image - 1]; + DEBUG("Selected image %d", image); - next_link_group_id = assign_link_group_ids(wim->image_metadata[image - 1].lgt); + next_ino = assign_inode_numbers(&imd->inode_list); + + DEBUG("(next_ino = %"PRIu64")", next_ino); - DEBUG("Resolving lookup table entries"); /* Resolve all the lookup table entries of the dentry tree */ - for_dentry_in_tree(wim_root_dentry(wim), dentry_resolve_ltes, + DEBUG("Resolving lookup table entries"); + for_dentry_in_tree(imd->root_dentry, dentry_resolve_ltes, wim->lookup_table); - DEBUG("Checking lookup table entry reference counts"); - - ret = for_lookup_table_entry(wim->lookup_table, check_lte_refcnt, NULL); - if (ret != 0) - goto out; - if (flags & WIMLIB_MOUNT_FLAG_READWRITE) - wim_get_current_image_metadata(wim)->modified = true; + imd->modified = true; if (!(flags & (WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR | diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index 8d142672..5e36469f 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -104,7 +104,7 @@ extract_wim_resource_to_ntfs_attr(const struct lookup_table_entry *lte, /* Writes the data streams to a NTFS file * * @ni: The NTFS inode for the file. - * @dentry: The directory entry in the WIM file. + * @inode: The WIM dentry that has an inode containing the streams. * @w: The WIMStruct for the WIM containing the image we are applying. * * Returns 0 on success, nonzero on failure. @@ -116,17 +116,18 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry, unsigned stream_idx = 0; ntfschar *stream_name = AT_UNNAMED; u32 stream_name_len = 0; + const struct inode *inode = dentry->inode; DEBUG("Writing %u NTFS data stream%s for `%s'", - dentry->num_ads + 1, - (dentry->num_ads == 0 ? "" : "s"), + inode->num_ads + 1, + (inode->num_ads == 0 ? "" : "s"), dentry->full_path_utf8); while (1) { struct lookup_table_entry *lte; ntfs_attr *na; - lte = dentry_stream_lte(dentry, stream_idx, w->lookup_table); + lte = inode_stream_lte(inode, stream_idx, w->lookup_table); if (stream_name_len) { /* Create an empty named stream. */ @@ -159,10 +160,10 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry, break; ntfs_attr_close(na); } - if (stream_idx == dentry->num_ads) + if (stream_idx == inode->num_ads) break; - stream_name = (ntfschar*)dentry->ads_entries[stream_idx].stream_name; - stream_name_len = dentry->ads_entries[stream_idx].stream_name_len / 2; + stream_name = (ntfschar*)inode->ads_entries[stream_idx]->stream_name; + stream_name_len = inode->ads_entries[stream_idx]->stream_name_len / 2; stream_idx++; } return ret; @@ -173,12 +174,12 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry, * * It is named @from_dentry->file_name and is located under the directory * specified by @dir_ni, and it is made to point to the previously extracted - * file located at @to_dentry->extracted_file. + * file located at @inode->extracted_file. * * Return 0 on success, nonzero on failure. */ static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry, - const struct dentry *to_dentry, + const struct inode *inode, ntfs_inode *dir_ni, ntfs_inode **to_ni_ret) { @@ -191,7 +192,7 @@ static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry, ntfs_volume *vol; wimlib_assert(dentry_is_regular_file(from_dentry) - && dentry_is_regular_file(to_dentry)); + && inode_is_regular_file(inode)); if (ntfs_inode_close(dir_ni) != 0) { ERROR_WITH_ERRNO("Error closing directory"); @@ -201,13 +202,12 @@ static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry, vol = dir_ni->vol; DEBUG("Extracting NTFS hard link `%s' => `%s'", - from_dentry->full_path_utf8, to_dentry->extracted_file); + from_dentry->full_path_utf8, inode->extracted_file); - to_ni = ntfs_pathname_to_inode(vol, NULL, - to_dentry->extracted_file); + to_ni = ntfs_pathname_to_inode(vol, NULL, inode->extracted_file); if (!to_ni) { ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'", - to_dentry->extracted_file); + inode->extracted_file); return WIMLIB_ERR_NTFS_3G; } p = from_dentry->full_path_utf8 + from_dentry->full_path_utf8_len; @@ -234,7 +234,7 @@ static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry, if (ret != 0) { ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'", from_dentry->full_path_utf8, - to_dentry->extracted_file); + inode->extracted_file); ret = WIMLIB_ERR_NTFS_3G; } *to_ni_ret = to_ni; @@ -250,14 +250,14 @@ apply_file_attributes_and_security_data(ntfs_inode *ni, const WIMStruct *w) { DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32, - dentry->full_path_utf8, dentry->attributes); + dentry->full_path_utf8, dentry->inode->attributes); int ret; #ifdef HAVE_NTFS_INODE_FUNCTIONS - ret = ntfs_set_inode_attributes(ni, dentry->attributes); + ret = ntfs_set_inode_attributes(ni, dentry->inode->attributes); #else struct SECURITY_CONTEXT ctx; u32 attributes_le32; - attributes_le32 = cpu_to_le32(dentry->attributes); + attributes_le32 = cpu_to_le32(dentry->inode->attributes); memset(&ctx, 0, sizeof(ctx)); ctx.vol = ni->vol; ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ATTRIB, @@ -270,15 +270,15 @@ apply_file_attributes_and_security_data(ntfs_inode *ni, dentry->full_path_utf8); return WIMLIB_ERR_NTFS_3G; } - if (dentry->security_id != -1) { + if (dentry->inode->security_id != -1) { const struct wim_security_data *sd; const char *descriptor; sd = wim_const_security_data(w); - wimlib_assert(dentry->security_id < sd->num_entries); - descriptor = sd->descriptors[dentry->security_id]; + wimlib_assert(dentry->inode->security_id < sd->num_entries); + descriptor = sd->descriptors[dentry->inode->security_id]; DEBUG("Applying security descriptor %d to `%s'", - dentry->security_id, dentry->full_path_utf8); + dentry->inode->security_id, dentry->full_path_utf8); #ifdef HAVE_NTFS_INODE_FUNCTIONS u32 selection = OWNER_SECURITY_INFORMATION | @@ -289,7 +289,7 @@ apply_file_attributes_and_security_data(ntfs_inode *ni, #else ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL, ni, dir_ni, descriptor, - sd->sizes[dentry->security_id], 0); + sd->sizes[dentry->inode->security_id], 0); #endif if (ret != 0) { @@ -307,9 +307,9 @@ static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry, struct lookup_table_entry *lte; int ret = 0; - wimlib_assert(dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT); + wimlib_assert(dentry->inode->attributes & FILE_ATTRIBUTE_REPARSE_POINT); - lte = dentry_unnamed_lte(dentry, w->lookup_table); + lte = inode_unnamed_lte(dentry->inode, w->lookup_table); DEBUG("Applying reparse data to `%s'", dentry->full_path_utf8); @@ -327,7 +327,7 @@ static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry, u8 reparse_data_buf[8 + wim_resource_size(lte)]; u8 *p = reparse_data_buf; - p = put_u32(p, dentry->reparse_tag); /* ReparseTag */ + p = put_u32(p, dentry->inode->reparse_tag); /* ReparseTag */ p = put_u16(p, wim_resource_size(lte)); /* ReparseDataLength */ p = put_u16(p, 0); /* Reserved */ @@ -364,10 +364,10 @@ static int preapply_dentry_with_dos_name(struct dentry *dentry, struct dentry *dentry_with_dos_name; dentry_with_dos_name = NULL; - list_for_each_entry(other, &dentry->link_group_list, - link_group_list) - { - if (dentry->parent == other->parent && other->short_name_len) { + inode_for_each_dentry(other, dentry->inode) { + if (other != dentry && (dentry->parent == other->parent) + && other->short_name_len) + { if (dentry_with_dos_name) { ERROR("Found multiple DOS names for file `%s' " "in the same directory", @@ -379,7 +379,7 @@ static int preapply_dentry_with_dos_name(struct dentry *dentry, } /* If there's a dentry with a DOS name, extract it first */ if (dentry_with_dos_name - && !dentry_with_dos_name->extracted_file) + && !dentry_with_dos_name->inode->extracted_file) { char *p; const char *dir_name; @@ -430,8 +430,9 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, ntfs_inode *ni = NULL; bool is_hardlink = false; ntfs_volume *vol = dir_ni->vol; + struct inode *inode = dentry->inode; - if (dentry->attributes & FILE_ATTRIBUTE_DIRECTORY) { + if (inode->attributes & FILE_ATTRIBUTE_DIRECTORY) { type = S_IFDIR; } else { struct dentry *other; @@ -446,26 +447,26 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, } type = S_IFREG; - /* See if we can make a hard link */ - list_for_each_entry(other, &dentry->link_group_list, - link_group_list) { - if (other->extracted_file) { - /* Already extracted another dentry in the hard - * link group. We can make a hard link instead - * of extracting the file data. */ - ret = wim_apply_hardlink_ntfs(dentry, other, + + if (inode->link_count > 1) { + /* Already extracted another dentry in the hard link + * group. We can make a hard link instead of extracting + * the file data. */ + if (inode->extracted_file) { + ret = wim_apply_hardlink_ntfs(dentry, inode, dir_ni, &ni); is_hardlink = true; - if (ret) { + if (ret) goto out_close_dir_ni; - } else { - dentry->extracted_file = dentry->full_path_utf8; + else goto out_set_dos_name; - } } + /* Can't make a hard link; extract the file itself */ + FREE(inode->extracted_file); + inode->extracted_file = STRDUP(dentry->full_path_utf8); + if (!inode->extracted_file) + return WIMLIB_ERR_NOMEM; } - /* Can't make a hard link; extract the file itself */ - dentry->extracted_file = dentry->full_path_utf8; } /* @@ -486,8 +487,8 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, /* Write the data streams, unless this is a directory or reparse point * */ - if (!dentry_is_directory(dentry) && - !(dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + if (!(inode->attributes & (FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_DIRECTORY))) { ret = write_ntfs_data_streams(ni, dentry, w); if (ret != 0) goto out_close_dir_ni; @@ -498,7 +499,7 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, if (ret != 0) goto out_close_dir_ni; - if (dentry->attributes & FILE_ATTR_REPARSE_POINT) { + if (inode->attributes & FILE_ATTR_REPARSE_POINT) { ret = apply_reparse_data(ni, dentry, w); if (ret != 0) goto out_close_dir_ni; @@ -625,7 +626,7 @@ static int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg) char orig; const char *dir_name; - if (dentry->extracted_file) + if (dentry_is_extracted(dentry)) return 0; wimlib_assert(dentry->full_path_utf8); @@ -677,9 +678,9 @@ static int wim_apply_dentry_timestamps(struct dentry *dentry, void *arg) } p = buf; - p = put_u64(p, dentry->creation_time); - p = put_u64(p, dentry->last_write_time); - p = put_u64(p, dentry->last_access_time); + p = put_u64(p, dentry->inode->creation_time); + p = put_u64(p, dentry->inode->last_write_time); + p = put_u64(p, dentry->inode->last_access_time); ret = ntfs_inode_set_times(ni, (const char*)buf, 3 * sizeof(u64), 0); if (ret != 0) { ERROR_WITH_ERRNO("Failed to set NTFS timestamps on `%s'", @@ -696,11 +697,9 @@ static int wim_apply_dentry_timestamps(struct dentry *dentry, void *arg) return ret; } -static int dentry_clear_extracted_file(struct dentry *dentry, void *ignore) +static int dentry_set_unextracted(struct dentry *dentry, void *ignore) { - if (dentry->extracted_file != dentry->full_path_utf8) - FREE(dentry->extracted_file); - dentry->extracted_file = NULL; + dentry->is_extracted = false; return 0; } @@ -722,8 +721,7 @@ static int do_wim_apply_image_ntfs(WIMStruct *w, const char *device, int extract args.w = w; root = wim_root_dentry(w); - for_dentry_in_tree(root, dentry_clear_extracted_file, NULL); - + for_dentry_in_tree(root, dentry_set_unextracted, NULL); ret = for_dentry_in_tree(root, wim_apply_dentry_ntfs, &args); if (ret != 0) goto out; diff --git a/src/ntfs-capture.c b/src/ntfs-capture.c index 9a8991ec..67517547 100644 --- a/src/ntfs-capture.c +++ b/src/ntfs-capture.c @@ -336,7 +336,7 @@ static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni, lte->ntfs_loc = ntfs_loc; lte->resource_location = RESOURCE_IN_NTFS_VOLUME; if (type == AT_REPARSE_POINT) { - dentry->reparse_tag = reparse_tag; + dentry->inode->reparse_tag = reparse_tag; ntfs_loc->is_reparse_point = true; lte->resource_entry.original_size = data_size - 8; lte->resource_entry.size = data_size - 8; @@ -355,14 +355,14 @@ static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni, } if (name_length == 0) { /* Unnamed data stream. Put the reference to it in the - * dentry. */ - if (dentry->lte) { + * dentry's inode. */ + if (dentry->inode->lte) { ERROR("Found two un-named data streams for " "`%s'", path); ret = WIMLIB_ERR_NTFS_3G; goto out_free_lte; } - dentry->lte = lte; + dentry->inode->lte = lte; } else { /* Named data stream. Put the reference to it in the * alternate data stream entries */ @@ -373,7 +373,7 @@ static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni, &stream_name_utf8_len); if (!stream_name_utf8) goto out_free_lte; - new_ads_entry = dentry_add_ads(dentry, stream_name_utf8); + new_ads_entry = inode_add_ads(dentry->inode, stream_name_utf8); FREE(stream_name_utf8); if (!new_ads_entry) goto out_free_lte; @@ -557,7 +557,7 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) printf("Scanning `%s'\n", path); - root = new_dentry(path_basename(path)); + root = new_dentry_with_inode(path_basename(path)); if (!root) return WIMLIB_ERR_NOMEM; *root_p = root; @@ -584,12 +584,12 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, } } - root->creation_time = le64_to_cpu(ni->creation_time); - root->last_write_time = le64_to_cpu(ni->last_data_change_time); - root->last_access_time = le64_to_cpu(ni->last_access_time); - root->attributes = le32_to_cpu(attributes); - root->link_group_id = ni->mft_no; - root->resolved = true; + root->inode->creation_time = le64_to_cpu(ni->creation_time); + root->inode->last_write_time = le64_to_cpu(ni->last_data_change_time); + root->inode->last_access_time = le64_to_cpu(ni->last_access_time); + root->inode->attributes = le32_to_cpu(attributes); + root->inode->ino = ni->mft_no; + root->inode->resolved = true; if (attributes & FILE_ATTR_REPARSE_POINT) { /* Junction point, symbolic link, or other reparse point */ @@ -672,20 +672,20 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, ni, dir_ni, sd, ret); } if (ret > 0) { - root->security_id = sd_set_add_sd(sd_set, sd, ret); - if (root->security_id == -1) { + root->inode->security_id = sd_set_add_sd(sd_set, sd, ret); + if (root->inode->security_id == -1) { ERROR("Out of memory"); return WIMLIB_ERR_NOMEM; } DEBUG("Added security ID = %u for `%s'", - root->security_id, path); + root->inode->security_id, path); ret = 0; } else if (ret < 0) { ERROR_WITH_ERRNO("Failed to get security information from " "`%s'", path); ret = WIMLIB_ERR_NTFS_3G; } else { - root->security_id = -1; + root->inode->security_id = -1; DEBUG("No security ID for `%s'", path); } #endif diff --git a/src/resource.c b/src/resource.c index 7baaa15c..5f97ed02 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1204,7 +1204,7 @@ int read_metadata_resource(WIMStruct *w, struct image_metadata *imd) dentry->prev = dentry; if (ret != 0) goto out_free_dentry_tree; - list_add(&dentry->inode_dentry_list, &dentry->inode->dentry_list); + inode_add_dentry(dentry, dentry->inode); /* Now read the entire directory entry tree into memory. */ DEBUG("Reading dentry tree"); @@ -1226,13 +1226,14 @@ int read_metadata_resource(WIMStruct *w, struct image_metadata *imd) for_dentry_in_tree(dentry, inode_table_insert, &inode_tab); - DEBUG("Fixing inconsistencies in the link groups"); + DEBUG("Fixing inconsistencies in the hard link groups"); ret = fix_inodes(&inode_tab, &inode_list); destroy_inode_table(&inode_tab); if (ret != 0) goto out_free_dentry_tree; DEBUG("Running miscellaneous verifications on the dentry tree"); + for_lookup_table_entry(w->lookup_table, lte_zero_real_refcnt, NULL); ret = for_dentry_in_tree(dentry, verify_dentry, w); if (ret != 0) goto out_free_dentry_tree; @@ -1240,6 +1241,7 @@ int read_metadata_resource(WIMStruct *w, struct image_metadata *imd) DEBUG("Done reading image metadata"); imd->root_dentry = dentry; + imd->inode_list = inode_list; goto out_free_buf; out_free_dentry_tree: free_dentry_tree(dentry, NULL); diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index c7a3f8fe..4e2f1d26 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -239,6 +239,8 @@ struct image_metadata { * resource. */ struct lookup_table_entry *metadata_lte; + struct hlist_head inode_list; + /* True if the filesystem of the image has been modified. If this is * the case, the memory for the filesystem is not freed when switching * to a different WIM image. */ diff --git a/src/write.c b/src/write.c index 49715899..e06ad05e 100644 --- a/src/write.c +++ b/src/write.c @@ -426,7 +426,7 @@ WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path, int image, int flags) if (ret != 0) goto done; - for_lookup_table_entry(w->lookup_table, zero_out_refcnts, NULL); + for_lookup_table_entry(w->lookup_table, lte_zero_out_refcnt, NULL); w->write_flags = flags; diff --git a/tests/test-imagex b/tests/test-imagex index dc67da2a..85c17b4d 100755 --- a/tests/test-imagex +++ b/tests/test-imagex @@ -19,11 +19,10 @@ imagex_info() { } cleanup() { + fusermount -u tmp &> /dev/null || true rm -rf dir* tmp* *.wim *.swm } -trap cleanup exit -fusermount -u tmp || true -rm -rf tmp || true +cleanup # Make test directory mkdir dir @@ -528,3 +527,4 @@ done echo "**********************************************************" echo " Basic imagex tests passed " echo "**********************************************************" +cleanup -- 2.43.0