X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Finode.c;h=6384b11c408d4ccb9212cccbf3c10053ea475d30;hp=bb953c57bfd2d1308fea4700c924c95688dd957a;hb=b5fae0e5ddac332b25856e3df7556aa3ee7f69fb;hpb=29b7365197d276ce89ba7d44efd629427b12b6aa diff --git a/src/inode.c b/src/inode.c index bb953c57..6384b11c 100644 --- a/src/inode.c +++ b/src/inode.c @@ -1,40 +1,41 @@ /* * inode.c + * + * Functions that operate on WIM inodes. + * + * See dentry.c for a description of the relationship between WIM dentries and + * WIM inodes. */ /* - * Copyright (C) 2012, 2013 Eric Biggers - * - * This file is part of wimlib, a library for working with WIM files. + * Copyright (C) 2012, 2013, 2014 Eric Biggers * - * wimlib is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free - * Software Foundation; either version 3 of the License, or (at your option) - * any later version. + * 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 + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. * - * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * - * You should have received a copy of the GNU General Public License - * along with wimlib; if not, see http://www.gnu.org/licenses/. + * You should have received a copy of the GNU Lesser General Public License + * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include + #include "wimlib/assert.h" -#include "wimlib/case.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/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" @@ -45,7 +46,7 @@ new_inode(void) { struct wim_inode *inode = new_timeless_inode(); if (inode) { - u64 now = get_wim_timestamp(); + u64 now = now_as_wim_timestamp(); inode->i_creation_time = now; inode->i_last_access_time = now; inode->i_last_write_time = now; @@ -53,7 +54,6 @@ new_inode(void) return inode; } - /* Allocate a new inode. Leave the timestamps zeroed out. */ struct wim_inode * new_timeless_inode(void) @@ -61,7 +61,7 @@ new_timeless_inode(void) struct wim_inode *inode = CALLOC(1, sizeof(struct wim_inode)); if (inode) { inode->i_security_id = -1; - inode->i_nlink = 1; + /*inode->i_nlink = 0;*/ inode->i_next_stream_id = 1; inode->i_not_rpfixed = 1; inode->i_canonical_streams = 1; @@ -71,43 +71,23 @@ new_timeless_inode(void) return inode; } -/* Decrement link count on an inode. */ -void -put_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. - * inode_put_fd() in mount_image.c handles dropping a file - * descriptor. */ - #ifdef WITH_FUSE - if (inode->i_num_opened_fds == 0) - #endif - free_inode(inode); - } -} - -/* De-allocate memory for an alternate data stream entry. */ +/* Free memory allocated within an alternate data stream entry. */ static void destroy_ads_entry(struct wim_ads_entry *ads_entry) { FREE(ads_entry->stream_name); } -/* 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. */ -void +static void free_inode(struct wim_inode *inode) { - if (inode == NULL) - return; - - if (inode->i_ads_entries) { - for (u16 i = 0; i < inode->i_num_ads; i++) + if (unlikely(inode->i_ads_entries)) { + for (unsigned 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)) @@ -115,19 +95,64 @@ free_inode(struct wim_inode *inode) FREE(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) +static inline void +free_inode_if_unneeded(struct wim_inode *inode) +{ + if (inode->i_nlink) + return; +#ifdef WITH_FUSE + if (inode->i_num_opened_fds) + return; +#endif + free_inode(inode); +} + +/* Associate a dentry with the specified inode. */ +void +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; + + wimlib_assert(inode->i_nlink > 0); + + list_del(&dentry->d_alias); + dentry->d_inode = NULL; + inode->i_nlink--; + + free_inode_if_unneeded(inode); +} + +#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 @@ -136,22 +161,27 @@ ads_entry_has_name(const struct wim_ads_entry *entry, * * If @p stream_name is the empty string, NULL is returned --- that is, this * function will not return "unnamed" alternate data stream entries. + * + * If NULL is returned, errno is set. */ struct wim_ads_entry * -inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name, - u16 *idx_ret) +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; + unsigned i; struct wim_ads_entry *result; - if (inode->i_num_ads == 0) + if (inode->i_num_ads == 0) { + errno = ENOENT; return NULL; + } - if (stream_name[0] == T('\0')) + if (stream_name[0] == T('\0')) { + errno = ENOENT; return NULL; + } ret = tstr_get_utf16le_and_len(stream_name, &stream_name_utf16le, &stream_name_utf16le_nbytes); @@ -161,13 +191,14 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name, 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)) + if (!cmp_utf16le_strings(inode->i_ads_entries[i].stream_name, + inode->i_ads_entries[i].stream_name_nbytes / + sizeof(utf16lechar), + stream_name_utf16le, + stream_name_utf16le_nbytes / + sizeof(utf16lechar), + default_ignore_case)) { - if (idx_ret) - *idx_ret = i; result = &inode->i_ads_entries[i]; break; } @@ -175,6 +206,8 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name, tstr_put_utf16le(stream_name_utf16le); + if (!result) + errno = ENOENT; return result; } @@ -182,21 +215,22 @@ static struct wim_ads_entry * do_inode_add_ads(struct wim_inode *inode, utf16lechar *stream_name, size_t stream_name_nbytes) { - u16 num_ads; + unsigned num_ads; struct wim_ads_entry *ads_entries; struct wim_ads_entry *new_entry; - if (inode->i_num_ads >= 0xfffe) { - ERROR("Too many alternate data streams in one inode!"); + if (unlikely(inode->i_num_ads >= 0xfffe)) { + ERROR("File \"%"TS"\" has too many alternate data streams!", + 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 == NULL) { - ERROR("Failed to allocate memory for new alternate data stream"); + if (!ads_entries) return NULL; - } + inode->i_ads_entries = ads_entries; new_entry = &inode->i_ads_entries[num_ads - 1]; @@ -209,6 +243,12 @@ do_inode_add_ads(struct wim_inode *inode, return new_entry; } +/* + * 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) @@ -229,25 +269,24 @@ inode_add_ads_utf16le(struct wim_inode *inode, } /* - * Add an alternate stream entry to a WIM inode. On success, returns a pointer - * to the new entry; on failure, returns NULL. + * 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; - int ret; struct wim_ads_entry *result; - if (stream_name && *stream_name) { - ret = tstr_to_utf16le(stream_name, - tstrlen(stream_name) * sizeof(tchar), - &stream_name_utf16le, - &stream_name_utf16le_nbytes); - if (ret) + 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; - } result = do_inode_add_ads(inode, stream_name_utf16le, stream_name_utf16le_nbytes); @@ -256,71 +295,51 @@ inode_add_ads(struct wim_inode *inode, const tchar *stream_name) return result; } -int +/* + * 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. + * + * 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. + */ +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) { - struct wim_ads_entry *new_ads_entry; + struct wim_ads_entry *new_entry; wimlib_assert(inode->i_resolved); - new_ads_entry = inode_add_ads(inode, name); - if (new_ads_entry == NULL) - return WIMLIB_ERR_NOMEM; + new_entry = inode_add_ads(inode, name); + if (unlikely(!new_entry)) + return NULL; - new_ads_entry->lte = new_stream_from_data_buffer(value, size, - lookup_table); - if (new_ads_entry->lte == NULL) { - inode_remove_ads(inode, new_ads_entry - inode->i_ads_entries, - lookup_table); - return WIMLIB_ERR_NOMEM; + new_entry->lte = new_stream_from_data_buffer(value, size, lookup_table); + if (unlikely(!new_entry->lte)) { + inode_remove_ads(inode, new_entry, NULL); + return NULL; } - return 0; -} - -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; -} - -/* Set the unnamed stream of a WIM inode, given a data buffer containing the - * stream contents. */ -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); - inode->i_lte = new_stream_from_data_buffer(data, len, lookup_table); - if (inode->i_lte == NULL) - return WIMLIB_ERR_NOMEM; - return 0; + return new_entry; } -/* Remove an alternate data stream from a WIM inode */ +/* Remove an alternate data stream from a WIM inode. */ void -inode_remove_ads(struct wim_inode *inode, u16 idx, +inode_remove_ads(struct wim_inode *inode, struct wim_ads_entry *entry, struct wim_lookup_table *lookup_table) { - struct wim_ads_entry *ads_entry; struct wim_lookup_table_entry *lte; + unsigned idx = entry - inode->i_ads_entries; wimlib_assert(idx < inode->i_num_ads); wimlib_assert(inode->i_resolved); - ads_entry = &inode->i_ads_entries[idx]; - - DEBUG("Remove alternate data stream \"%"WS"\"", ads_entry->stream_name); - - lte = ads_entry->lte; + lte = entry->lte; if (lte) lte_decrement_refcnt(lte, lookup_table); - destroy_ads_entry(ads_entry); + destroy_ads_entry(entry); memmove(&inode->i_ads_entries[idx], &inode->i_ads_entries[idx + 1], @@ -328,189 +347,166 @@ inode_remove_ads(struct wim_inode *inode, u16 idx, inode->i_num_ads--; } +/* Return true iff the specified inode has at least one named data stream. */ bool -inode_has_unix_data(const struct wim_inode *inode) +inode_has_named_stream(const struct wim_inode *inode) { - for (u16 i = 0; i < inode->i_num_ads; i++) - if (ads_entry_is_unix_data(&inode->i_ads_entries[i])) + for (unsigned i = 0; i < inode->i_num_ads; i++) + if (inode->i_ads_entries[i].stream_name_nbytes) return true; return false; } -#ifndef __WIN32__ +/* Set the unnamed stream of a WIM inode, given a data buffer containing the + * stream contents. The inode must be resolved and cannot already have an + * unnamed stream. */ int -inode_get_unix_data(const struct wim_inode *inode, - struct wimlib_unix_data *unix_data, - u16 *stream_idx_ret) +inode_set_unnamed_stream(struct wim_inode *inode, const void *data, size_t len, + struct wim_lookup_table *lookup_table) { - const struct wim_ads_entry *ads_entry; - const struct wim_lookup_table_entry *lte; - size_t size; - int ret; - wimlib_assert(inode->i_resolved); + wimlib_assert(!inode->i_lte); - ads_entry = inode_get_ads_entry((struct wim_inode*)inode, - WIMLIB_UNIX_DATA_TAG, NULL); - if (ads_entry == NULL) - return NO_UNIX_DATA; - - if (stream_idx_ret) - *stream_idx_ret = ads_entry - inode->i_ads_entries; - - lte = ads_entry->lte; - if (lte == NULL) - return NO_UNIX_DATA; - - size = lte->size; - if (size != sizeof(struct wimlib_unix_data)) - return BAD_UNIX_DATA; - - ret = read_full_stream_into_buf(lte, unix_data); - if (ret) - return ret; - - if (unix_data->version != 0) - return BAD_UNIX_DATA; + inode->i_lte = new_stream_from_data_buffer(data, len, lookup_table); + if (!inode->i_lte) + return WIMLIB_ERR_NOMEM; return 0; } -int -inode_set_unix_data(struct wim_inode *inode, u16 uid, u16 gid, u16 mode, - struct wim_lookup_table *lookup_table, int which) -{ - struct wimlib_unix_data unix_data; - int ret; - bool have_good_unix_data = false; - bool have_unix_data = false; - u16 stream_idx; - - if (!(which & UNIX_DATA_CREATE)) { - ret = inode_get_unix_data(inode, &unix_data, &stream_idx); - if (ret == 0 || ret == BAD_UNIX_DATA || ret > 0) - have_unix_data = true; - if (ret == 0) - have_good_unix_data = true; - } - unix_data.version = 0; - if (which & UNIX_DATA_UID || !have_good_unix_data) - unix_data.uid = uid; - if (which & UNIX_DATA_GID || !have_good_unix_data) - unix_data.gid = gid; - if (which & UNIX_DATA_MODE || !have_good_unix_data) - unix_data.mode = mode; - ret = inode_add_ads_with_data(inode, WIMLIB_UNIX_DATA_TAG, - &unix_data, - sizeof(struct wimlib_unix_data), - lookup_table); - if (ret == 0 && have_unix_data) - inode_remove_ads(inode, stream_idx, lookup_table); - return ret; -} -#endif /* __WIN32__ */ - /* - * Resolve an inode's lookup table entries. + * Resolve an inode's single-instance streams. * - * This replaces the SHA1 hash fields (which are used to lookup an entry in the - * lookup table) with pointers directly to the lookup table entries. + * 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. * * If @force is %false: - * If any needed SHA1 message digests are not found in the lookup table, - * WIMLIB_ERR_RESOURCE_NOT_FOUND is returned and the inode is left - * unmodified. + * 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 @force is %true: - * If any needed SHA1 message digests are not found in the lookup table, - * new entries are allocated and inserted into the lookup table. + * 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 + * 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. */ int inode_resolve_streams(struct wim_inode *inode, struct wim_lookup_table *table, bool force) { const u8 *hash; + struct wim_lookup_table_entry *lte, *ads_lte; - if (!inode->i_resolved) { - 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; + } + } + } - /* Resolve the default file stream */ - lte = NULL; - hash = inode->i_hash; + /* Resolve the alternate data streams */ + for (unsigned i = 0; i < inode->i_num_ads; i++) { + struct wim_ads_entry *cur_entry; + + ads_lte = NULL; + cur_entry = &inode->i_ads_entries[i]; + hash = cur_entry->hash; if (!is_zero_hash(hash)) { - lte = lookup_stream(table, hash); - if (!lte) { + ads_lte = lookup_stream(table, hash); + if (!ads_lte) { if (force) { - lte = new_lookup_table_entry(); - if (!lte) + ads_lte = new_lookup_table_entry(); + if (!ads_lte) return WIMLIB_ERR_NOMEM; - copy_hash(lte->hash, hash); - lookup_table_insert(table, lte); + copy_hash(ads_lte->hash, hash); + lookup_table_insert(table, ads_lte); } else { goto stream_not_found; } } } - - /* Resolve the alternate data streams */ - struct wim_lookup_table_entry *ads_ltes[inode->i_num_ads]; - for (u16 i = 0; i < inode->i_num_ads; i++) { - struct wim_ads_entry *cur_entry; - - 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; - } - } - } - ads_ltes[i] = ads_lte; - } - 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; + ads_ltes[i] = ads_lte; } + inode->i_lte = lte; + for (unsigned 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) { - if (inode->i_lte) - copy_hash(inode->i_hash, inode->i_lte->hash); + 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 (unsigned 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_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; + 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) +{ + 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" + " %"TS"", + inode_first_full_path(inode), hashstr); + } + return WIMLIB_ERR_RESOURCE_NOT_FOUND; } /* - * Returns 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. + * Return the lookup table entry for the specified stream of the inode, or NULL + * if the specified stream is empty or not available. * - * This works for both resolved and un-resolved inodes. + * stream_idx = 0: default data stream + * stream_idx > 0: alternate data stream */ struct wim_lookup_table_entry * inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx, @@ -518,77 +514,87 @@ inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx, { if (inode->i_resolved) return inode_stream_lte_resolved(inode, stream_idx); - else - return inode_stream_lte_unresolved(inode, stream_idx, table); + if (stream_idx == 0) + return lookup_stream(table, inode->i_hash); + return lookup_stream(table, inode->i_ads_entries[stream_idx - 1].hash); } +/* + * Return the lookup table entry for the unnamed data stream of a *resolved* + * inode, or NULL if the inode's unnamed data stream is empty. Also return the + * 0-based index of the unnamed data stream in *stream_idx_ret. + */ struct wim_lookup_table_entry * -inode_unnamed_stream_resolved(const struct wim_inode *inode, u16 *stream_idx_ret) +inode_unnamed_stream_resolved(const struct wim_inode *inode, + unsigned *stream_idx_ret) { 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; -} -struct wim_lookup_table_entry * -inode_unnamed_lte_resolved(const struct wim_inode *inode) -{ - u16 stream_idx; - return inode_unnamed_stream_resolved(inode, &stream_idx); -} + *stream_idx_ret = 0; + if (likely(inode->i_lte)) + return inode->i_lte; -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))) + for (unsigned i = 0; i < inode->i_num_ads; i++) { + if (inode->i_ads_entries[i].stream_name_nbytes == 0 && + inode->i_ads_entries[i].lte) { - return inode_stream_lte_unresolved(inode, i, table); + *stream_idx_ret = i + 1; + return inode->i_ads_entries[i].lte; } } 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. +/* + * Return the lookup table entry for the unnamed data stream of an inode, or + * NULL if the inode's unnamed data stream is empty or not available. * - * Also, note that a dentry may appear to have more 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 - * 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. + * Note: this is complicated by the fact that WIMGAPI may put the unnamed data + * stream in an alternate data stream entry rather than in the dentry itself. */ struct wim_lookup_table_entry * inode_unnamed_lte(const struct wim_inode *inode, const struct wim_lookup_table *table) { + struct wim_lookup_table_entry *lte; + if (inode->i_resolved) return inode_unnamed_lte_resolved(inode); - else - return inode_unnamed_lte_unresolved(inode, table); + + lte = lookup_stream(table, inode->i_hash); + if (likely(lte)) + return lte; + + for (unsigned i = 0; i < inode->i_num_ads; i++) { + if (inode->i_ads_entries[i].stream_name_nbytes) + continue; + lte = lookup_stream(table, inode->i_ads_entries[i].hash); + if (lte) + return lte; + } + return NULL; } -/* Returns the SHA1 message digest of the unnamed data stream of a WIM inode, or - * 'zero_hash' if the unnamed data stream is missing has all zeroes in its SHA1 - * message digest field. */ +/* Return the SHA-1 message digest of the specified stream of the inode, or a + * void SHA-1 of all zeroes if the specified stream is empty. */ +const u8 * +inode_stream_hash(const struct wim_inode *inode, unsigned stream_idx) +{ + if (inode->i_resolved) { + struct wim_lookup_table_entry *lte; + + lte = inode_stream_lte_resolved(inode, stream_idx); + if (lte) + return lte->hash; + return zero_hash; + } + if (stream_idx == 0) + return inode->i_hash; + return inode->i_ads_entries[stream_idx - 1].hash; +} + +/* 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's unnamed data stream is empty. */ const u8 * inode_unnamed_stream_hash(const struct wim_inode *inode) { @@ -604,38 +610,39 @@ inode_unnamed_stream_hash(const struct wim_inode *inode) return zero_hash; } -/* Given an unhashed stream, get the pointer to it in an inode. - * As this is only for unhashed streams, there can only be one such pointer. */ -struct wim_lookup_table_entry ** -retrieve_lte_pointer(struct wim_lookup_table_entry *lte) +/* Acquire another reference to each single-instance stream referenced by this + * inode. This is necessary when creating a hard link to this inode. + * + * The inode must be resolved. */ +void +inode_ref_streams(struct wim_inode *inode) { - 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; + wimlib_assert(inode->i_resolved); + + if (inode->i_lte) + inode->i_lte->refcnt++; + for (unsigned i = 0; i < inode->i_num_ads; i++) + if (inode->i_ads_entries[i].lte) + inode->i_ads_entries[i].lte->refcnt++; } -int -stream_not_found_error(const struct wim_inode *inode, const u8 *hash) +/* Drop a reference to each single-instance stream 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) { - if (wimlib_print_errors) { - ERROR("\"%"TS"\": stream not found", inode_first_full_path(inode)); - tfprintf(stderr, T(" SHA-1 message digest of missing stream:\n ")); - print_hash(hash, stderr); - tputc(T('\n'), stderr); + for (unsigned i = 0; i <= inode->i_num_ads; i++) { + struct wim_lookup_table_entry *lte; + + lte = inode_stream_lte(inode, i, lookup_table); + if (lte) + lte_decrement_refcnt(lte, lookup_table); } - return WIMLIB_ERR_RESOURCE_NOT_FOUND; } /* - * Reads the alternate data stream entries of a WIM dentry. + * Read the alternate data stream entries of a WIM dentry. * * @p: * Pointer to buffer that starts with the first alternate stream entry. @@ -644,8 +651,9 @@ stream_not_found_error(const struct wim_inode *inode, const u8 *hash) * 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. * - * @remaining_size: + * @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 @@ -658,9 +666,10 @@ stream_not_found_error(const struct wim_inode *inode, const u8 *hash) */ int read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, - size_t nbytes_remaining) + size_t *nbytes_remaining_p) { - u16 num_ads; + size_t nbytes_remaining = *nbytes_remaining_p; + unsigned num_ads; struct wim_ads_entry *ads_entries; int ret; @@ -670,11 +679,11 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, * data stream entries. */ num_ads = inode->i_num_ads; ads_entries = CALLOC(num_ads, sizeof(inode->i_ads_entries[0])); - if (ads_entries == NULL) + if (!ads_entries) goto out_of_memory; /* Read the entries into our newly allocated buffer. */ - for (u16 i = 0; i < num_ads; i++) { + for (unsigned i = 0; i < num_ads; i++) { u64 length; struct wim_ads_entry *cur_entry; const struct wim_ads_entry_on_disk *disk_entry = @@ -726,7 +735,7 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, cur_entry->stream_name = utf16le_dupz(disk_entry->stream_name, cur_entry->stream_name_nbytes); - if (cur_entry->stream_name == NULL) + if (!cur_entry->stream_name) goto out_of_memory; } else { /* Mark inode as having weird stream entries. */ @@ -736,12 +745,12 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, /* 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 + * unaligned. 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) & ~(u64)7; + length = (length + 7) & ~7; p += length; if (nbytes_remaining < length) nbytes_remaining = 0; @@ -750,6 +759,7 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, } 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: @@ -760,7 +770,7 @@ out_invalid: ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; out_free_ads_entries: if (ads_entries) { - for (u16 i = 0; i < num_ads; i++) + for (unsigned i = 0; i < num_ads; i++) destroy_ads_entry(&ads_entries[i]); FREE(ads_entries); } @@ -768,18 +778,9 @@ out: return ret; } -/* - * Verify a WIM inode: - * - * - Check to make sure the security ID is valid - * - Check to make sure there is at most one unnamed stream - * - Check to make sure there is at most one DOS name. - * - * Return values: - * WIMLIB_ERR_SUCCESS (0) - */ -int -verify_inode(struct wim_inode *inode, const struct wim_security_data *sd) +/* 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 @@ -802,165 +803,33 @@ verify_inode(struct wim_inode *inode, const struct wim_security_data *sd) num_unnamed_streams++; } if (num_unnamed_streams > 1) { - WARNING("\"%"TS"\" has multiple (%u) un-named streams", + WARNING("\"%"TS"\" has multiple (%u) unnamed 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 0; } -void -inode_ref_streams(struct wim_inode *inode) -{ - for (unsigned i = 0; i <= inode->i_num_ads; i++) { - struct wim_lookup_table_entry *lte; - lte = inode_stream_lte_resolved(inode, i); - if (lte) - lte->refcnt++; - } -} - -void -inode_unref_streams(struct wim_inode *inode, - struct wim_lookup_table *lookup_table) -{ - struct wim_lookup_table_entry *lte; - unsigned i; - - for (i = 0; i <= inode->i_num_ads; i++) { - lte = inode_stream_lte(inode, i, lookup_table); - if (lte) - lte_decrement_refcnt(lte, lookup_table); - } -} - -int -init_inode_table(struct wim_inode_table *table, size_t capacity) -{ - table->array = CALLOC(capacity, sizeof(table->array[0])); - if (table->array == NULL) { - ERROR("Cannot initalize inode table: out of memory"); - return WIMLIB_ERR_NOMEM; - } - table->num_entries = 0; - table->capacity = capacity; - INIT_LIST_HEAD(&table->extra_inodes); - return 0; -} - -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; - - hlist_for_each_entry(inode, cur, &table->array[pos], i_hlist) { - if (inode->i_ino == ino && inode->i_devno == devno) { - DEBUG("Using existing inode {devno=%"PRIu64", ino=%"PRIu64"}", - devno, ino); - inode->i_nlink++; - return inode; - } - } - 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; -} - - -/* Given a directory entry with the name @name for the file with the inode - * number @ino and device number @devno, create a new WIM dentry with an - * associated inode, where the inode is shared if an inode with the same @ino - * and @devno has already been created. On success, the new WIM dentry is - * written to *dentry_ret, and its inode has i_nlink > 1 if a previously - * existing inode was used. +/* + * 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!) */ -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 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; -} - - - -/* Assign consecutive inode numbers to a new set of inodes from the inode table, - * and append the inodes to a single list @head that contains the inodes already - * existing in the WIM image. */ -void -inode_table_prepare_inode_list(struct wim_inode_table *table, - struct list_head *head) +struct wim_lookup_table_entry ** +retrieve_lte_pointer(struct wim_lookup_table_entry *lte) { - 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); - } - 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(lte->unhashed); + struct wim_inode *inode = lte->back_inode; + u32 stream_id = lte->back_stream_id; + if (stream_id == 0) + return &inode->i_lte; + for (unsigned 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; }