#ifndef _WIMLIB_INODE_H
#define _WIMLIB_INODE_H
-#include "wimlib/assert.h"
#include "wimlib/list.h"
-#include "wimlib/lookup_table.h"
-#include "wimlib/rbtree.h"
#include "wimlib/sha1.h"
-#include "wimlib/unix_data.h"
-
-#include <string.h>
+#include "wimlib/types.h"
+struct avl_tree_node;
struct wim_ads_entry;
struct wim_dentry;
-struct wim_security_data;
struct wim_lookup_table;
+struct wim_lookup_table_entry;
+struct wim_security_data;
struct wimfs_fd;
/*
* WIM inode.
*
- * As mentioned in the comment above `struct wim_dentry', in the WIM file that
+ * As mentioned in the comment above `struct wim_dentry', in WIM files there
* is no on-disk analogue of a real inode, as most of these fields are
* duplicated in the dentries. Instead, a `struct wim_inode' is something we
* create ourselves to simplify the handling of hard links.
* this inode. */
u32 i_attributes;
- /* Root of a red-black tree storing the child dentries of this inode, if
- * any. Keyed by wim_dentry->file_name, case sensitively. */
- struct rb_root i_children;
+ /* Root of a balanced binary search tree storing the child directory
+ * entries of this inode, if any. Keyed by wim_dentry->file_name, case
+ * sensitively. If this inode is not a directory or if it has no
+ * children then this will be an empty tree (NULL). */
+ struct avl_tree_node *i_children;
- /* Root of a red-black tree storing the children of this inode, if any.
- * Keyed by wim_dentry->file_name, case insensitively. */
- struct rb_root i_children_case_insensitive;
+ /* Root of a balanced binary search tree storing the child directory
+ * entries of this inode, if any. Keyed by wim_dentry->file_name, case
+ * insensitively. If this inode is not a directory or if it has no
+ * children then this will be an empty tree (NULL). */
+ struct avl_tree_node *i_children_ci;
/* List of dentries that are aliases for this inode. There will be
* i_nlink dentries in this list. */
/* Field to place this inode into a list. */
union {
- /* Hash list node- used in hardlink.c when the inodes are placed
- * into a hash table keyed by inode number and optionally device
- * number, in order to detect dentries that are aliases for the
- * same inode. */
+ /* Hash list node- used in inode_fixup.c when the inodes are
+ * placed into a hash table keyed by inode number and optionally
+ * device number, in order to detect dentries that are aliases
+ * for the same inode. */
struct hlist_node i_hlist;
- /* Normal list node- used to connect all the inodes of a WIM image
- * into a single linked list referenced from the
- * `struct wim_image_metadata' for that image. */
+ /* Normal list node- used to connect all the inodes of a WIM
+ * image into a single linked list referenced from the `struct
+ * wim_image_metadata' for that image. */
struct list_head i_list;
};
* error paths. */
u8 i_visited : 1;
- /* Set if the DOS name of an inode has already been extracted. */
- u8 i_dos_name_extracted : 1;
-
/* 1 iff all ADS entries of this inode are named or if this inode
* has no ADS entries */
u8 i_canonical_streams : 1;
+ /* Cached value */
+ u8 i_can_externally_back : 1;
+
/* Pointer to a malloc()ed array of i_num_ads alternate data stream
* entries for this inode. */
struct wim_ads_entry *i_ads_entries;
- /* Creation time, last access time, and last write time for this inode, in
- * 100-nanosecond intervals since 12:00 a.m UTC January 1, 1601. They
- * should correspond to the times gotten by calling GetFileTime() on
- * Windows. */
+ /* If not NULL, a pointer to the extra data that was read from the
+ * dentry. This should be a series of tagged items, each of which
+ * represents a bit of extra metadata, such as the file's object ID.
+ * See tagged_items.c for more information. */
+ void *i_extra;
+
+ /* Size of @i_extra buffer in bytes. If 0, there is no extra data. */
+ size_t i_extra_size;
+
+ /* Creation time, last access time, and last write time for this inode,
+ * in 100-nanosecond intervals since 12:00 a.m UTC January 1, 1601.
+ * They should correspond to the times gotten by calling GetFileTime()
+ * on Windows. */
u64 i_creation_time;
u64 i_last_access_time;
u64 i_last_write_time;
/* Corresponds to 'security_id' in `struct wim_dentry_on_disk': The
* index of this inode's security descriptor in the WIM image's table of
- * security descriptors, or -1. Note: in verify_inode(), called
- * whenever a WIM image is loaded, out-of-bounds indices are set to -1,
- * so the extraction code does not need to do bounds checks. */
+ * security descriptors, or -1. Note: when a WIM image is loaded,
+ * wimlib sets out-of-bounds indices and values less than -1 in this
+ * field to -1. So the extraction code need not do an upper bound check
+ * after checking for -1 (or equivalently < 0). */
int32_t i_security_id;
/* Identity of a reparse point. See
* to 0 otherwise. */
u64 i_devno;
+ /* Fields used only during extraction */
struct {
-
- /* Used only during image extraction: pointer to the first path
- * (malloc()ed buffer) at which this inode has been extracted.
- * Freed and set to NULL after the extraction is done (either
- * success or failure). */
- tchar *i_extracted_file;
-
- /** Used only during image extraction: "cookie" that
- * identifies this extracted file (inode), for example
- * an inode number. Only used if supported by the
- * extraction mode. */
- u64 extract_cookie;
+ /* List of aliases of this dentry that are being
+ * extracted in the current extraction operation. This
+ * will be a (possibly nonproper) subset of the dentries
+ * in the i_dentry list. This list will be constructed
+ * regardless of whether the extraction backend supports
+ * hard links or not. */
+ struct list_head i_extraction_aliases;
+
+ #ifdef WITH_NTFS_3G
+ /* In NTFS-3g extraction mode, this is set to the Master
+ * File Table (MFT) number of the NTFS file that was
+ * created for this inode. */
+ u64 i_mft_no;
+ #endif
};
+ /* Used during WIM writing with
+ * WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES: the number
+ * of data streams this inode has that have not yet been fully
+ * read. */
+ u32 num_remaining_streams;
+
#ifdef WITH_FUSE
- /* Used only during image mount: Table of file descriptors that
- * have been opened to this inode. The table is automatically
- * freed when the last file descriptor is closed. */
- struct wimfs_fd **i_fds;
+ struct {
+ /* Used only during image mount: Table of file
+ * descriptors that have been opened to this inode.
+ * This table is freed when the last file descriptor is
+ * closed. */
+ struct wimfs_fd **i_fds;
+
+ /* Lower bound on the index of the next available entry
+ * in 'i_fds'. */
+ u16 i_next_fd;
+ };
#endif
};
/* WIM alternate data stream entry (on-disk format) */
struct wim_ads_entry_on_disk {
- /* Length of the entry, in bytes. This apparently includes all
- * fixed-length fields, plus the stream name and null terminator if
- * present, and the padding up to an 8 byte boundary. wimlib is a
- * little less strict when reading the entries, and only requires that
- * the number of bytes from this field is at least as large as the size
- * of the fixed length fields and stream name without null terminator.
- * */
- le64 length;
+ /* Length of the entry, in bytes. This includes all fixed-length
+ * fields, plus the stream name and null terminator if present, and the
+ * padding up to an 8 byte boundary. wimlib is a little less strict
+ * when reading the entries, and only requires that the number of bytes
+ * from this field is at least as large as the size of the fixed length
+ * fields and stream name without null terminator. */
+ le64 length;
- le64 reserved;
+ le64 reserved;
/* SHA1 message digest of the uncompressed stream; or, alternatively,
- * can be all zeroes if the stream has zero length. */
+ * can be all zeroes if the stream has zero length. */
u8 hash[SHA1_HASH_SIZE];
/* Length of the stream name, in bytes. 0 if the stream is unnamed. */
le16 stream_name_nbytes;
/* Stream name in UTF-16LE. It is @stream_name_nbytes bytes long,
- * excluding the the null terminator. There is a null terminator
- * character if @stream_name_nbytes != 0; i.e., if this stream is named.
- * */
+ * excluding the null terminator. There is a null terminator character
+ * if @stream_name_nbytes != 0; i.e., if this stream is named. */
utf16lechar stream_name[];
} _packed_attribute;
extern struct wim_inode *
new_timeless_inode(void) _malloc_attribute;
-extern void
-put_inode(struct wim_inode *inode);
-
-extern void
-free_inode(struct wim_inode *inode);
-
-/* Iterate through each alias of an inode. */
+/* Iterate through each alias of the specified inode. */
#define inode_for_each_dentry(dentry, inode) \
- list_for_each_entry((dentry), &(inode)->i_dentry, d_alias)
+ list_for_each_entry((dentry), &(inode)->i_dentry, d_alias)
-/* Add a new alias for an inode. Does not increment i_nlink; that must be done
- * separately. */
-#define inode_add_dentry(dentry, inode) \
- list_add_tail(&(dentry)->d_alias, &(inode)->i_dentry)
-
-/* Return an alias of an inode. */
+/* Return an alias of the specified inode. */
#define inode_first_dentry(inode) \
- container_of(inode->i_dentry.next, struct wim_dentry, d_alias)
+ container_of(inode->i_dentry.next, struct wim_dentry, d_alias)
-/* Return the full path of an alias of an inode, or NULL if it could not be
- * determined. */
+/* Return the full path of an alias of the specified inode, or NULL if a full
+ * path could not be determined. */
#define inode_first_full_path(inode) \
- dentry_full_path(inode_first_dentry(inode))
-
-extern struct wim_ads_entry *
-inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
- u16 *idx_ret);
-
-extern struct wim_ads_entry *
-inode_add_ads_utf16le(struct wim_inode *inode,
- const utf16lechar *stream_name,
- size_t stream_name_nbytes);
-
-extern struct wim_ads_entry *
-inode_add_ads(struct wim_inode *dentry, const tchar *stream_name);
-
-extern int
-inode_add_ads_with_data(struct wim_inode *inode, const tchar *name,
- const void *value, size_t size,
- struct wim_lookup_table *lookup_table);
-
-extern bool
-inode_has_named_stream(const struct wim_inode *inode);
-
-extern int
-inode_set_unnamed_stream(struct wim_inode *inode, const void *data, size_t len,
- struct wim_lookup_table *lookup_table);
+ dentry_full_path(inode_first_dentry(inode))
extern void
-inode_remove_ads(struct wim_inode *inode, u16 idx,
- struct wim_lookup_table *lookup_table);
+d_associate(struct wim_dentry *dentry, struct wim_inode *inode);
-static inline bool
-ads_entry_is_unix_data(const struct wim_ads_entry *entry)
-{
- return (entry->stream_name_nbytes ==
- WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES) &&
- !memcmp(entry->stream_name, WIMLIB_UNIX_DATA_TAG_UTF16LE,
- WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES);
-}
+extern void
+d_disassociate(struct wim_dentry *dentry);
-static inline bool
-ads_entry_is_named_stream(const struct wim_ads_entry *entry)
-{
- return entry->stream_name_nbytes != 0 && !ads_entry_is_unix_data(entry);
-}
+#ifdef WITH_FUSE
+extern void
+inode_dec_num_opened_fds(struct wim_inode *inode);
+#endif
/* Is the inode a directory?
* This doesn't count directories with reparse data.
}
/* Is the inode a directory with the encrypted attribute set?
- * This currently returns true for encrypted directories even if they have
- * reparse data (not sure if such files can even exist). */
+ * This returns true for encrypted directories even if they have reparse data
+ * (I'm not sure if such files can even exist!). */
static inline bool
inode_is_encrypted_directory(const struct wim_inode *inode)
{
/* Does the inode have children?
* Currently (based on read_dentry_tree()), this can only return true for inodes
- * for which inode_is_directory() returns true. However, if a directory is
- * empty, this returns false. */
+ * for which inode_is_directory() returns true. (This also returns false on
+ * empty directories.) */
static inline bool
inode_has_children(const struct wim_inode *inode)
{
- return inode->i_children.rb_node != NULL;
+ return inode->i_children != NULL;
}
+extern struct wim_ads_entry *
+inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name);
+
+extern struct wim_ads_entry *
+inode_add_ads_utf16le(struct wim_inode *inode, const utf16lechar *stream_name,
+ size_t stream_name_nbytes);
+
+extern struct wim_ads_entry *
+inode_add_ads(struct wim_inode *dentry, const tchar *stream_name);
+
+extern 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);
+
+extern void
+inode_remove_ads(struct wim_inode *inode, struct wim_ads_entry *entry,
+ struct wim_lookup_table *lookup_table);
+
+extern bool
+inode_has_named_stream(const struct wim_inode *inode);
+
extern int
-inode_resolve_streams(struct wim_inode *inode, struct wim_lookup_table *table,
- bool force);
+inode_set_unnamed_stream(struct wim_inode *inode, const void *data, size_t len,
+ struct wim_lookup_table *lookup_table);
extern int
-stream_not_found_error(const struct wim_inode *inode, const u8 *hash);
+inode_resolve_streams(struct wim_inode *inode, struct wim_lookup_table *table,
+ bool force);
extern void
inode_unresolve_streams(struct wim_inode *inode);
+extern int
+stream_not_found_error(const struct wim_inode *inode, const u8 *hash);
+
static inline struct wim_lookup_table_entry *
inode_stream_lte_resolved(const struct wim_inode *inode, unsigned stream_idx)
{
- wimlib_assert(inode->i_resolved);
- wimlib_assert(stream_idx <= inode->i_num_ads);
if (stream_idx == 0)
return inode->i_lte;
- else
- return inode->i_ads_entries[stream_idx - 1].lte;
-}
-
-static inline struct wim_lookup_table_entry *
-inode_stream_lte_unresolved(const struct wim_inode *inode, unsigned stream_idx,
- const struct wim_lookup_table *table)
-{
- wimlib_assert(!inode->i_resolved);
- wimlib_assert(stream_idx <= inode->i_num_ads);
- if (table == NULL)
- return NULL;
- if (stream_idx == 0)
- return lookup_stream(table, inode->i_hash);
- else
- return lookup_stream(table, inode->i_ads_entries[ stream_idx - 1].hash);
+ return inode->i_ads_entries[stream_idx - 1].lte;
}
extern struct wim_lookup_table_entry *
inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx,
const struct wim_lookup_table *table);
-static inline const u8 *
-inode_stream_hash_unresolved(const struct wim_inode *inode, unsigned stream_idx)
+extern struct wim_lookup_table_entry *
+inode_unnamed_stream_resolved(const struct wim_inode *inode,
+ unsigned *stream_idx_ret);
+
+static inline struct wim_lookup_table_entry *
+inode_unnamed_lte_resolved(const struct wim_inode *inode)
{
- wimlib_assert(!inode->i_resolved);
- wimlib_assert(stream_idx <= inode->i_num_ads);
- if (stream_idx == 0)
- return inode->i_hash;
- else
- return inode->i_ads_entries[stream_idx - 1].hash;
+ unsigned stream_idx;
+ return inode_unnamed_stream_resolved(inode, &stream_idx);
}
+extern struct wim_lookup_table_entry *
+inode_unnamed_lte(const struct wim_inode *inode,
+ const struct wim_lookup_table *table);
-static inline const u8 *
-inode_stream_hash_resolved(const struct wim_inode *inode, unsigned stream_idx)
-{
- struct wim_lookup_table_entry *lte;
- lte = inode_stream_lte_resolved(inode, stream_idx);
- if (lte)
- return lte->hash;
- else
- return zero_hash;
-}
+extern const u8 *
+inode_stream_hash(const struct wim_inode *inode, unsigned stream_idx);
-/*
- * Returns the hash 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 dentries.
- */
-static inline const u8 *
-inode_stream_hash(const struct wim_inode *inode, unsigned stream_idx)
-{
- if (inode->i_resolved)
- return inode_stream_hash_resolved(inode, stream_idx);
- else
- return inode_stream_hash_unresolved(inode, stream_idx);
-}
+extern const u8 *
+inode_unnamed_stream_hash(const struct wim_inode *inode);
-static inline u16
+static inline unsigned
inode_stream_name_nbytes(const struct wim_inode *inode, unsigned stream_idx)
{
- wimlib_assert(stream_idx <= inode->i_num_ads);
if (stream_idx == 0)
return 0;
- else
- return inode->i_ads_entries[stream_idx - 1].stream_name_nbytes;
+ return inode->i_ads_entries[stream_idx - 1].stream_name_nbytes;
}
-extern struct wim_lookup_table_entry *
-inode_unnamed_stream_resolved(const struct wim_inode *inode, u16 *stream_idx_ret);
-
-extern struct wim_lookup_table_entry *
-inode_unnamed_lte_resolved(const struct wim_inode *inode);
-
-extern struct wim_lookup_table_entry *
-inode_unnamed_lte_unresolved(const struct wim_inode *inode,
- const struct wim_lookup_table *table);
+static inline u32
+inode_stream_idx_to_id(const struct wim_inode *inode, unsigned stream_idx)
+{
+ if (stream_idx == 0)
+ return 0;
+ return inode->i_ads_entries[stream_idx - 1].stream_id;
+}
-extern struct wim_lookup_table_entry *
-inode_unnamed_lte(const struct wim_inode *inode, const struct wim_lookup_table *table);
+extern void
+inode_ref_streams(struct wim_inode *inode);
-extern const u8 *
-inode_unnamed_stream_hash(const struct wim_inode *inode);
+extern void
+inode_unref_streams(struct wim_inode *inode,
+ struct wim_lookup_table *lookup_table);
extern int
read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode,
- size_t nbytes_remaining);
-
-extern int
-verify_inode(struct wim_inode *inode, const struct wim_security_data *sd);
+ size_t *nbytes_remaining_p);
extern void
-inode_ref_streams(struct wim_inode *inode);
+check_inode(struct wim_inode *inode, const struct wim_security_data *sd);
/* inode_fixup.c */
extern int