From d977c5b4f348208e47fd2922f202f3eb60d5d5cb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Aug 2012 18:01:13 -0500 Subject: [PATCH] mount changes (IN PROGRESS) --- src/dentry.c | 181 ++++++++++++++----- src/dentry.h | 41 +++-- src/extract.c | 2 +- src/lookup_table.c | 86 ++++++--- src/lookup_table.h | 21 +-- src/modify.c | 13 +- src/mount.c | 431 ++++++++++++++++++++++----------------------- src/util.c | 10 ++ 8 files changed, 469 insertions(+), 316 deletions(-) diff --git a/src/dentry.c b/src/dentry.c index 68446822..e5af01ba 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -39,6 +39,26 @@ #include #include +/* + * Returns true if @dentry has the UTF-8 file name @name that has length + * @name_len. + */ +static bool dentry_has_name(const struct dentry *dentry, const char *name, + size_t name_len) +{ + if (dentry->file_name_utf8_len != name_len) + return false; + return memcmp(dentry->file_name_utf8, name, name_len) == 0; +} + +static bool ads_entry_has_name(const struct ads_entry *entry, + const char *name, size_t name_len) +{ + if (entry->stream_name_utf8_len != name_len) + return false; + return memcmp(entry->stream_name_utf8, name, name_len) == 0; +} + /* Real length of a dentry, including the alternate data stream entries, which * are not included in the dentry->length field... */ u64 dentry_total_length(const struct dentry *dentry) @@ -76,7 +96,7 @@ void dentry_to_stbuf(const struct dentry *dentry, struct stat *stbuf, stbuf->st_mode = S_IFREG | 0644; if (table) - lte = lookup_resource(table, dentry->hash); + lte = __lookup_resource(table, dentry_hash(dentry)); else lte = NULL; @@ -104,6 +124,55 @@ void dentry_update_all_timestamps(struct dentry *dentry) dentry->last_write_time = now; } +struct ads_entry *dentry_get_ads_entry(struct dentry *dentry, + const char *stream_name) +{ + size_t stream_name_len = strlen(stream_name); + if (!stream_name) + return NULL; + for (u16 i = 0; i < dentry->num_ads; i++) + if (ads_entry_has_name(&dentry->ads_entries[i], + stream_name, stream_name_len)) + return &dentry->ads_entries[i]; + return NULL; +} + +/* Add an alternate stream entry to a dentry and return a pointer to it, or NULL + * on failure. */ +struct ads_entry *dentry_add_ads(struct dentry *dentry, const char *stream_name) +{ + u16 num_ads = dentry->num_ads + 1; + struct ads_entry *ads_entries; + struct ads_entry *new_entry; + if (num_ads == 0xffff) + return NULL; + ads_entries = MALLOC(num_ads * sizeof(struct ads_entry)); + if (!ads_entries) + return NULL; + + new_entry = &ads_entries[num_ads - 1]; + if (change_ads_name(new_entry, stream_name) != 0) { + FREE(ads_entries); + return NULL; + } + + memcpy(ads_entries, dentry->ads_entries, + (num_ads - 1) * sizeof(struct ads_entry)); + FREE(dentry->ads_entries); + dentry->ads_entries = ads_entries; + dentry->num_ads = num_ads; + return memset(new_entry, 0, sizeof(struct ads_entry)); +} + +void dentry_remove_ads(struct dentry *dentry, struct ads_entry *sentry) +{ + destroy_ads_entry(sentry); + memcpy(sentry, sentry + 1, + (dentry->num_ads - (sentry - dentry->ads_entries)) + * sizeof(struct ads_entry)); + dentry->num_ads--; +} + /* * Calls a function on all directory entries in a directory tree. It is called * on a parent before its children. @@ -384,15 +453,10 @@ int print_dentry(struct dentry *dentry, void *lookup_table) puts("\""); printf("Short Name Length = %hu\n", dentry->short_name_len); printf("Full Path (UTF-8) = \"%s\"\n", dentry->full_path_utf8); - if (lookup_table) { - lte = lookup_resource(lookup_table, dentry->hash); - if (lte) - print_lookup_table_entry(lte, NULL); - else - putchar('\n'); - } else { + if (lookup_table && (lte = __lookup_resource(lookup_table, dentry->hash))) + print_lookup_table_entry(lte, NULL); + else putchar('\n'); - } for (u16 i = 0; i < dentry->num_ads; i++) { printf("[Alternate Stream Entry %u]\n", i); printf("Name = \"%s\"\n", dentry->ads_entries[i].stream_name_utf8); @@ -400,12 +464,14 @@ int print_dentry(struct dentry *dentry, void *lookup_table) dentry->ads_entries[i].stream_name_len); printf("Hash = 0x"); print_hash(dentry->ads_entries[i].hash); - putchar('\n'); - lte = lookup_resource(lookup_table, dentry->ads_entries[i].hash); - if (lte) + if (lookup_table && + (lte = __lookup_resource(lookup_table, + dentry->ads_entries[i].hash))) + { print_lookup_table_entry(lte, NULL); - else + } else { putchar('\n'); + } } return 0; } @@ -446,10 +512,8 @@ struct dentry *new_dentry(const char *name) static void dentry_free_ads_entries(struct dentry *dentry) { - for (u16 i = 0; i < dentry->num_ads; i++) { - FREE(dentry->ads_entries[i].stream_name); - FREE(dentry->ads_entries[i].stream_name_utf8); - } + for (u16 i = 0; i < dentry->num_ads; i++) + destroy_ads_entry(&dentry->ads_entries[i]); FREE(dentry->ads_entries); dentry->ads_entries = NULL; dentry->num_ads = 0; @@ -566,36 +630,59 @@ static inline void recalculate_dentry_size(struct dentry *dentry) dentry->length = (dentry->length + 7) & ~7; } -/* Changes the name of a dentry to @new_name. Only changes the file_name and - * file_name_utf8 fields; does not change the short_name, short_name_utf8, or - * full_path_utf8 fields. Also recalculates its length. */ -int change_dentry_name(struct dentry *dentry, const char *new_name) +static int do_name_change(char **file_name_ret, + char **file_name_utf8_ret, + u16 *file_name_len_ret, + u16 *file_name_utf8_len_ret, + const char *new_name) { size_t utf8_len; size_t utf16_len; - - FREE(dentry->file_name); + char *file_name, *file_name_utf8; utf8_len = strlen(new_name); - dentry->file_name = utf8_to_utf16(new_name, utf8_len, &utf16_len); + file_name = utf8_to_utf16(new_name, utf8_len, &utf16_len); - if (!dentry->file_name) + if (!file_name) return WIMLIB_ERR_NOMEM; - FREE(dentry->file_name_utf8); - dentry->file_name_utf8 = MALLOC(utf8_len + 1); - if (!dentry->file_name_utf8) { - FREE(dentry->file_name); - dentry->file_name = NULL; + file_name_utf8 = MALLOC(utf8_len + 1); + if (!file_name_utf8) { + FREE(file_name); return WIMLIB_ERR_NOMEM; } + memcpy(file_name_utf8, new_name, utf8_len + 1); + + FREE(*file_name_ret); + FREE(*file_name_utf8_ret); + *file_name_ret = file_name; + *file_name_utf8_ret = file_name_utf8; + *file_name_len_ret = utf16_len; + *file_name_utf8_len_ret = utf8_len; +} - dentry->file_name_len = utf16_len; - dentry->file_name_utf8_len = utf8_len; - memcpy(dentry->file_name_utf8, new_name, utf8_len + 1); - recalculate_dentry_size(dentry); - return 0; +/* Changes the name of a dentry to @new_name. Only changes the file_name and + * file_name_utf8 fields; does not change the short_name, short_name_utf8, or + * full_path_utf8 fields. Also recalculates its length. */ +int change_dentry_name(struct dentry *dentry, const char *new_name) +{ + int ret; + + ret = do_name_change(&dentry->file_name, &dentry->file_name_utf8, + &dentry->file_name_len, &dentry->file_name_utf8_len, + new_name); + if (ret == 0) + recalculate_dentry_size(dentry); + return ret; +} + +int change_ads_name(struct ads_entry *entry, const char *new_name) +{ + return do_name_change(&entry->stream_name, &entry->stream_name_utf8, + &entry->stream_name_len, + &entry->stream_name_utf8_len, + new_name); } /* Parameters for calculate_dentry_statistics(). */ @@ -611,21 +698,31 @@ static int calculate_dentry_statistics(struct dentry *dentry, void *arg) { struct image_statistics *stats; struct lookup_table_entry *lte; + u16 i; stats = arg; - lte = lookup_resource(stats->lookup_table, dentry->hash); if (dentry_is_directory(dentry) && !dentry_is_root(dentry)) ++*stats->dir_count; else ++*stats->file_count; - if (lte) { - u64 size = lte->resource_entry.original_size; - *stats->total_bytes += size; - if (++lte->out_refcnt == 1) - *stats->hard_link_bytes += size; + lte = __lookup_resource(stats->lookup_table, dentry->hash); + i = 0; + while (1) { + if (lte) { + u64 size = lte->resource_entry.original_size; + *stats->total_bytes += size; + if (++lte->out_refcnt == 1) + *stats->hard_link_bytes += size; + } + if (i == dentry->num_ads) + break; + lte = __lookup_resource(stats->lookup_table, + dentry->ads_entries[i].hash); + i++; } + return 0; } @@ -709,7 +806,7 @@ static int read_ads_entries(const u8 *p, struct dentry *dentry, cur_entry->stream_name_utf8 = utf16_to_utf8(cur_entry->stream_name, cur_entry->stream_name_len, &utf8_len); - cur_entry->stream_name_len_utf8 = utf8_len; + cur_entry->stream_name_utf8_len = utf8_len; if (!cur_entry->stream_name_utf8) { ret = WIMLIB_ERR_NOMEM; diff --git a/src/dentry.h b/src/dentry.h index 2aabf02e..c1d16776 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -59,7 +59,7 @@ struct ads_entry { u16 stream_name_len; /* Length of stream name (UTF-8) */ - u16 stream_name_len_utf8; + u16 stream_name_utf8_len; /* Stream name (UTF-16) */ char *stream_name; @@ -74,6 +74,14 @@ static inline u64 ads_entry_length(const struct ads_entry *entry) return (len + 7) & ~7; } +static inline void destroy_ads_entry(struct ads_entry *entry) +{ + FREE(entry->stream_name); + FREE(entry->stream_name_utf8); + memset(entry, 0, sizeof(entry)); +} + + /* In-memory structure for a directory entry. There is a directory tree for * each image in the WIM. */ struct dentry { @@ -177,6 +185,11 @@ struct dentry { /* Number of references to the dentry tree itself, as in multiple * WIMStructs */ int refcnt; + + /* Next dentry in the hard link set */ + //struct dentry *next_dentry_in_link_set; + /* Next hard link that has a lookup table entry */ + //struct dentry *next_link_set; }; /* Return hash of the "unnamed" (default) data stream. */ @@ -192,6 +205,17 @@ static inline const u8 *dentry_hash(const struct dentry *dentry) return dentry->hash; } + +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 void dentry_remove_ads(struct dentry *dentry, struct ads_entry *entry); + +extern const char *path_stream_name(const char *path); + extern u64 dentry_total_length(const struct dentry *dentry); extern void stbuf_to_dentry(const struct stat *stbuf, struct dentry *dentry); @@ -210,6 +234,7 @@ extern int for_dentry_in_tree_depth(struct dentry *root, extern int calculate_dentry_full_path(struct dentry *dentry, void *ignore); extern void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p); extern int change_dentry_name(struct dentry *dentry, const char *new_name); +extern int change_ads_name(struct ads_entry *entry, const char *new_name); extern void unlink_dentry(struct dentry *dentry); extern void link_dentry(struct dentry *dentry, struct dentry *parent); @@ -252,17 +277,6 @@ extern int dentry_set_symlink_buf(struct dentry *dentry, /* Inline utility functions for WIMDentries */ -/* - * Returns true if @dentry has the UTF-8 file name @name that has length - * @name_len. - */ -static inline bool dentry_has_name(const struct dentry *dentry, const char *name, - size_t name_len) -{ - if (dentry->file_name_utf8_len != name_len) - return false; - return memcmp(dentry->file_name_utf8, name, name_len) == 0; -} static inline bool dentry_is_root(const struct dentry *dentry) { @@ -281,7 +295,8 @@ static inline bool dentry_is_only_child(const struct dentry *dentry) static inline bool dentry_is_directory(const struct dentry *dentry) { - return (dentry->attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + return (dentry->attributes & FILE_ATTRIBUTE_DIRECTORY) + && !(dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT); } /* For our purposes, we consider "real" symlinks and "junction points" to both diff --git a/src/extract.c b/src/extract.c index 49433f08..87628d82 100644 --- a/src/extract.c +++ b/src/extract.c @@ -97,7 +97,7 @@ static int extract_regular_file(WIMStruct *w, int out_fd; const struct resource_entry *res_entry; - lte = lookup_resource(w->lookup_table, dentry_hash(dentry)); + lte = __lookup_resource(w->lookup_table, dentry_hash(dentry)); /* If we already extracted the same file or a hard link copy of it, we * may be able to simply create a link. The exact action is specified diff --git a/src/lookup_table.c b/src/lookup_table.c index 821954eb..c0369a9f 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -118,7 +118,7 @@ void lookup_table_unlink(struct lookup_table *table, /* Decrement the reference count for the dentry having hash value @hash in the * lookup table. The lookup table entry is unlinked and freed if there are no * references to in remaining. */ -void lookup_table_decrement_refcnt(struct lookup_table* table, const u8 hash[]) +bool lookup_table_decrement_refcnt(struct lookup_table* table, const u8 hash[]) { size_t pos = *(size_t*)hash % table->capacity; struct lookup_table_entry *prev = NULL; @@ -129,36 +129,21 @@ void lookup_table_decrement_refcnt(struct lookup_table* table, const u8 hash[]) if (memcmp(hash, entry->hash, WIM_HASH_SIZE) == 0) { wimlib_assert(entry->refcnt != 0); if (--entry->refcnt == 0) { - free_lookup_table_entry(entry); + if (entry->staging_num_times_opened == 0) + free_lookup_table_entry(entry); if (prev) prev->next = next; else table->array[pos] = next; + return true; } } prev = entry; entry = next; } + return false; } -/* - * Looks up an entry in the lookup table. - */ -struct lookup_table_entry *lookup_resource(const struct lookup_table *lookup_table, - const u8 hash[]) -{ - size_t pos; - struct lookup_table_entry *lte; - - pos = *(size_t*)hash % lookup_table->capacity; - lte = lookup_table->array[pos]; - while (lte) { - if (memcmp(hash, lte->hash, WIM_HASH_SIZE) == 0) - return lte; - lte = lte->next; - } - return NULL; -} /* * Calls a function on all the entries in the lookup table. Stop early and @@ -349,8 +334,59 @@ WIMLIBAPI void wimlib_print_lookup_table(WIMStruct *w) print_lookup_table_entry, NULL); } -/*struct lookup_table_entry *lookup_resource(const struct lookup_table *table,*/ - /*const char *path, int lookup_flags)*/ -/*{*/ - /*return lookup_resource(w->lookup_table, dentry->hash);*/ -/*}*/ +/* + * Looks up an entry in the lookup table. + */ +struct lookup_table_entry * +__lookup_resource(const struct lookup_table *lookup_table, const u8 hash[]) +{ + size_t pos; + struct lookup_table_entry *lte; + + pos = *(size_t*)hash % lookup_table->capacity; + lte = lookup_table->array[pos]; + while (lte) { + if (memcmp(hash, lte->hash, WIM_HASH_SIZE) == 0) + return lte; + lte = lte->next; + } + return NULL; +} + +int lookup_resource(WIMStruct *w, const char *path, + int lookup_flags, + struct dentry **dentry_ret, + struct lookup_table_entry **lte_ret, + u8 **hash_ret) +{ + struct dentry *dentry = get_dentry(w, path); + struct lookup_table_entry *lte; + const u8 *hash; + if (!dentry) + return -ENOENT; + if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK) + && dentry_is_directory(dentry)) + return -EISDIR; + if (lookup_flags & LOOKUP_FLAG_ADS_OK) { + const char *stream_name = path_stream_name(path); + if (stream_name) { + for (u16 i = 0; i < dentry->num_ads; i++) { + if (strcmp(stream_name, dentry->ads_entries[i].stream_name) == 0) { + hash = dentry->ads_entries[i].hash; + goto do_lookup; + } + } + return -ENOENT; + } + } + hash = dentry->hash; +do_lookup: + lte = __lookup_resource(w->lookup_table, hash); + if (dentry_ret) + *dentry_ret = dentry; + if (lte_ret) + *lte_ret = lte; + if (hash_ret) + *hash_ret = hash; + return 0; +} diff --git a/src/lookup_table.h b/src/lookup_table.h index a269e512..faf62a02 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -7,7 +7,9 @@ /* Size of each lookup table entry in the WIM file. */ #define WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE 50 -#define LOOKUP_FLAG_ADS_OK +#define LOOKUP_FLAG_ADS_OK 0x00000001 +#define LOOKUP_FLAG_DIRECTORY_OK 0x00000002 +#define LOOKUP_FLAG_FOLLOW_SYMLINKS 0x00000004 /* A lookup table that is used to translate the hash codes of dentries into the @@ -112,6 +114,7 @@ struct lookup_table_entry { bool refcnt_is_incremented; }; struct resource_entry output_resource_entry; + struct dentry *hard_link_sets; }; extern struct lookup_table *new_lookup_table(size_t capacity); @@ -122,7 +125,7 @@ extern void lookup_table_insert(struct lookup_table *table, extern void lookup_table_unlink(struct lookup_table *table, struct lookup_table_entry *lte); -extern void lookup_table_decrement_refcnt(struct lookup_table* table, +extern bool lookup_table_decrement_refcnt(struct lookup_table* table, const u8 hash[]); @@ -132,15 +135,13 @@ extern int for_lookup_table_entry(struct lookup_table *table, int (*visitor)(struct lookup_table_entry *, void *), void *arg); -extern struct lookup_table_entry *lookup_resource(const struct lookup_table *table, - const u8 hash[]); - -static inline struct lookup_table_entry * -wim_lookup_resource(const WIMStruct *w, const struct dentry *dentry) -{ - return lookup_resource(w->lookup_table, dentry->hash); -} +extern struct lookup_table_entry * +__lookup_resource(const struct lookup_table *lookup_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, + u8 **hash_ret); extern int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore); diff --git a/src/modify.c b/src/modify.c index 6b1239da..29b16081 100644 --- a/src/modify.c +++ b/src/modify.c @@ -174,7 +174,7 @@ static int build_dentry_tree(struct dentry *root, const char *root_disk_path, struct lookup_table_entry *lte; struct lookup_table_entry *existing_lte; - existing_lte = lookup_resource(lookup_table, symlink_buf_hash); + existing_lte = __lookup_resource(lookup_table, symlink_buf_hash); if (existing_lte) { existing_lte->refcnt++; } else { @@ -192,17 +192,18 @@ static int build_dentry_tree(struct dentry *root, const char *root_disk_path, lookup_table_insert(lookup_table, lte); } } else { + /* Regular file */ struct lookup_table_entry *lte; - /* For each non-directory, we must check to see if the file is - * in the lookup table already; if it is, we increment its - * refcnt; otherwise, we create a new lookup table entry and - * insert it. */ + /* For each regular file, we must check to see if the file is in + * the lookup table already; if it is, we increment its refcnt; + * otherwise, we create a new lookup table entry and insert it. + * */ ret = sha1sum(root_disk_path, root->hash); if (ret != 0) return ret; - lte = lookup_resource(lookup_table, root->hash); + lte = __lookup_resource(lookup_table, root->hash); if (lte) { lte->refcnt++; } else { diff --git a/src/mount.c b/src/mount.c index 640b27c6..f4f30d11 100644 --- a/src/mount.c +++ b/src/mount.c @@ -66,6 +66,14 @@ static int mount_flags; static const char *mount_dir; +static inline int get_lookup_flags() +{ + if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) + return LOOKUP_FLAG_ADS_OK; + else + return 0; +} + /* * Creates a randomly named staging directory and returns its name into the * static variable staging_dir_name. @@ -317,35 +325,40 @@ static int close_staging_file(struct lookup_table_entry *lte, void *ignore) * file. Updates the SHA1 sum in the dentry and the lookup table entry. If * there is already a lookup table entry with the same checksum, increment its * reference count and destroy the lookup entry with the updated checksum. */ -static int calculate_sha1sum_for_staging_file(struct dentry *dentry, void *lookup_table) +static int calculate_sha1sum_for_staging_file(struct dentry *dentry, + void *_lookup_table) { - struct lookup_table *table; - struct lookup_table_entry *lte; - struct lookup_table_entry *existing; - int ret; - - table = lookup_table; - lte = lookup_resource(table, dentry->hash); - - if (lte && lte->staging_file_name) { - - DEBUG("Calculating SHA1 hash for file `%s'", - dentry->file_name_utf8); - ret = sha1sum(lte->staging_file_name, dentry->hash); - if (ret != 0) - return ret; - - lookup_table_unlink(table, lte); - memcpy(lte->hash, dentry->hash, WIM_HASH_SIZE); - existing = lookup_resource(table, dentry->hash); - if (existing) { - DEBUG("Merging duplicate lookup table entries for file " - "`%s'", dentry->file_name_utf8); - free_lookup_table_entry(lte); - existing->refcnt++; - } else { - lookup_table_insert(table, lte); + struct lookup_table *lookup_table = _lookup_table; + u8 *hash = dentry->hash; + u16 i = 0; + while (1) { + struct lookup_table_entry *lte = __lookup_resource(lookup_table, hash); + if (lte && lte->staging_file_name) { + struct lookup_table_entry *existing; + int ret; + + DEBUG("Calculating SHA1 hash for file `%s'", + dentry->file_name_utf8); + ret = sha1sum(lte->staging_file_name, lte->hash); + if (ret != 0) + return ret; + + lookup_table_unlink(lookup_table, lte); + memcpy(hash, lte->hash, WIM_HASH_SIZE); + existing = __lookup_resource(lookup_table, hash); + if (existing) { + DEBUG("Merging duplicate lookup table entries for file " + "`%s'", dentry->file_name_utf8); + free_lookup_table_entry(lte); + existing->refcnt++; + } else { + lookup_table_insert(lookup_table, lte); + } } + if (i == dentry->num_ads) + break; + hash = dentry->ads_entries[i].hash; + i++; } return 0; } @@ -509,7 +522,7 @@ static int wimfs_mkdir(const char *path, mode_t mode) * @return: The file descriptor for the new file. Returns -1 and sets errno on * error, for any reason possible from the creat() function. */ -static int create_staging_file(char **name_ret) +static int create_staging_file(char **name_ret, int open_flags) { size_t name_len; char *name; @@ -524,29 +537,28 @@ static int create_staging_file(char **name_ret) return -1; } - memcpy(name, staging_dir_name, staging_dir_name_len); - name[staging_dir_name_len] = '/'; - randomize_char_array_with_alnum(name + staging_dir_name_len + 1, - WIM_HASH_SIZE); - name[name_len] = '\0'; + do { + + memcpy(name, staging_dir_name, staging_dir_name_len); + name[staging_dir_name_len] = '/'; + randomize_char_array_with_alnum(name + staging_dir_name_len + 1, + WIM_HASH_SIZE); + name[name_len] = '\0'; /* Just in case, verify that the randomly generated name doesn't name an * existing file, and try again if so */ - if (stat(name, &stbuf) == 0) { - /* stat succeeded-- the file must exist. Try another name. */ - FREE(name); - return create_staging_file(name_ret); - } else { - if (errno != ENOENT) - /* other error! */ - return -1; - /* doesn't exist--- ok */ - } + } while (stat(name, &stbuf) == 0); - DEBUG("Creating staging file '%s'", name); + if (errno != ENOENT) + /* other error! */ + return -1; + + /* doesn't exist--- ok */ + + DEBUG("Creating staging file `%s'", name); - fd = creat(name, 0600); + fd = open(name, mode | O_CREAT | O_TRUNC, 0600); if (fd == -1) { errno_save = errno; FREE(name); @@ -557,111 +569,106 @@ static int create_staging_file(char **name_ret) return fd; } -/* Creates a regular file. This is done in the staging directory. */ +/* Creates a regular file. */ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev) { - struct dentry *parent, *dentry; - const char *basename; - struct lookup_table_entry *lte; - char *tmpfile_name; - int fd; - int err; - - /* Make sure that the parent of @path exists and is a directory, and - * that the dentry named by @path does not already exist. */ - parent = get_parent_dentry(w, path); - if (!parent) - return -ENOENT; - if (!dentry_is_directory(parent)) - return -ENOTDIR; - basename = path_basename(path); - if (get_dentry_child_with_name(parent, path)) - return -EEXIST; - - dentry = new_dentry(basename); - - /* XXX fill in a temporary random hash value- really should check for - * duplicates */ - randomize_byte_array(dentry->hash, WIM_HASH_SIZE); - - /* Create a lookup table entry having the same hash value */ - lte = new_lookup_table_entry(); - memcpy(lte->hash, dentry->hash, WIM_HASH_SIZE); - - fd = create_staging_file(&tmpfile_name); - - if (fd == -1) - goto mknod_error; - - if (close(fd) != 0) - goto mknod_error; - - lte->staging_file_name = tmpfile_name; + const char *stream_name; + if ((mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) + && (stream_name = path_stream_name(path))) { + struct ads_entry *new_entry; + struct dentry *dentry; + + dentry = get_dentry(w, path); + if (!dentry || !dentry_is_regular_file(dentry)) + return -ENOENT; + if (dentry_get_ads_entry(dentry, stream_name)) + return -EEXIST; + new_entry = dentry_add_ads(dentry, stream_name); + if (!new_entry) + return -ENOENT; + } else { + struct dentry *dentry, *parent; + const char *basename; - /* Insert the lookup table entry, and link the new dentry with its - * parent. */ - lookup_table_insert(w->lookup_table, lte); - link_dentry(dentry, parent); + /* Make sure that the parent of @path exists and is a directory, and + * that the dentry named by @path does not already exist. */ + parent = get_parent_dentry(w, path); + if (!parent) + return -ENOENT; + if (!dentry_is_directory(parent)) + return -ENOTDIR; + basename = path_basename(path); + if (get_dentry_child_with_name(parent, path)) + return -EEXIST; + + dentry = new_dentry(basename); + link_dentry(dentry, parent); + } return 0; -mknod_error: - err = errno; - free_lookup_table_entry(lte); - return -err; } + /* Open a file. */ static int wimfs_open(const char *path, struct fuse_file_info *fi) { struct dentry *dentry; struct lookup_table_entry *lte; - - dentry = get_dentry(w, path); + u8 *dentry_hash; + int ret; - if (!dentry) - return -ENOENT; - if (dentry_is_directory(dentry)) - return -EISDIR; - lte = wim_lookup_resource(w, dentry); + ret = lookup_resource(w, path, get_lookup_flags(), &dentry, <e, + &dentry_hash); + if (ret != 0) + return ret; if (lte) { /* If this file is in the staging directory and the file is not * currently open, open it. */ - if (lte->staging_file_name && lte->staging_num_times_opened == 0) { - lte->staging_fd = open(lte->staging_file_name, O_RDWR); - if (lte->staging_fd == -1) - return -errno; - lte->staging_offset = 0; + if (lte->staging_file_name) { + if (lte->staging_num_times_opened == 0) { + lte->staging_fd = open(lte->staging_file_name, O_RDWR); + if (lte->staging_fd == -1) + return -errno; + lte->staging_offset = 0; + } + } else { + /* File in the WIM. We must extract it to the staging directory + * before it can be written to. */ + ret = extract_resource_to_staging_dir(dentry_hash, lte, + lte->resource_entry.original_size); + if (ret != 0) + return ret; } } else { - /* no lookup table entry, so the file must be empty. Create a - * lookup table entry for the file, unless it's a read-only - * filesystem. */ + /* Empty file with no lookup-table entry. This is fine if it's + * a read-only filesystem. Otherwise we need to move the file + * to the staging directory with a new lookup table entry, even + * if we aren't opening it for writing at the moment, so that we + * will have a lookup table entry for the file in case it's + * changed. */ + if (!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) { + fi->fd = 0; + return 0; + } char *tmpfile_name; int fd; - if (!staging_dir_name) /* Read-only filesystem */ - return 0; + fd = create_staging_file(&tmpfile_name, O_RDWR); + if (fd == -1) + return -errno; lte = new_lookup_table_entry(); if (!lte) return -ENOMEM; - fd = create_staging_file(&tmpfile_name); - - if (fd == -1) { - int err = errno; - free(lte); - return -err; - } - lte->resource_entry.original_size = 0; randomize_byte_array(lte->hash, WIM_HASH_SIZE); - memcpy(dentry->hash, lte->hash, WIM_HASH_SIZE); + memcpy(dentry_hash, lte->hash, WIM_HASH_SIZE); lte->staging_file_name = tmpfile_name; lte->staging_fd = fd; - lte->staging_offset = 0; lookup_table_insert(w->lookup_table, lte); } lte->staging_num_times_opened++; + fi->fd = (uint64_t)lte; return 0; } @@ -673,6 +680,7 @@ static int wimfs_opendir(const char *path, struct fuse_file_info *fi) dentry = get_dentry(w, path); if (!dentry || !dentry_is_directory(dentry)) return -ENOTDIR; + fi->fd = (uint64_t)dentry; return 0; } @@ -683,19 +691,9 @@ static int wimfs_opendir(const char *path, struct fuse_file_info *fi) static int wimfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - struct dentry *dentry; struct lookup_table_entry *lte; - - dentry = get_dentry(w, path); - - if (!dentry) - return -EEXIST; - - if (!dentry_is_regular_file(dentry)) - return -EISDIR; - - lte = wim_lookup_resource(w, dentry); + lte = (struct lookup_table_entry*)fi->fh; if (!lte) return 0; @@ -752,18 +750,7 @@ 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; - struct dentry *child; - struct stat st; - - parent = get_dentry(w, path); - - if (!parent) - return -EEXIST; - - if (!dentry_is_directory(parent)) - return -ENOTDIR; - + struct dentry *parent = (struct dentry*) fi->fh; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); @@ -799,23 +786,18 @@ static int wimfs_readlink(const char *path, char *buf, size_t buf_len) /* Close a file. */ static int wimfs_release(const char *path, struct fuse_file_info *fi) { - struct dentry *dentry; struct lookup_table_entry *lte; int ret; - - dentry = get_dentry(w, path); - if (!dentry) - return -EEXIST; - lte = wim_lookup_resource(w, dentry); - if (!lte) - return 0; + lte = (struct lookup_table_entry*)fi->fh; - if (lte->staging_num_times_opened == 0) + if (!lte || lte->staging_num_times_opened == 0) return -EBADF; if (--lte->staging_num_times_opened == 0 && lte->staging_file_name) { ret = close(lte->staging_fd); + if (lte->refcnt == 0) + free_lookup_table_entry(lte); if (ret != 0) return -errno; } @@ -872,10 +854,10 @@ static int wimfs_rmdir(const char *path) dentry = get_dentry(w, path); if (!dentry) - return -EEXIST; + return -ENOENT; if (!dentry_is_empty_directory(dentry)) - return -EEXIST; + return -ENOTEMPTY; unlink_dentry(dentry); free_dentry(dentry); @@ -892,7 +874,7 @@ static int wimfs_rmdir(const char *path) * * Returns the negative error code on failure. */ -static int extract_resource_to_staging_dir(struct dentry *dentry, +static int extract_resource_to_staging_dir(u8 *dentry_hash, struct lookup_table_entry *lte, u64 size) { @@ -925,8 +907,8 @@ static int extract_resource_to_staging_dir(struct dentry *dentry, new_lte = new_lookup_table_entry(); if (!new_lte) return -ENOMEM; - randomize_byte_array(dentry->hash, WIM_HASH_SIZE); - memcpy(new_lte->hash, dentry->hash, WIM_HASH_SIZE); + randomize_byte_array(dentry_hash, WIM_HASH_SIZE); + memcpy(new_lte->hash, dentry_hash, WIM_HASH_SIZE); new_lte->resource_entry.flags = 0; new_lte->staging_num_times_opened = lte->staging_num_times_opened; @@ -952,14 +934,17 @@ static int wimfs_truncate(const char *path, off_t size) struct dentry *dentry; struct lookup_table_entry *lte; int ret; + u8 *dentry_hash; + + ret = lookup_resource(w, path, get_lookup_flags(), &dentry, + <e, &dentry_hash); - dentry = get_dentry(w, path); - if (!dentry) - return -EEXIST; - lte = wim_lookup_resource(w, dentry); + if (ret != 0) + return ret; if (!lte) /* Already a zero-length file */ return 0; + if (lte->staging_file_name) { /* File on disk. Call POSIX API */ if (lte->staging_num_times_opened != 0) @@ -970,12 +955,12 @@ static int wimfs_truncate(const char *path, off_t size) return -errno; dentry_update_all_timestamps(dentry); lte->resource_entry.original_size = size; - return 0; } else { /* File in WIM. Extract it to the staging directory, but only * the first @size bytes of it. */ - return extract_resource_to_staging_dir(dentry, lte, size); + ret = extract_resource_to_staging_dir(dentry_hash, lte, size); } + return ret; } /* Remove a regular file */ @@ -983,27 +968,52 @@ static int wimfs_unlink(const char *path) { struct dentry *dentry; struct lookup_table_entry *lte; + int ret; + u8 *dentry_hash; - dentry = get_dentry(w, path); - if (!dentry) - return -EEXIST; + ret = lookup_resource(w, path, get_lookup_flags(), &dentry, + <e, &dentry_hash); - if (!dentry_is_regular_file(dentry)) - return -EEXIST; + if (ret != 0) + return ret; - lte = wim_lookup_resource(w, dentry); - if (lte) { - if (lte->staging_file_name) - if (unlink(lte->staging_file_name) != 0) - return -errno; - lookup_table_decrement_refcnt(w->lookup_table, dentry->hash); - } + if (lte && lte->staging_file_name) + if (unlink(lte->staging_file_name) != 0) + return -errno; - unlink_dentry(dentry); - free_dentry(dentry); + if (dentry_hash == dentry->hash) { + /* We are removing the full dentry including all alternate data + * streams. */ + const u8 *hash = dentry->hash; + u16 i = 0; + while (1) { + lookup_table_decrement_refcnt(w->lookup_table, hash); + if (i == dentry->num_ads) + break; + hash = dentry->ads_entries[i].hash; + i++; + } + + unlink_dentry(dentry); + free_dentry(dentry); + } else { + /* We are removing an alternate data stream. */ + struct ads_entry *cur_entry = dentry->ads_entries; + while (cur_entry->hash != dentry_hash) + cur_entry++; + lookup_table_decrement_refcnt(w->lookup_table, cur_entry->hash); + + dentry_remove_ads(dentry, cur_entry); + } + /* 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. */ return 0; } +/* Change the timestamp on a file dentry. + * + * There is no distinction between a file and its alternate data streams here. */ static int wimfs_utimens(const char *path, const struct timespec tv[2]) { struct dentry *dentry = get_dentry(w, path); @@ -1018,61 +1028,44 @@ static int wimfs_utimens(const char *path, const struct timespec tv[2]) return 0; } -/* Writes to a file in the WIM filesystem. */ +/* Writes to a file in the WIM filesystem. + * It may be an alternate data stream, but here we don't even notice because we + * just get a lookup table entry. */ static int wimfs_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi) { - struct dentry *dentry; + /* Grab our lookup table entry from the FUSE file info structure. */ struct lookup_table_entry *lte; - ssize_t ret; - - dentry = get_dentry(w, path); - if (!dentry) - return -EEXIST; - lte = wim_lookup_resource(w, dentry); - - if (!lte) /* this should not happen */ - return -EEXIST; + lte = (struct lookup_table_entry*)fi->fh; + int ret; - if (lte->staging_num_times_opened == 0) + if (!lte || !lte->staging_file_name) return -EBADF; - if (lte->staging_file_name) { - /* File in staging directory. We can write to it directly. */ + /* Seek to correct position in file if needed. */ + if (lte->staging_offset != offset) { + if (lseek(lte->staging_fd, offset, SEEK_SET) == -1) + return -errno; + lte->staging_offset = offset; + } - /* Seek to correct position in file if needed. */ - if (lte->staging_offset != offset) { - if (lseek(lte->staging_fd, offset, SEEK_SET) == -1) - return -errno; - lte->staging_offset = offset; - } + /* Write the data. */ + ret = write(lte->staging_fd, buf, size); + if (ret == -1) + return -errno; - /* Write the data. */ - ret = write(lte->staging_fd, buf, size); - if (ret == -1) - return -errno; + /* Adjust the stored offset of staging_fd. */ + lte->staging_offset = offset + ret; - /* Adjust the stored offset of staging_fd. */ - lte->staging_offset = offset + ret; + /* Increase file size if needed. */ + if (lte->resource_entry.original_size < lte->staging_offset) + lte->resource_entry.original_size = lte->staging_offset; - /* Increase file size if needed. */ - if (lte->resource_entry.original_size < lte->staging_offset) - lte->resource_entry.original_size = lte->staging_offset; + /* The file has been modified, so all its timestamps must be + * updated. */ + dentry_update_all_timestamps(dentry); - /* The file has been modified, so all its timestamps must be - * updated. */ - dentry_update_all_timestamps(dentry); - return ret; - } else { - /* File in the WIM. We must extract it to the staging directory - * before it can be written to. */ - ret = extract_resource_to_staging_dir(dentry, lte, - lte->resource_entry.original_size); - if (ret != 0) - return ret; - else - return wimfs_write(path, buf, size, offset, fi); - } + return ret; } diff --git a/src/util.c b/src/util.c index 725b1272..e14ddd40 100644 --- a/src/util.c +++ b/src/util.c @@ -428,6 +428,16 @@ const char *path_basename(const char *path) return p + 1; } +const char *path_stream_name(const char *path) +{ + const char *base = path_basename(path); + const char *stream_name = strchr(base, ':'); + if (!stream_name) + return NULL; + else + return stream_name + 1; +} + /* * Splits a file path into the part before the first '/', or the entire name if * there is no '/', and the part after the first sequence of '/' characters. -- 2.43.0