/*
* 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
+ * Copyright (C) 2012, 2013, 2014 Eric Biggers
*
* This file is part of wimlib, a library for working with WIM files.
*
return inode;
}
-
/* Allocate a new inode. Leave the timestamps zeroed out. */
struct wim_inode *
new_timeless_inode(void)
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. */
+ * around until all file descriptors to it have been closed. */
#ifdef WITH_FUSE
if (inode->i_num_opened_fds == 0)
#endif
}
}
-/* 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)
{
void
free_inode(struct wim_inode *inode)
{
- if (inode == NULL)
+ if (unlikely(!inode))
return;
- if (inode->i_ads_entries) {
+ 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 (inode->i_extra)
+ 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(). */
struct wim_ads_entry *new_entry;
if (inode->i_num_ads >= 0xfffe) {
- ERROR("Too many alternate data streams in one inode!");
+ 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];
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)
}
/*
- * Add an alternate stream entry to a WIM inode. On success, returns a pointer
- * to the new entry; on failure, returns NULL and sets errno.
+ * 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)
return result;
}
+/*
+ * 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,
new_entry->lte = new_stream_from_data_buffer(value, size, lookup_table);
if (!new_entry->lte) {
- inode_remove_ads(inode, new_entry, lookup_table);
+ inode_remove_ads(inode, new_entry, NULL);
return NULL;
}
return new_entry;
}
+/*
+ * Does the inode have any named data streams?
+ */
bool
inode_has_named_stream(const struct wim_inode *inode)
{
}
/* Set the unnamed stream of a WIM inode, given a data buffer containing the
- * stream contents. */
+ * 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 == NULL)
+ if (!inode->i_lte)
return WIMLIB_ERR_NOMEM;
return 0;
}
-/* 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, struct wim_ads_entry *entry,
struct wim_lookup_table *lookup_table)
}
/*
- * 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)
+ 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;
+ }
+ }
+ }
- if (!inode->i_resolved) {
- struct wim_lookup_table_entry *lte, *ads_lte;
+ /* Resolve the alternate data streams */
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ struct wim_ads_entry *cur_entry;
- /* Resolve the default file stream */
- lte = NULL;
- hash = inode->i_hash;
+ 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 (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) {
- 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 (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_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
+ * 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.
*
return inode_stream_lte_unresolved(inode, stream_idx, table);
}
+/*
+ * 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 NULL;
}
+/*
+ * Return the lookup table entry for the unnamed data stream of a *resolved*
+ * inode, or NULL if there is none.
+ */
struct wim_lookup_table_entry *
inode_unnamed_lte_resolved(const struct wim_inode *inode)
{
return inode_unnamed_stream_resolved(inode, &stream_idx);
}
+/*
+ * 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)
return NULL;
}
-/* Return the lookup table entry for the unnamed data stream of an inode, or
+/*
+ * Return the lookup table entry for the unnamed data stream of an inode, or
* NULL if there is none.
*
* You'd think this would be easier than it actually is, since the unnamed data
* 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 SHA1 message digest is all 0's then the corresponding stream does not
+ * 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
return inode_unnamed_lte_unresolved(inode, table);
}
-/* 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 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)
{
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. */
+/*
+ * 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)
{
return NULL;
}
-int
-stream_not_found_error(const struct wim_inode *inode, const u8 *hash)
-{
- if (wimlib_print_errors) {
- ERROR("\"%"TS"\": stream not found", inode_first_full_path(inode));
- tfprintf(wimlib_error_file,
- T(" SHA-1 message digest of missing stream:\n "));
- print_hash(hash, wimlib_error_file);
- tputc(T('\n'), wimlib_error_file);
- }
- 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.
* 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. */
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. */
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
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 0;
}
+/* 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)
{
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++;
}
}
+/* 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)
{
- struct wim_lookup_table_entry *lte;
- unsigned i;
+ for (unsigned i = 0; i <= inode->i_num_ads; i++) {
+ struct wim_lookup_table_entry *lte;
- 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);
}
}
+/* 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 == NULL) {
- ERROR("Cannot initalize inode table: out of memory");
+ 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)
{
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) {
- DEBUG("Using existing inode {devno=%"PRIu64", ino=%"PRIu64"}",
- devno, ino);
+ /* 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;
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.
+/*
+ * 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.
+ *
+ * @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.
*/
int
inode_table_new_dentry(struct wim_inode_table *table, const tchar *name,
-/* 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. */
+/*
+ * 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)