X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Finode.c;h=690eb22f3abf657ef51b354e030e85fe00eec8ac;hp=c459f3cb15ddc78db68b576d7603525ac64cb656;hb=ebdba490384924ef422b1df675ba11d5030faf09;hpb=3071e89c11d1be71cf45b694432e5908e0c4ded9 diff --git a/src/inode.c b/src/inode.c index c459f3cb..690eb22f 100644 --- a/src/inode.c +++ b/src/inode.c @@ -8,7 +8,7 @@ */ /* - * Copyright (C) 2012, 2013, 2014 Eric Biggers + * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -28,449 +28,469 @@ # include "config.h" #endif +#include + #include "wimlib/assert.h" -#include "wimlib/dentry.h" /* Only for dentry_full_path(). Otherwise the code - in this file doesn't care about file names/paths. - */ +#include "wimlib/blob_table.h" +#include "wimlib/dentry.h" #include "wimlib/encoding.h" -#include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/inode.h" -#include "wimlib/inode_table.h" -#include "wimlib/lookup_table.h" -#include "wimlib/security.h" #include "wimlib/timestamp.h" -#include +/* + * The 'stream_name' field of unnamed streams always points to this array, which + * is an empty UTF-16 string. + */ +const utf16lechar NO_STREAM_NAME[1]; -/* Allocate a new inode. Set the timestamps to the current time. */ +/* Allocate a new inode and associate the specified dentry with it. */ struct wim_inode * -new_inode(void) +new_inode(struct wim_dentry *dentry, bool set_timestamps) { - struct wim_inode *inode = new_timeless_inode(); - if (inode) { - u64 now = get_wim_timestamp(); + struct wim_inode *inode; + + inode = CALLOC(1, sizeof(struct wim_inode)); + if (!inode) + return NULL; + + inode->i_security_id = -1; + /*inode->i_nlink = 0;*/ + inode->i_not_rpfixed = 1; + INIT_LIST_HEAD(&inode->i_dentry); + inode->i_streams = inode->i_embedded_streams; + if (set_timestamps) { + u64 now = now_as_wim_timestamp(); inode->i_creation_time = now; inode->i_last_access_time = now; inode->i_last_write_time = now; } + d_associate(dentry, inode); return inode; } -/* Allocate a new inode. Leave the timestamps zeroed out. */ -struct wim_inode * -new_timeless_inode(void) +static inline void +destroy_stream(struct wim_inode_stream *strm) { - struct wim_inode *inode = CALLOC(1, sizeof(struct wim_inode)); - if (inode) { - inode->i_security_id = -1; - inode->i_nlink = 1; - inode->i_next_stream_id = 1; - inode->i_not_rpfixed = 1; - inode->i_canonical_streams = 1; - INIT_LIST_HEAD(&inode->i_list); - INIT_LIST_HEAD(&inode->i_dentry); - } - return inode; + if (strm->stream_name != NO_STREAM_NAME) + FREE(strm->stream_name); } -/* Decrement link count on an inode. */ -void -put_inode(struct wim_inode *inode) +static void +free_inode(struct wim_inode *inode) { - wimlib_assert(inode->i_nlink != 0); - if (--inode->i_nlink == 0) { - /* If FUSE mounts are enabled, we must keep a unlinked inode - * around until all file descriptors to it have been closed. */ - #ifdef WITH_FUSE - if (inode->i_num_opened_fds == 0) - #endif - free_inode(inode); - } + for (unsigned i = 0; i < inode->i_num_streams; i++) + destroy_stream(&inode->i_streams[i]); + if (inode->i_streams != inode->i_embedded_streams) + FREE(inode->i_streams); + if (inode->i_extra) + FREE(inode->i_extra); + if (!hlist_unhashed(&inode->i_hlist)) + hlist_del(&inode->i_hlist); + FREE(inode); } -/* Free memory allocated within an alternate data stream entry. */ -static void -destroy_ads_entry(struct wim_ads_entry *ads_entry) +static inline void +free_inode_if_unneeded(struct wim_inode *inode) { - FREE(ads_entry->stream_name); + if (inode->i_nlink) + return; +#ifdef WITH_FUSE + if (inode->i_num_opened_fds) + return; +#endif + free_inode(inode); } -/* Free an inode. Only use this if there can't be other links to the inode or - * if it doesn't matter if there are. */ +/* Associate a dentry with the specified inode. */ void -free_inode(struct wim_inode *inode) +d_associate(struct wim_dentry *dentry, struct wim_inode *inode) +{ + wimlib_assert(!dentry->d_inode); + + list_add_tail(&dentry->d_alias, &inode->i_dentry); + dentry->d_inode = inode; + inode->i_nlink++; +} + +/* Disassociate a dentry from its inode, if any. Following this, free the inode + * if it is no longer in use. */ +void +d_disassociate(struct wim_dentry *dentry) { + struct wim_inode *inode = dentry->d_inode; + if (unlikely(!inode)) return; - if (unlikely(inode->i_ads_entries)) { - for (u16 i = 0; i < inode->i_num_ads; i++) - destroy_ads_entry(&inode->i_ads_entries[i]); - FREE(inode->i_ads_entries); - } - if (unlikely(inode->i_extra)) - FREE(inode->i_extra); - /* HACK: This may instead delete the inode from i_list, but hlist_del() - * behaves the same as list_del(). */ - if (!hlist_unhashed(&inode->i_hlist)) - hlist_del(&inode->i_hlist); - FREE(inode); + wimlib_assert(inode->i_nlink > 0); + + list_del(&dentry->d_alias); + dentry->d_inode = NULL; + inode->i_nlink--; + + free_inode_if_unneeded(inode); } -/* Return %true iff the alternate data stream entry @entry has the UTF-16LE - * stream name @name that has length @name_nbytes bytes. */ -static inline bool -ads_entry_has_name(const struct wim_ads_entry *entry, - const utf16lechar *name, size_t name_nbytes, - bool ignore_case) +#ifdef WITH_FUSE +void +inode_dec_num_opened_fds(struct wim_inode *inode) { - return 0 == cmp_utf16le_strings(name, - name_nbytes / 2, - entry->stream_name, - entry->stream_name_nbytes / 2, - ignore_case); + wimlib_assert(inode->i_num_opened_fds > 0); + + if (--inode->i_num_opened_fds == 0) { + /* The last file descriptor to this inode was closed. */ + FREE(inode->i_fds); + inode->i_fds = NULL; + inode->i_num_allocated_fds = 0; + + free_inode_if_unneeded(inode); + } } +#endif /* - * Returns the alternate data stream entry belonging to @inode that has the - * stream name @stream_name, or NULL if the inode has no alternate data stream - * with that name. + * Retrieve a stream of an inode. * - * If @p stream_name is the empty string, NULL is returned --- that is, this - * function will not return "unnamed" alternate data stream entries. + * @inode + * The inode from which the stream is desired + * @stream_type + * The type of the stream desired + * @stream_name + * The name of the stream desired as a null-terminated UTF-16LE string, or + * NO_STREAM_NAME if an unnamed stream is desired * - * If NULL is returned, errno is set. + * Returns a pointer to the stream if found, otherwise NULL. */ -struct wim_ads_entry * -inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name) -{ - int ret; - const utf16lechar *stream_name_utf16le; - size_t stream_name_utf16le_nbytes; - u16 i; - struct wim_ads_entry *result; - - if (inode->i_num_ads == 0) { - errno = ENOENT; - return NULL; +struct wim_inode_stream * +inode_get_stream(const struct wim_inode *inode, int stream_type, + const utf16lechar *stream_name) +{ + if (stream_name == NO_STREAM_NAME) /* Optimization */ + return inode_get_unnamed_stream(inode, stream_type); + + for (unsigned i = 0; i < inode->i_num_streams; i++) { + struct wim_inode_stream *strm = &inode->i_streams[i]; + if (strm->stream_type == stream_type && + !cmp_utf16le_strings_z(strm->stream_name, stream_name, + default_ignore_case)) + { + return strm; + } } + return NULL; +} - if (stream_name[0] == T('\0')) { - errno = ENOENT; - return NULL; +/* + * This is equivalent to inode_get_stream(inode, stream_type, NO_STREAM_NAME), + * but this optimizes for the unnamed case by not doing full string comparisons. + */ +struct wim_inode_stream * +inode_get_unnamed_stream(const struct wim_inode *inode, int stream_type) +{ + for (unsigned i = 0; i < inode->i_num_streams; i++) { + struct wim_inode_stream *strm = &inode->i_streams[i]; + if (strm->stream_type == stream_type && + strm->stream_name == NO_STREAM_NAME) + { + return strm; + } } + return NULL; +} - ret = tstr_get_utf16le_and_len(stream_name, &stream_name_utf16le, - &stream_name_utf16le_nbytes); - if (ret) - return NULL; - i = 0; - result = NULL; - do { - if (ads_entry_has_name(&inode->i_ads_entries[i], - stream_name_utf16le, - stream_name_utf16le_nbytes, - default_ignore_case)) - { - result = &inode->i_ads_entries[i]; - break; - } - } while (++i != inode->i_num_ads); +static void +inode_set_stream_blob(struct wim_inode *inode, struct wim_inode_stream *strm, + struct blob_descriptor *new_blob) +{ + strm->_stream_blob = new_blob; + strm->stream_resolved = 1; + if (new_blob) + new_blob->refcnt += inode->i_nlink; +} - tstr_put_utf16le(stream_name_utf16le); +static void +inode_unset_stream_blob(struct wim_inode *inode, struct wim_inode_stream *strm, + struct blob_table *blob_table) +{ + struct blob_descriptor *old_blob; - if (!result) - errno = ENOENT; - return result; + old_blob = stream_blob(strm, blob_table); + if (old_blob) + blob_subtract_refcnt(old_blob, blob_table, inode->i_nlink); + strm->_stream_blob = NULL; + strm->stream_resolved = 1; } -static struct wim_ads_entry * -do_inode_add_ads(struct wim_inode *inode, - utf16lechar *stream_name, size_t stream_name_nbytes) +/* + * Replace the blob associated with the specified stream. + * + * @inode + * The inode containing @strm + * @strm + * The stream whose data needs to be replaced + * @new_blob + * The new blob descriptor to assign + * @blob_table + * Pointer to the blob table in which data blobs are being indexed + */ +void +inode_replace_stream_blob(struct wim_inode *inode, + struct wim_inode_stream *strm, + struct blob_descriptor *new_blob, + struct blob_table *blob_table) { - u16 num_ads; - struct wim_ads_entry *ads_entries; - struct wim_ads_entry *new_entry; + inode_unset_stream_blob(inode, strm, blob_table); + inode_set_stream_blob(inode, strm, new_blob); +} - if (inode->i_num_ads >= 0xfffe) { - ERROR("File \"%"TS"\" has too many alternate data streams!", +/* + * Add a new stream to the specified inode. + * + * @inode + * The inode to which to add the stream + * @stream_type + * The type of the stream being added + * @stream_name + * The name of the stream being added as a null-terminated UTF-16LE string, + * or NO_STREAM_NAME if the stream is unnamed + * @blob + * The blob that the new stream will initially reference, or NULL + * + * Returns a pointer to the new stream, or NULL with errno set if it could not + * be added. + */ +struct wim_inode_stream * +inode_add_stream(struct wim_inode *inode, int stream_type, + const utf16lechar *stream_name, struct blob_descriptor *blob) +{ + if (inode->i_num_streams >= 0xFFFF) { + ERROR("Inode has too many streams! Path=\"%"TS"\"", inode_first_full_path(inode)); errno = EFBIG; return NULL; } - num_ads = inode->i_num_ads + 1; - ads_entries = REALLOC(inode->i_ads_entries, - num_ads * sizeof(inode->i_ads_entries[0])); - if (!ads_entries) - return NULL; - inode->i_ads_entries = ads_entries; + struct wim_inode_stream *streams; + struct wim_inode_stream *new_strm; - new_entry = &inode->i_ads_entries[num_ads - 1]; + if (inode->i_streams == inode->i_embedded_streams) { + if (inode->i_num_streams < ARRAY_LEN(inode->i_embedded_streams)) { + streams = inode->i_embedded_streams; + } else { + streams = MALLOC((inode->i_num_streams + 1) * + sizeof(inode->i_streams[0])); + if (!streams) + return NULL; + memcpy(streams, inode->i_streams, + (inode->i_num_streams * + sizeof(inode->i_streams[0]))); + inode->i_streams = streams; + } + } else { + streams = REALLOC(inode->i_streams, + (inode->i_num_streams + 1) * + sizeof(inode->i_streams[0])); + if (!streams) + return NULL; + inode->i_streams = streams; + } + new_strm = &streams[inode->i_num_streams]; - memset(new_entry, 0, sizeof(struct wim_ads_entry)); - new_entry->stream_name = stream_name; - new_entry->stream_name_nbytes = stream_name_nbytes; - new_entry->stream_id = inode->i_next_stream_id++; - inode->i_num_ads = num_ads; - return new_entry; -} + memset(new_strm, 0, sizeof(*new_strm)); -/* - * Add an alternate data stream entry to a WIM inode (UTF-16LE version). On - * success, returns a pointer to the new entry. Note that this pointer might - * become invalid if another ADS entry is added to the inode. On failure, - * returns NULL and sets errno. - */ -struct wim_ads_entry * -inode_add_ads_utf16le(struct wim_inode *inode, - const utf16lechar *stream_name, size_t stream_name_nbytes) -{ - utf16lechar *dup = NULL; - struct wim_ads_entry *result; - - if (stream_name_nbytes) { - dup = utf16le_dupz(stream_name, stream_name_nbytes); - if (!dup) + new_strm->stream_type = stream_type; + if (!*stream_name) { + /* Unnamed stream */ + new_strm->stream_name = (utf16lechar *)NO_STREAM_NAME; + } else { + /* Named stream */ + new_strm->stream_name = utf16le_dup(stream_name); + if (!new_strm->stream_name) return NULL; } - result = do_inode_add_ads(inode, dup, stream_name_nbytes); - if (!result) - FREE(dup); - return result; -} + new_strm->stream_id = inode->i_next_stream_id++; -/* - * Add an alternate data stream entry to a WIM inode (tchar version). On - * success, returns a pointer to the new entry. Note that this pointer might - * become invalid if another ADS entry is added to the inode. On failure, - * returns NULL and sets errno. - */ -struct wim_ads_entry * -inode_add_ads(struct wim_inode *inode, const tchar *stream_name) -{ - utf16lechar *stream_name_utf16le = NULL; - size_t stream_name_utf16le_nbytes = 0; - struct wim_ads_entry *result; - - if (stream_name && *stream_name) - if (tstr_to_utf16le(stream_name, - tstrlen(stream_name) * sizeof(tchar), - &stream_name_utf16le, - &stream_name_utf16le_nbytes)) - return NULL; + inode_set_stream_blob(inode, new_strm, blob); - result = do_inode_add_ads(inode, stream_name_utf16le, - stream_name_utf16le_nbytes); - if (!result) - FREE(stream_name_utf16le); - return result; + inode->i_num_streams++; + + return new_strm; } /* - * Add an data alternate stream entry to a WIM inode, where the contents of the - * new stream are specified in a data buffer. The inode must be resolved. + * Replace the data of the specified stream. + * + * @inode + * The inode containing @strm + * @strm + * The stream whose data needs to be replaced + * @data + * The buffer of data to assign to the stream + * @size + * Size of the @data buffer, in bytes + * @blob_table + * Pointer to the blob table in which data blobs are being indexed * - * On success, returns a pointer to the new alternate data stream entry. Note - * that this pointer might become invalid if another ADS entry is added to the - * inode. On failure, returns NULL and sets errno. + * Returns true if successful; false with errno set if unsuccessful. */ -struct wim_ads_entry * -inode_add_ads_with_data(struct wim_inode *inode, const tchar *name, - const void *value, size_t size, - struct wim_lookup_table *lookup_table) +bool +inode_replace_stream_data(struct wim_inode *inode, + struct wim_inode_stream *strm, + const void *data, size_t size, + struct blob_table *blob_table) { - struct wim_ads_entry *new_entry; - - wimlib_assert(inode->i_resolved); - - new_entry = inode_add_ads(inode, name); - if (!new_entry) - return NULL; + struct blob_descriptor *new_blob = NULL; - new_entry->lte = new_stream_from_data_buffer(value, size, lookup_table); - if (!new_entry->lte) { - inode_remove_ads(inode, new_entry, NULL); - return NULL; + if (size) { + new_blob = new_blob_from_data_buffer(data, size, blob_table); + if (!new_blob) + return false; } - return new_entry; + + inode_replace_stream_blob(inode, strm, new_blob, blob_table); + return true; } /* - * Does the inode have any named data streams? + * Add a new stream to the specified inode and assign it the specified data. + * + * @inode + * The inode to which to add the stream + * @stream_type + * The type of the stream being added + * @stream_name + * The name of the stream being added as a null-terminated UTF-16LE string, + * or NO_STREAM_NAME if the stream is unnamed + * @data + * The buffer of data to assign to the new stream + * @size + * Size of the @data buffer, in bytes + * @blob_table + * Pointer to the blob table in which data blobs are being indexed + * + * Returns true if successful; false with errno set if unsuccessful. */ bool -inode_has_named_stream(const struct wim_inode *inode) -{ - for (u16 i = 0; i < inode->i_num_ads; i++) - if (ads_entry_is_named_stream(&inode->i_ads_entries[i])) - return true; - return false; -} +inode_add_stream_with_data(struct wim_inode *inode, + int stream_type, const utf16lechar *stream_name, + const void *data, size_t size, + struct blob_table *blob_table) +{ + struct wim_inode_stream *strm; + struct blob_descriptor *blob = NULL; + + strm = inode_add_stream(inode, stream_type, stream_name, NULL); + if (!strm) + return false; + + if (size) { + blob = new_blob_from_data_buffer(data, size, blob_table); + if (unlikely(!blob)) { + inode_remove_stream(inode, strm, blob_table); + return false; + } + } -/* Set the unnamed stream of a WIM inode, given a data buffer containing the - * stream contents. The inode must be resolved and cannot have an unnamed - * stream specified already. */ -int -inode_set_unnamed_stream(struct wim_inode *inode, const void *data, size_t len, - struct wim_lookup_table *lookup_table) -{ - wimlib_assert(inode->i_resolved); - wimlib_assert(!inode->i_lte); - inode->i_lte = new_stream_from_data_buffer(data, len, lookup_table); - if (!inode->i_lte) - return WIMLIB_ERR_NOMEM; - return 0; + inode_set_stream_blob(inode, strm, blob); + return true; } -/* Remove an alternate data stream from a WIM inode. */ +/* + * Remove a stream from the specified inode. + * + * This handles releasing the references to the blob descriptor, if any. + */ void -inode_remove_ads(struct wim_inode *inode, struct wim_ads_entry *entry, - struct wim_lookup_table *lookup_table) +inode_remove_stream(struct wim_inode *inode, struct wim_inode_stream *strm, + struct blob_table *blob_table) { - struct wim_lookup_table_entry *lte; - unsigned idx = entry - inode->i_ads_entries; + unsigned idx = strm - inode->i_streams; + + wimlib_assert(idx < inode->i_num_streams); - wimlib_assert(idx < inode->i_num_ads); - wimlib_assert(inode->i_resolved); + inode_unset_stream_blob(inode, strm, blob_table); - lte = entry->lte; - if (lte) - lte_decrement_refcnt(lte, lookup_table); + destroy_stream(strm); - destroy_ads_entry(entry); + memmove(strm, strm + 1, + (inode->i_num_streams - idx - 1) * sizeof(inode->i_streams[0])); + inode->i_num_streams--; +} - memmove(&inode->i_ads_entries[idx], - &inode->i_ads_entries[idx + 1], - (inode->i_num_ads - idx - 1) * sizeof(inode->i_ads_entries[0])); - inode->i_num_ads--; +/* Returns true iff the specified inode has at least one named data stream. */ +bool +inode_has_named_data_stream(const struct wim_inode *inode) +{ + for (unsigned i = 0; i < inode->i_num_streams; i++) + if (stream_is_named_data_stream(&inode->i_streams[i])) + return true; + return false; } /* - * Resolve an inode's single-instance streams. + * Resolve an inode's streams. * - * This takes each SHA-1 message digest stored in the inode or one of its ADS - * entries and replaces it with a pointer directly to the appropriate 'struct - * wim_lookup_table_entry' currently inserted into @table to represent the - * single-instance stream having that SHA-1 message digest. + * For each stream, this replaces the SHA-1 message digest of the blob data with + * a pointer to the 'struct blob_descriptor' for the blob. Blob descriptors are + * looked up in @table. * * If @force is %false: - * If any of the needed single-instance streams do not exist in @table, - * return WIMLIB_ERR_RESOURCE_NOT_FOUND and leave the inode unmodified. + * If any of the needed blobs do not exist in @table, return + * WIMLIB_ERR_RESOURCE_NOT_FOUND. * If @force is %true: - * If any of the needed single-instance streams do not exist in @table, - * allocate new entries for them and insert them into @table. This does - * not, of course, cause these streams to magically exist, but this is + * If any of the needed blobs do not exist in @table, allocate new blob + * descriptors for them and insert them into @table. This does not, of + * course, cause the data of these blobs to magically exist, but this is * needed by the code for extraction from a pipe. * - * If the inode is already resolved, this function does nothing. - * * Returns 0 on success; WIMLIB_ERR_NOMEM if out of memory; or - * WIMLIB_ERR_RESOURCE_NOT_FOUND if @force is %false and at least one - * single-instance stream referenced by the inode was missing. + * WIMLIB_ERR_RESOURCE_NOT_FOUND if @force is %false and at least one blob + * referenced by the inode was missing. */ int -inode_resolve_streams(struct wim_inode *inode, struct wim_lookup_table *table, +inode_resolve_streams(struct wim_inode *inode, struct blob_table *table, bool force) { - const u8 *hash; - struct wim_lookup_table_entry *lte, *ads_lte; - - if (inode->i_resolved) - return 0; - - struct wim_lookup_table_entry *ads_ltes[inode->i_num_ads]; - - /* Resolve the default data stream */ - lte = NULL; - hash = inode->i_hash; - if (!is_zero_hash(hash)) { - lte = lookup_stream(table, hash); - if (!lte) { - if (force) { - lte = new_lookup_table_entry(); - if (!lte) - return WIMLIB_ERR_NOMEM; - copy_hash(lte->hash, hash); - lookup_table_insert(table, lte); - } else { - goto stream_not_found; - } - } - } + for (unsigned i = 0; i < inode->i_num_streams; i++) { + struct wim_inode_stream *strm = &inode->i_streams[i]; + + if (strm->stream_resolved) + continue; - /* Resolve the alternate data streams */ - for (u16 i = 0; i < inode->i_num_ads; i++) { - struct wim_ads_entry *cur_entry; + const u8 *hash = stream_hash(strm); + struct blob_descriptor *blob = NULL; - ads_lte = NULL; - cur_entry = &inode->i_ads_entries[i]; - hash = cur_entry->hash; if (!is_zero_hash(hash)) { - ads_lte = lookup_stream(table, hash); - if (!ads_lte) { - if (force) { - ads_lte = new_lookup_table_entry(); - if (!ads_lte) - return WIMLIB_ERR_NOMEM; - copy_hash(ads_lte->hash, hash); - lookup_table_insert(table, ads_lte); - } else { - goto stream_not_found; - } + blob = lookup_blob(table, hash); + if (!blob) { + if (!force) + return blob_not_found_error(inode, hash); + blob = new_blob_descriptor(); + if (!blob) + return WIMLIB_ERR_NOMEM; + copy_hash(blob->hash, hash); + blob_table_insert(table, blob); } } - ads_ltes[i] = ads_lte; + strm->_stream_blob = blob; + strm->stream_resolved = 1; } - inode->i_lte = lte; - for (u16 i = 0; i < inode->i_num_ads; i++) - inode->i_ads_entries[i].lte = ads_ltes[i]; - inode->i_resolved = 1; return 0; - -stream_not_found: - return stream_not_found_error(inode, hash); -} - -/* - * Undo the effects of inode_resolve_streams(). - * - * If the inode is not resolved, this function does nothing. - */ -void -inode_unresolve_streams(struct wim_inode *inode) -{ - if (!inode->i_resolved) - return; - - if (inode->i_lte) - copy_hash(inode->i_hash, inode->i_lte->hash); - else - zero_out_hash(inode->i_hash); - - for (u16 i = 0; i < inode->i_num_ads; i++) { - if (inode->i_ads_entries[i].lte) - copy_hash(inode->i_ads_entries[i].hash, - inode->i_ads_entries[i].lte->hash); - else - zero_out_hash(inode->i_ads_entries[i].hash); - } - inode->i_resolved = 0; } int -stream_not_found_error(const struct wim_inode *inode, const u8 *hash) +blob_not_found_error(const struct wim_inode *inode, const u8 *hash) { if (wimlib_print_errors) { tchar hashstr[SHA1_HASH_SIZE * 2 + 1]; sprint_hash(hash, hashstr); - ERROR("\"%"TS"\": stream not found\n" - " SHA-1 message digest of missing stream:\n" + ERROR("\"%"TS"\": blob not found\n" + " SHA-1 message digest of missing blob:\n" " %"TS"", inode_first_full_path(inode), hashstr); } @@ -478,505 +498,131 @@ stream_not_found_error(const struct wim_inode *inode, const u8 *hash) } /* - * Return the lookup table entry for stream @stream_idx of the inode, where - * stream_idx = 0 means the default un-named file stream, and stream_idx >= 1 - * corresponds to an alternate data stream. - * - * This works for both resolved and un-resolved inodes. + * Return the blob descriptor for the specified stream, or NULL if the stream is + * empty or its blob is not available in @table. */ -struct wim_lookup_table_entry * -inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx, - const struct wim_lookup_table *table) +struct blob_descriptor * +stream_blob(const struct wim_inode_stream *strm, const struct blob_table *table) { - if (inode->i_resolved) - return inode_stream_lte_resolved(inode, stream_idx); + if (strm->stream_resolved) + return strm->_stream_blob; else - return inode_stream_lte_unresolved(inode, stream_idx, table); + return lookup_blob(table, strm->_stream_hash); } -/* - * Return the lookup table entry for the unnamed data stream of a *resolved* - * inode, or NULL if there is none. Also return the 0-based index of the - * corresponding stream in *stream_idx_ret. - */ -struct wim_lookup_table_entry * -inode_unnamed_stream_resolved(const struct wim_inode *inode, u16 *stream_idx_ret) +/* Return the SHA-1 message digest of the data of the specified stream, or a + * void SHA-1 of all zeroes if the specified stream is empty. */ +const u8 * +stream_hash(const struct wim_inode_stream *strm) { - wimlib_assert(inode->i_resolved); - for (unsigned i = 0; i <= inode->i_num_ads; i++) { - if (inode_stream_name_nbytes(inode, i) == 0 && - !is_zero_hash(inode_stream_hash_resolved(inode, i))) - { - *stream_idx_ret = i; - return inode_stream_lte_resolved(inode, i); - } - } - *stream_idx_ret = 0; - return NULL; + if (strm->stream_resolved) + return strm->_stream_blob ? strm->_stream_blob->hash : zero_hash; + else + return strm->_stream_hash; } /* - * Return the lookup table entry for the unnamed data stream of a *resolved* - * inode, or NULL if there is none. + * Return the blob descriptor for the unnamed data stream of the inode, or NULL + * if the inode does not have an unnamed data stream, the inode's unnamed data + * stream is empty, or the blob for the inode's unnamed data stream is not + * available in @blob_table. */ -struct wim_lookup_table_entry * -inode_unnamed_lte_resolved(const struct wim_inode *inode) +struct blob_descriptor * +inode_get_blob_for_unnamed_data_stream(const struct wim_inode *inode, + const struct blob_table *blob_table) { - u16 stream_idx; - return inode_unnamed_stream_resolved(inode, &stream_idx); -} + const struct wim_inode_stream *strm; -/* - * Return the lookup table entry for the unnamed data stream of an *unresolved* - * inode, or NULL if there is none. - */ -struct wim_lookup_table_entry * -inode_unnamed_lte_unresolved(const struct wim_inode *inode, - const struct wim_lookup_table *table) -{ - wimlib_assert(!inode->i_resolved); - for (unsigned i = 0; i <= inode->i_num_ads; i++) { - if (inode_stream_name_nbytes(inode, i) == 0 && - !is_zero_hash(inode_stream_hash_unresolved(inode, i))) - { - return inode_stream_lte_unresolved(inode, i, table); - } - } - return NULL; -} + strm = inode_get_unnamed_data_stream(inode); + if (!strm) + return NULL; -/* - * 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 WIM - * dentry itself. So we need to check the alternate data streams too. - * - * Also, note that a dentry may appear to have more than one unnamed stream, but - * if the SHA-1 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 - * entries.). This is despite the fact that we may need to extract such a - * missing entry as an empty file or empty named data stream. - */ -struct wim_lookup_table_entry * -inode_unnamed_lte(const struct wim_inode *inode, - const struct wim_lookup_table *table) -{ - if (inode->i_resolved) - return inode_unnamed_lte_resolved(inode); - else - return inode_unnamed_lte_unresolved(inode, table); + return stream_blob(strm, blob_table); } -/* - * Return the SHA-1 message digest of the unnamed data stream of a WIM inode. - * - * If inode does not have an unnamed data stream, this returns a void SHA-1 - * message digest containing all zero bytes. - */ -const u8 * -inode_unnamed_stream_hash(const struct wim_inode *inode) +/* Like inode_get_blob_for_unnamed_data_stream(), but assumes the unnamed data + * stream is resolved. */ +struct blob_descriptor * +inode_get_blob_for_unnamed_data_stream_resolved(const struct wim_inode *inode) { - const u8 *hash; + const struct wim_inode_stream *strm; - for (unsigned i = 0; i <= inode->i_num_ads; i++) { - if (inode_stream_name_nbytes(inode, i) == 0) { - hash = inode_stream_hash(inode, i); - if (!is_zero_hash(hash)) - return hash; - } - } - return zero_hash; -} + strm = inode_get_unnamed_data_stream(inode); + if (!strm) + return NULL; -/* - * Translate a single-instance stream entry into the pointer contained in the - * inode (or ads entry of an inode) that references it. - * - * This is only possible for "unhashed" streams, which are guaranteed to have - * only one reference, and that reference is guaranteed to be in a resolved - * inode. (It can't be in an unresolved inode, since that would imply the hash - * is known!) - */ -struct wim_lookup_table_entry ** -retrieve_lte_pointer(struct wim_lookup_table_entry *lte) -{ - wimlib_assert(lte->unhashed); - struct wim_inode *inode = lte->back_inode; - u32 stream_id = lte->back_stream_id; - if (stream_id == 0) - return &inode->i_lte; - else - for (u16 i = 0; i < inode->i_num_ads; i++) - if (inode->i_ads_entries[i].stream_id == stream_id) - return &inode->i_ads_entries[i].lte; - wimlib_assert(0); - return NULL; + return stream_blob_resolved(strm); } /* - * Read the alternate data stream entries of a WIM dentry. - * - * @p: - * Pointer to buffer that starts with the first alternate stream entry. - * - * @inode: - * Inode to load the alternate data streams into. @inode->i_num_ads must - * have been set to the number of alternate data streams that are expected. - * - * @nbytes_remaining_p: - * Number of bytes of data remaining in the buffer pointed to by @p. - * On success this will be updated to point just past the ADS entries. - * - * On success, inode->i_ads_entries is set to an array of `struct - * wim_ads_entry's of length inode->i_num_ads. On failure, @inode is not - * modified. - * - * Return values: - * WIMLIB_ERR_SUCCESS (0) - * WIMLIB_ERR_INVALID_METADATA_RESOURCE - * WIMLIB_ERR_NOMEM + * Return the SHA-1 message digest of the unnamed data stream of the inode, or a + * void SHA-1 of all zeroes if the inode does not have an unnamed data stream or + * if the inode's unnamed data stream is empty. */ -int -read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, - size_t *nbytes_remaining_p) +const u8 * +inode_get_hash_of_unnamed_data_stream(const struct wim_inode *inode) { - size_t nbytes_remaining = *nbytes_remaining_p; - u16 num_ads; - struct wim_ads_entry *ads_entries; - int ret; - - BUILD_BUG_ON(sizeof(struct wim_ads_entry_on_disk) != WIM_ADS_ENTRY_DISK_SIZE); - - /* Allocate an array for our in-memory representation of the alternate - * data stream entries. */ - num_ads = inode->i_num_ads; - ads_entries = CALLOC(num_ads, sizeof(inode->i_ads_entries[0])); - if (!ads_entries) - goto out_of_memory; - - /* Read the entries into our newly allocated buffer. */ - for (u16 i = 0; i < num_ads; i++) { - u64 length; - struct wim_ads_entry *cur_entry; - const struct wim_ads_entry_on_disk *disk_entry = - (const struct wim_ads_entry_on_disk*)p; - - cur_entry = &ads_entries[i]; - ads_entries[i].stream_id = i + 1; - - /* Do we have at least the size of the fixed-length data we know - * need? */ - if (nbytes_remaining < sizeof(struct wim_ads_entry_on_disk)) - goto out_invalid; - - /* Read the length field */ - length = le64_to_cpu(disk_entry->length); - - /* Make sure the length field is neither so small it doesn't - * include all the fixed-length data nor so large it overflows - * the metadata resource buffer. */ - if (length < sizeof(struct wim_ads_entry_on_disk) || - length > nbytes_remaining) - goto out_invalid; - - /* Read the rest of the fixed-length data. */ - - cur_entry->reserved = le64_to_cpu(disk_entry->reserved); - copy_hash(cur_entry->hash, disk_entry->hash); - cur_entry->stream_name_nbytes = le16_to_cpu(disk_entry->stream_name_nbytes); - - /* If stream_name_nbytes != 0, this is a named stream. - * Otherwise this is an unnamed stream, or in some cases (bugs - * in Microsoft's software I guess) a meaningless entry - * distinguished from the real unnamed stream entry, if any, by - * the fact that the real unnamed stream entry has a nonzero - * hash field. */ - if (cur_entry->stream_name_nbytes) { - /* The name is encoded in UTF16-LE, which uses 2-byte - * coding units, so the length of the name had better be - * an even number of bytes... */ - if (cur_entry->stream_name_nbytes & 1) - goto out_invalid; - - /* Add the length of the stream name to get the length - * we actually need to read. Make sure this isn't more - * than the specified length of the entry. */ - if (sizeof(struct wim_ads_entry_on_disk) + - cur_entry->stream_name_nbytes > length) - goto out_invalid; - - cur_entry->stream_name = utf16le_dupz(disk_entry->stream_name, - cur_entry->stream_name_nbytes); - if (!cur_entry->stream_name) - goto out_of_memory; - } else { - /* Mark inode as having weird stream entries. */ - inode->i_canonical_streams = 0; - } + const struct wim_inode_stream *strm; - /* It's expected that the size of every ADS entry is a multiple - * of 8. However, to be safe, I'm allowing the possibility of - * an ADS entry at the very end of the metadata resource ending - * un-aligned. So although we still need to increment the input - * pointer by @length to reach the next ADS entry, it's possible - * that less than @length is actually remaining in the metadata - * resource. We should set the remaining bytes to 0 if this - * happens. */ - length = (length + 7) & ~7; - p += length; - if (nbytes_remaining < length) - nbytes_remaining = 0; - else - nbytes_remaining -= length; - } - inode->i_ads_entries = ads_entries; - inode->i_next_stream_id = inode->i_num_ads + 1; - *nbytes_remaining_p = nbytes_remaining; - ret = 0; - goto out; -out_of_memory: - ret = WIMLIB_ERR_NOMEM; - goto out_free_ads_entries; -out_invalid: - ERROR("An alternate data stream entry is invalid"); - ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; -out_free_ads_entries: - if (ads_entries) { - for (u16 i = 0; i < num_ads; i++) - destroy_ads_entry(&ads_entries[i]); - FREE(ads_entries); - } -out: - return ret; -} - -/* Check a WIM inode for unusual field values. */ -void -check_inode(struct wim_inode *inode, const struct wim_security_data *sd) -{ - /* Check the security ID. -1 is valid and means "no security - * descriptor". Anything else has to be a valid index into the WIM - * image's security descriptors table. */ - if (inode->i_security_id < -1 || - (inode->i_security_id >= 0 && - inode->i_security_id >= sd->num_entries)) - { - WARNING("\"%"TS"\" has an invalid security ID (%d)", - inode_first_full_path(inode), inode->i_security_id); - inode->i_security_id = -1; - } + strm = inode_get_unnamed_data_stream(inode); + if (!strm) + return zero_hash; - /* Make sure there is only one unnamed data stream. */ - unsigned num_unnamed_streams = 0; - for (unsigned i = 0; i <= inode->i_num_ads; i++) { - const u8 *hash; - hash = inode_stream_hash(inode, i); - if (inode_stream_name_nbytes(inode, i) == 0 && !is_zero_hash(hash)) - num_unnamed_streams++; - } - if (num_unnamed_streams > 1) { - WARNING("\"%"TS"\" has multiple (%u) un-named streams", - inode_first_full_path(inode), num_unnamed_streams); - /* We currently don't treat this as an error and will just end - * up using the first unnamed data stream in the inode. */ - } + return stream_hash(strm); } -/* Acquire another reference to each single-instance stream referenced by this - * inode. This is necessary when creating a hard link to this inode. +/* Acquire another reference to each blob referenced by this inode. This is + * necessary when creating a hard link to this inode. * - * The inode must be resolved. */ + * All streams of the inode must be resolved. */ void -inode_ref_streams(struct wim_inode *inode) +inode_ref_blobs(struct wim_inode *inode) { - for (unsigned i = 0; i <= inode->i_num_ads; i++) { - struct wim_lookup_table_entry *lte; + for (unsigned i = 0; i < inode->i_num_streams; i++) { + struct blob_descriptor *blob; - lte = inode_stream_lte_resolved(inode, i); - if (lte) - lte->refcnt++; + blob = stream_blob_resolved(&inode->i_streams[i]); + if (blob) + blob->refcnt++; } } -/* Drop a reference to each single-instance stream referenced by this inode. - * This is necessary when deleting a hard link to this inode. */ +/* Release a reference to each blob referenced by this inode. This is necessary + * when deleting a hard link to this inode. */ void -inode_unref_streams(struct wim_inode *inode, - struct wim_lookup_table *lookup_table) +inode_unref_blobs(struct wim_inode *inode, struct blob_table *blob_table) { - for (unsigned i = 0; i <= inode->i_num_ads; i++) { - struct wim_lookup_table_entry *lte; + for (unsigned i = 0; i < inode->i_num_streams; i++) { + struct blob_descriptor *blob; - lte = inode_stream_lte(inode, i, lookup_table); - if (lte) - lte_decrement_refcnt(lte, lookup_table); + blob = stream_blob(&inode->i_streams[i], blob_table); + if (blob) + blob_decrement_refcnt(blob, blob_table); } } -/* Initialize a hash table for hard link detection. */ -int -init_inode_table(struct wim_inode_table *table, size_t capacity) -{ - table->array = CALLOC(capacity, sizeof(table->array[0])); - if (!table->array) - return WIMLIB_ERR_NOMEM; - table->num_entries = 0; - table->capacity = capacity; - INIT_LIST_HEAD(&table->extra_inodes); - return 0; -} - -/* Free the memory allocated by init_inode_table(). */ -void -destroy_inode_table(struct wim_inode_table *table) -{ - FREE(table->array); -} - -static struct wim_inode * -inode_table_get_inode(struct wim_inode_table *table, u64 ino, u64 devno) -{ - u64 hash = hash_u64(hash_u64(ino) + hash_u64(devno)); - size_t pos = hash % table->capacity; - struct wim_inode *inode; - struct hlist_node *cur; - - /* Search for an existing inode having the same inode number and device - * number. */ - hlist_for_each_entry(inode, cur, &table->array[pos], i_hlist) { - if (inode->i_ino == ino && inode->i_devno == devno) { - /* Found; use the existing inode. */ - inode->i_nlink++; - return inode; - } - } - - /* Create a new inode and insert it into the table. */ - inode = new_timeless_inode(); - if (inode) { - inode->i_ino = ino; - inode->i_devno = devno; - hlist_add_head(&inode->i_hlist, &table->array[pos]); - table->num_entries++; - } - return inode; -} - /* - * Allocate a new dentry, with hard link detection. - * - * @table - * The inode table being used for the current directory scan operation. It - * will contain the mapping from (ino, devno) pairs to inodes. - * - * @name - * The name to give the new dentry. - * - * @ino - * The inode number of the file, read from the filesystem. + * Given a blob descriptor, return a pointer to the pointer contained in the + * stream that references it. * - * @devno - * The device number of the file, read from the filesystem. Proper setting - * of this parameter prevents cross-device hardlinks from being created. - * If this is not a problem (perhaps because the current directory scan - * operation is guaranteed to never traverse a filesystem boundary), then - * this parameter can just be a fixed value such as 0. - * - * @noshare - * If %true, the new dentry will not be hard linked to any existing inode, - * regardless of the values of @ino and @devno. If %false, normal hard - * link detection will be done. - * - * @dentry_ret - * On success, a pointer to the new dentry will be returned in this - * location. If i_nlink of the dentry's inode is greater than 1, then this - * function created a hard link to an existing inode rather than creating a - * new inode. - * - * On success, returns 0. On failure, returns WIMLIB_ERR_NOMEM or an error code - * resulting from a failed string conversion. + * This is only possible for "unhashed" blobs, which are guaranteed to have only + * one referencing stream, and that reference is guaranteed to be in a resolved + * stream. (It can't be in an unresolved stream, since that would imply the + * hash is known!) */ -int -inode_table_new_dentry(struct wim_inode_table *table, const tchar *name, - u64 ino, u64 devno, bool noshare, - struct wim_dentry **dentry_ret) +struct blob_descriptor ** +retrieve_pointer_to_unhashed_blob(struct blob_descriptor *blob) { - struct wim_dentry *dentry; - struct wim_inode *inode; - int ret; - - if (noshare) { - /* File that cannot be hardlinked--- Return a new inode with its - * inode and device numbers left at 0. */ - ret = new_dentry_with_timeless_inode(name, &dentry); - if (ret) - return ret; - list_add_tail(&dentry->d_inode->i_list, &table->extra_inodes); - } else { - /* File that can be hardlinked--- search the table for an - * existing inode matching the inode number and device; - * otherwise create a new inode. */ - ret = new_dentry(name, &dentry); - if (ret) - return ret; - inode = inode_table_get_inode(table, ino, devno); - if (!inode) { - free_dentry(dentry); - return WIMLIB_ERR_NOMEM; - } - /* If using an existing inode, we need to gain a reference to - * each of its streams. */ - if (inode->i_nlink > 1) - inode_ref_streams(inode); - dentry->d_inode = inode; - inode_add_dentry(dentry, inode); - } - *dentry_ret = dentry; - return 0; -} - + wimlib_assert(blob->unhashed); - -/* - * Following the allocation of dentries with hard link detection using - * inode_table_new_dentry(), this function will assign consecutive inode numbers - * to the new set of inodes. It will also append the list of new inodes to the - * list @head, which must contain any inodes already existing in the WIM image. - */ -void -inode_table_prepare_inode_list(struct wim_inode_table *table, - struct list_head *head) -{ - struct wim_inode *inode, *tmp_inode; - struct hlist_node *cur, *tmp; - u64 cur_ino = 1; - - /* Re-assign inode numbers in the existing list to avoid duplicates. */ - list_for_each_entry(inode, head, i_list) - inode->i_ino = cur_ino++; - - /* Assign inode numbers to the new inodes and move them to the image's - * inode list. */ - for (size_t i = 0; i < table->capacity; i++) { - hlist_for_each_entry_safe(inode, cur, tmp, &table->array[i], i_hlist) - { - inode->i_ino = cur_ino++; - inode->i_devno = 0; - list_add_tail(&inode->i_list, head); + struct wim_inode *inode = blob->back_inode; + for (unsigned i = 0; i < inode->i_num_streams; i++) { + if (inode->i_streams[i].stream_id == blob->back_stream_id) { + wimlib_assert(inode->i_streams[i]._stream_blob == blob); + return &inode->i_streams[i]._stream_blob; } - INIT_HLIST_HEAD(&table->array[i]); - } - list_for_each_entry_safe(inode, tmp_inode, &table->extra_inodes, i_list) - { - inode->i_ino = cur_ino++; - inode->i_devno = 0; - list_add_tail(&inode->i_list, head); } - INIT_LIST_HEAD(&table->extra_inodes); - table->num_entries = 0; + + wimlib_assert(0); + return NULL; }