src/export_image.c \
src/extract.c \
src/file_io.c \
- src/hardlink.c \
src/header.c \
+ src/inode.c \
+ src/inode_fixup.c \
src/integrity.c \
+ src/iterate_dir.c \
src/join.c \
src/lookup_table.c \
src/lzms-common.c \
src/paths.c \
src/resource.c \
src/rbtree.c \
+ src/reference.c \
src/security.c \
src/sha1.c \
src/split.c \
src/reparse.c \
+ src/template.c \
src/timestamp.c \
src/update_image.c \
src/util.c \
include/wimlib/assert.h \
include/wimlib/callback.h \
include/wimlib/capture.h \
+ include/wimlib/case.h \
include/wimlib/compiler.h \
include/wimlib/compressor_ops.h \
include/wimlib/compress_common.h \
include/wimlib/file_io.h \
include/wimlib/glob.h \
include/wimlib/header.h \
+ include/wimlib/inode.h \
+ include/wimlib/inode_table.h \
include/wimlib/integrity.h \
include/wimlib/list.h \
include/wimlib/lookup_table.h \
include/wimlib/sha1.h \
include/wimlib/timestamp.h \
include/wimlib/types.h \
+ include/wimlib/unix_data.h \
include/wimlib/util.h \
include/wimlib/version.h \
include/wimlib/wildcard.h \
#define _WIMLIB_CAPTURE_H
#include "wimlib.h"
+#include "wimlib/inode_table.h"
#include "wimlib/list.h"
#include "wimlib/security.h"
#include "wimlib/util.h"
struct wim_dentry;
struct wim_inode;
-/* Hash table to find inodes, given an inode number (in the case of reading
- * a WIM images), or both an inode number and a device number (in the case of
- * capturing a WIM image). */
-struct wim_inode_table {
- /* Fields for the hash table */
- struct hlist_head *array;
- u64 num_entries;
- u64 capacity;
-
- /*
- * Linked list of "extra" inodes. These may be:
- *
- * - inodes with link count 1, which are all allowed to have 0 for their
- * inode number, meaning we cannot insert them into the hash table.
- *
- * - Groups we create ourselves by splitting a nominal inode due to
- * inconsistencies in the dentries. These inodes will share an inode
- * number with some other inode until assign_inode_numbers() is
- * called.
- */
- struct list_head extra_inodes;
-};
-
/* Common parameters to implementations of building an in-memory dentry tree
* from an on-disk directory structure. */
struct add_image_params {
- /* Pointer to the lookup table of the WIM. */
+ /* Pointer to the lookup table of the WIM. */
struct wim_lookup_table *lookup_table;
+ /* List of streams that have been added so far, but without their SHA1
+ * message digests being calculated (as a shortcut). */
+ struct list_head *unhashed_streams;
+
/* Hash table of inodes that have been captured for this tree so far. */
struct wim_inode_table inode_table;
inode_table_prepare_inode_list(struct wim_inode_table *table,
struct list_head *head);
-static inline void
-destroy_inode_table(struct wim_inode_table *table)
-{
- FREE(table->array);
-}
-
#ifdef WITH_NTFS_3G
/* ntfs-3g_capture.c */
--- /dev/null
+#ifndef _WIMLIB_CASE_H
+#define _WIMLIB_CASE_H
+
+#include <stdbool.h>
+
+/* Note: the NTFS-3g headers define CASE_SENSITIVE, hence the WIMLIB prefix. */
+typedef enum {
+ /* Use either case-sensitive or case-insensitive search, depending on
+ * the variable @default_ignore_case. */
+ WIMLIB_CASE_PLATFORM_DEFAULT = 0,
+
+ /* Use case-sensitive search. */
+ WIMLIB_CASE_SENSITIVE = 1,
+
+ /* Use case-insensitive search. */
+ WIMLIB_CASE_INSENSITIVE = 2,
+} CASE_SENSITIVITY_TYPE;
+
+extern bool default_ignore_case;
+
+static inline bool
+will_ignore_case(CASE_SENSITIVITY_TYPE case_type)
+{
+ if (case_type == WIMLIB_CASE_SENSITIVE)
+ return false;
+ if (case_type == WIMLIB_CASE_INSENSITIVE)
+ return true;
+
+ return default_ignore_case;
+}
+
+#endif /* _WIMLIB_CASE_H */
#ifndef _WIMLIB_DENTRY_H
#define _WIMLIB_DENTRY_H
+#include "wimlib/case.h"
#include "wimlib/compiler.h"
+#include "wimlib/inode.h"
#include "wimlib/list.h"
#include "wimlib/rbtree.h"
-#include "wimlib/sha1.h"
#include "wimlib/types.h"
-#include <string.h>
-#include <sys/types.h> /* uid_t, gid_t */
-
+struct wim_inode;
struct wim_lookup_table;
struct wim_lookup_table_entry;
-struct wimfs_fd;
-struct wim_inode;
struct wim_security_data;
-/* Size of the struct wim_dentry up to and including the file_name_len. */
-#define WIM_DENTRY_DISK_SIZE 102
-
-/*
- * Reparse tags documented at
- * http://msdn.microsoft.com/en-us/library/dd541667(v=prot.10).aspx
- */
-#define WIM_IO_REPARSE_TAG_RESERVED_ZERO 0x00000000
-#define WIM_IO_REPARSE_TAG_RESERVED_ONE 0x00000001
-#define WIM_IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
-#define WIM_IO_REPARSE_TAG_HSM 0xC0000004
-#define WIM_IO_REPARSE_TAG_HSM2 0x80000006
-#define WIM_IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005
-#define WIM_IO_REPARSE_TAG_SIS 0x80000007
-#define WIM_IO_REPARSE_TAG_DFS 0x8000000A
-#define WIM_IO_REPARSE_TAG_DFSR 0x80000012
-#define WIM_IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B
-#define WIM_IO_REPARSE_TAG_SYMLINK 0xA000000C
-
-#define FILE_ATTRIBUTE_READONLY 0x00000001
-#define FILE_ATTRIBUTE_HIDDEN 0x00000002
-#define FILE_ATTRIBUTE_SYSTEM 0x00000004
-#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
-#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
-#define FILE_ATTRIBUTE_DEVICE 0x00000040
-#define FILE_ATTRIBUTE_NORMAL 0x00000080
-#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
-#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200
-#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
-#define FILE_ATTRIBUTE_COMPRESSED 0x00000800
-#define FILE_ATTRIBUTE_OFFLINE 0x00001000
-#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
-#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000
-#define FILE_ATTRIBUTE_VIRTUAL 0x00010000
-
-
-/* Alternate data stream entry.
- *
- * We read this from disk in the read_ads_entries() function; see that function
- * for more explanation. */
-struct wim_ads_entry {
- union {
- /* SHA-1 message digest of stream contents */
- u8 hash[SHA1_HASH_SIZE];
-
- /* The corresponding lookup table entry (only for resolved
- * streams) */
- struct wim_lookup_table_entry *lte;
- };
-
- /* Length of UTF16-encoded stream name, in bytes, not including the
- * terminating null character; or 0 if the stream is unnamed. */
- u16 stream_name_nbytes;
-
- /* Number to identify an alternate data stream even after it's possibly
- * been moved or renamed. */
- u32 stream_id;
-
- /* Stream name (UTF-16LE), null-terminated, or NULL if the stream is
- * unnamed. */
- utf16lechar *stream_name;
-
- /* Reserved field. We read it into memory so we can write it out
- * unchanged. */
- u64 reserved;
-};
-
-
-static inline bool
-ads_entries_have_same_name(const struct wim_ads_entry *entry_1,
- const struct wim_ads_entry *entry_2)
-{
- return entry_1->stream_name_nbytes == entry_2->stream_name_nbytes &&
- memcmp(entry_1->stream_name, entry_2->stream_name,
- entry_1->stream_name_nbytes) == 0;
-}
+/* Base size of a WIM dentry in the on-disk format, up to and including the file
+ * name length. This does not include the variable-length file name, short
+ * name, alternate data stream entries, and padding to 8-byte boundaries. */
+#define WIM_DENTRY_DISK_SIZE 102
/*
* In-memory structure for a WIM directory entry (dentry). There is a directory
#define rbnode_dentry(node) container_of(node, struct wim_dentry, rb_node)
-/*
- * WIM inode.
- *
- * As mentioned in the comment above `struct wim_dentry', in the WIM file that
- * 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.
- */
-struct wim_inode {
- /* If i_resolved == 0:
- * SHA1 message digest of the contents of the unnamed-data stream
- * of this inode.
- *
- * If i_resolved == 1:
- * Pointer to the lookup table entry for the unnamed data stream
- * of this inode, or NULL.
- *
- * i_hash corresponds to the 'unnamed_stream_hash' field of the `struct
- * wim_dentry_on_disk' and the additional caveats documented about that
- * field apply here (for example, the quirks regarding all-zero hashes).
- */
- union {
- u8 i_hash[SHA1_HASH_SIZE];
- struct wim_lookup_table_entry *i_lte;
- };
-
- /* Corresponds to the 'attributes' field of `struct wim_dentry_on_disk';
- * bitwise OR of the FILE_ATTRIBUTE_* flags that give the attributes of
- * 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 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;
-
- /* List of dentries that are aliases for this inode. There will be
- * i_nlink dentries in this list. */
- struct list_head i_dentry;
-
- /* 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. */
- 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. */
- struct list_head i_list;
- };
-
- /* Number of dentries that are aliases for this inode. */
- u32 i_nlink;
-
- /* Number of alternate data streams (ADS) associated with this inode */
- u16 i_num_ads;
-
- /* Flag that indicates whether this inode's streams have been
- * "resolved". By default, the inode starts as "unresolved", meaning
- * that the i_hash field, along with the hash field of any associated
- * wim_ads_entry's, are valid and should be used as keys in the WIM
- * lookup table to find the associated `struct wim_lookup_table_entry'.
- * But if the inode has been resolved, then each of these fields is
- * replaced with a pointer directly to the appropriate `struct
- * wim_lookup_table_entry', or NULL if the stream is empty. */
- u8 i_resolved : 1;
-
- /* Flag used to mark this inode as visited; this is used when visiting
- * all the inodes in a dentry tree exactly once. It will be 0 by
- * default and must be cleared following the tree traversal, even in
- * 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;
-
- /* 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. */
- 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. */
- int32_t i_security_id;
-
- /* Identity of a reparse point. See
- * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365503(v=vs.85).aspx
- * for what a reparse point is. */
- u32 i_reparse_tag;
-
- /* Unused/unknown fields that we just read into memory so we can
- * re-write them unchanged. */
- u32 i_rp_unknown_1;
- u16 i_rp_unknown_2;
-
- /* Corresponds to not_rpfixed in `struct wim_dentry_on_disk': Set to 0
- * if reparse point fixups have been done. Otherwise set to 1. Note:
- * this actually may reflect the SYMBOLIC_LINK_RELATIVE flag.
- */
- u16 i_not_rpfixed;
-
- /* Inode number; corresponds to hard_link_group_id in the `struct
- * wim_dentry_on_disk'. */
- u64 i_ino;
-
- union {
- /* Device number, used only during image capture, so we can
- * identify hard linked files by the combination of inode number
- * and device number (rather than just inode number, which could
- * be ambigious if the captured tree spans a mountpoint). Set
- * to 0 otherwise. */
- u64 i_devno;
-
- 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;
- };
-
-#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;
-#endif
- };
-
-#ifdef WITH_FUSE
- u16 i_num_opened_fds;
- u16 i_num_allocated_fds;
-#endif
-
- /* Next alternate data stream ID to be assigned */
- u32 i_next_stream_id;
-};
-
-#define inode_for_each_dentry(dentry, inode) \
- list_for_each_entry((dentry), &(inode)->i_dentry, d_alias)
-
-#define inode_add_dentry(dentry, inode) \
- list_add_tail(&(dentry)->d_alias, &(inode)->i_dentry)
-
-#define inode_first_dentry(inode) \
- container_of(inode->i_dentry.next, struct wim_dentry, d_alias)
-
-#define inode_first_full_path(inode) \
- dentry_full_path(inode_first_dentry(inode))
-
static inline bool
dentry_is_first_in_inode(const struct wim_dentry *dentry)
{
calculate_subdir_offsets(struct wim_dentry *dentry, u64 *subdir_offset_p);
extern int
-set_dentry_name(struct wim_dentry *dentry, const tchar *new_name);
-
-
-/* Note: the NTFS-3g headers define CASE_SENSITIVE, hence the WIMLIB prefix. */
-typedef enum {
- /* Use either case-sensitive or case-insensitive search, depending on
- * the variable @default_ignore_case. */
- WIMLIB_CASE_PLATFORM_DEFAULT = 0,
-
- /* Use case-sensitive search. */
- WIMLIB_CASE_SENSITIVE = 1,
-
- /* Use case-insensitive search. */
- WIMLIB_CASE_INSENSITIVE = 2,
-} CASE_SENSITIVITY_TYPE;
-
-extern bool default_ignore_case;
+dentry_set_name(struct wim_dentry *dentry, const tchar *new_name);
extern struct wim_dentry *
get_dentry(struct WIMStruct *wim, const tchar *path,
get_parent_dentry(struct WIMStruct *wim, const tchar *path,
CASE_SENSITIVITY_TYPE case_type);
+#ifdef WITH_FUSE
+
+#define LOOKUP_FLAG_ADS_OK 0x00000001
+#define LOOKUP_FLAG_DIRECTORY_OK 0x00000002
+
+extern int
+wim_pathname_to_stream(WIMStruct *wim,
+ const tchar *path,
+ int lookup_flags,
+ struct wim_dentry **dentry_ret,
+ struct wim_lookup_table_entry **lte_ret,
+ u16 *stream_idx_ret);
+#endif
+
extern int
print_dentry(struct wim_dentry *dentry, void *lookup_table);
extern tchar *
dentry_full_path(struct wim_dentry *dentry);
-extern struct wim_inode *
-new_timeless_inode(void) _malloc_attribute;
-
extern int
new_dentry(const tchar *name, struct wim_dentry **dentry_ret);
extern int
new_filler_directory(const tchar *name, struct wim_dentry **dentry_ret);
-extern void
-free_inode(struct wim_inode *inode);
-
extern void
free_dentry(struct wim_dentry *dentry);
rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to,
CASE_SENSITIVITY_TYPE case_type);
-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);
-
-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);
-
-extern void
-inode_remove_ads(struct wim_inode *inode, u16 idx,
- struct wim_lookup_table *lookup_table);
-
-
-#define WIMLIB_UNIX_DATA_TAG "$$__wimlib_UNIX_data"
-#define WIMLIB_UNIX_DATA_TAG_NBYTES (sizeof(WIMLIB_UNIX_DATA_TAG) - 1)
-
-#define WIMLIB_UNIX_DATA_TAG_UTF16LE "$\0$\0_\0_\0w\0i\0m\0l\0i\0b\0_\0U\0N\0I\0X\0_\0d\0a\0t\0a\0"
-#define WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES (sizeof(WIMLIB_UNIX_DATA_TAG_UTF16LE) - 1)
-
-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);
-}
-
-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);
-}
-
-#ifndef __WIN32__
-/* Format for special alternate data stream entries to store UNIX data for files
- * and directories (see: WIMLIB_ADD_FLAG_UNIX_DATA) */
-struct wimlib_unix_data {
- u16 version; /* Must be 0 */
- u16 uid;
- u16 gid;
- u16 mode;
-} _packed_attribute;
-
-#define NO_UNIX_DATA (-1)
-#define BAD_UNIX_DATA (-2)
-extern int
-inode_get_unix_data(const struct wim_inode *inode,
- struct wimlib_unix_data *unix_data,
- u16 *stream_idx_ret);
-
-#define UNIX_DATA_UID 0x1
-#define UNIX_DATA_GID 0x2
-#define UNIX_DATA_MODE 0x4
-#define UNIX_DATA_ALL (UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE)
-#define UNIX_DATA_CREATE 0x8
-extern int
-inode_set_unix_data(struct wim_inode *inode, uid_t uid, gid_t gid, mode_t mode,
- struct wim_lookup_table *lookup_table, int which);
-#endif /* !__WIN32__ */
-
-extern bool
-inode_has_unix_data(const struct wim_inode *inode);
extern int
read_dentry(const u8 * restrict metadata_resource,
return dentry->parent == dentry;
}
-static inline bool
-inode_is_directory(const struct wim_inode *inode)
-{
- return (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
- FILE_ATTRIBUTE_REPARSE_POINT))
- == FILE_ATTRIBUTE_DIRECTORY;
-}
-
-static inline bool
-inode_is_encrypted_directory(const struct wim_inode *inode)
-{
- return ((inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
- FILE_ATTRIBUTE_ENCRYPTED))
- == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ENCRYPTED));
-}
-
static inline bool
dentry_is_directory(const struct wim_dentry *dentry)
{
return inode_is_directory(dentry->d_inode);
}
-/* For our purposes, we consider "real" symlinks and "junction points" to both
- * be symlinks. */
-static inline bool
-inode_is_symlink(const struct wim_inode *inode)
-{
- return (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
- && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
- inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
-}
-
-static inline bool
-inode_has_children(const struct wim_inode *inode)
-{
- return inode->i_children.rb_node != NULL;
-}
-
static inline bool
dentry_has_children(const struct wim_dentry *dentry)
{
return dentry->file_name_nbytes != 0;
}
-extern void
-inode_ref_streams(struct wim_inode *inode);
-
extern int
dentry_tree_fix_inodes(struct wim_dentry *root, struct list_head *inode_list);
-
-extern int
-verify_inode(struct wim_inode *inode, const struct wim_security_data *sd);
-
#endif /* _WIMLIB_DENTRY_H */
const utf16lechar *s2, size_t n2,
bool ignore_case);
+extern int
+get_utf16le_string(const tchar *name, utf16lechar **name_utf16le_ret,
+ u16 *name_utf16le_nbytes_ret);
+
+
#endif /* _WIMLIB_ENCODING_H */
--- /dev/null
+#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>
+
+struct wim_ads_entry;
+struct wim_dentry;
+struct wim_security_data;
+struct wim_lookup_table;
+struct wimfs_fd;
+
+/*
+ * WIM inode.
+ *
+ * As mentioned in the comment above `struct wim_dentry', in the WIM file that
+ * 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.
+ */
+struct wim_inode {
+ /* If i_resolved == 0:
+ * SHA1 message digest of the contents of the unnamed-data stream
+ * of this inode.
+ *
+ * If i_resolved == 1:
+ * Pointer to the lookup table entry for the unnamed data stream
+ * of this inode, or NULL.
+ *
+ * i_hash corresponds to the 'unnamed_stream_hash' field of the `struct
+ * wim_dentry_on_disk' and the additional caveats documented about that
+ * field apply here (for example, the quirks regarding all-zero hashes).
+ */
+ union {
+ u8 i_hash[SHA1_HASH_SIZE];
+ struct wim_lookup_table_entry *i_lte;
+ };
+
+ /* Corresponds to the 'attributes' field of `struct wim_dentry_on_disk';
+ * bitwise OR of the FILE_ATTRIBUTE_* flags that give the attributes of
+ * 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 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;
+
+ /* List of dentries that are aliases for this inode. There will be
+ * i_nlink dentries in this list. */
+ struct list_head i_dentry;
+
+ /* 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. */
+ 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. */
+ struct list_head i_list;
+ };
+
+ /* Number of dentries that are aliases for this inode. */
+ u32 i_nlink;
+
+ /* Number of alternate data streams (ADS) associated with this inode */
+ u16 i_num_ads;
+
+ /* Flag that indicates whether this inode's streams have been
+ * "resolved". By default, the inode starts as "unresolved", meaning
+ * that the i_hash field, along with the hash field of any associated
+ * wim_ads_entry's, are valid and should be used as keys in the WIM
+ * lookup table to find the associated `struct wim_lookup_table_entry'.
+ * But if the inode has been resolved, then each of these fields is
+ * replaced with a pointer directly to the appropriate `struct
+ * wim_lookup_table_entry', or NULL if the stream is empty. */
+ u8 i_resolved : 1;
+
+ /* Flag used to mark this inode as visited; this is used when visiting
+ * all the inodes in a dentry tree exactly once. It will be 0 by
+ * default and must be cleared following the tree traversal, even in
+ * 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;
+
+ /* 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. */
+ 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. */
+ int32_t i_security_id;
+
+ /* Identity of a reparse point. See
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365503(v=vs.85).aspx
+ * for what a reparse point is. */
+ u32 i_reparse_tag;
+
+ /* Unused/unknown fields that we just read into memory so we can
+ * re-write them unchanged. */
+ u32 i_rp_unknown_1;
+ u16 i_rp_unknown_2;
+
+ /* Corresponds to not_rpfixed in `struct wim_dentry_on_disk': Set to 0
+ * if reparse point fixups have been done. Otherwise set to 1. Note:
+ * this actually may reflect the SYMBOLIC_LINK_RELATIVE flag.
+ */
+ u16 i_not_rpfixed;
+
+ /* Inode number; corresponds to hard_link_group_id in the `struct
+ * wim_dentry_on_disk'. */
+ u64 i_ino;
+
+ union {
+ /* Device number, used only during image capture, so we can
+ * identify hard linked files by the combination of inode number
+ * and device number (rather than just inode number, which could
+ * be ambigious if the captured tree spans a mountpoint). Set
+ * to 0 otherwise. */
+ u64 i_devno;
+
+ 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;
+ };
+
+#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;
+#endif
+ };
+
+#ifdef WITH_FUSE
+ u16 i_num_opened_fds;
+ u16 i_num_allocated_fds;
+#endif
+
+ /* Next alternate data stream ID to be assigned */
+ u32 i_next_stream_id;
+};
+
+/* Alternate data stream entry.
+ *
+ * We read this from disk in the read_ads_entries() function; see that function
+ * for more explanation. */
+struct wim_ads_entry {
+ union {
+ /* SHA-1 message digest of stream contents */
+ u8 hash[SHA1_HASH_SIZE];
+
+ /* The corresponding lookup table entry (only for resolved
+ * streams) */
+ struct wim_lookup_table_entry *lte;
+ };
+
+ /* Length of UTF16-encoded stream name, in bytes, not including the
+ * terminating null character; or 0 if the stream is unnamed. */
+ u16 stream_name_nbytes;
+
+ /* Number to identify an alternate data stream even after it's possibly
+ * been moved or renamed. */
+ u32 stream_id;
+
+ /* Stream name (UTF-16LE), null-terminated, or NULL if the stream is
+ * unnamed. */
+ utf16lechar *stream_name;
+
+ /* Reserved field. We read it into memory so we can write it out
+ * unchanged. */
+ u64 reserved;
+};
+
+/* 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;
+
+ le64 reserved;
+
+ /* SHA1 message digest of the uncompressed stream; or, alternatively,
+ * 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.
+ * */
+ utf16lechar stream_name[];
+} _packed_attribute;
+
+#define WIM_ADS_ENTRY_DISK_SIZE 38
+
+/*
+ * Reparse tags documented at
+ * http://msdn.microsoft.com/en-us/library/dd541667(v=prot.10).aspx
+ */
+#define WIM_IO_REPARSE_TAG_RESERVED_ZERO 0x00000000
+#define WIM_IO_REPARSE_TAG_RESERVED_ONE 0x00000001
+#define WIM_IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
+#define WIM_IO_REPARSE_TAG_HSM 0xC0000004
+#define WIM_IO_REPARSE_TAG_HSM2 0x80000006
+#define WIM_IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005
+#define WIM_IO_REPARSE_TAG_SIS 0x80000007
+#define WIM_IO_REPARSE_TAG_DFS 0x8000000A
+#define WIM_IO_REPARSE_TAG_DFSR 0x80000012
+#define WIM_IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B
+#define WIM_IO_REPARSE_TAG_SYMLINK 0xA000000C
+
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#define FILE_ATTRIBUTE_HIDDEN 0x00000002
+#define FILE_ATTRIBUTE_SYSTEM 0x00000004
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
+#define FILE_ATTRIBUTE_DEVICE 0x00000040
+#define FILE_ATTRIBUTE_NORMAL 0x00000080
+#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
+#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200
+#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
+#define FILE_ATTRIBUTE_COMPRESSED 0x00000800
+#define FILE_ATTRIBUTE_OFFLINE 0x00001000
+#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
+#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000
+#define FILE_ATTRIBUTE_VIRTUAL 0x00010000
+
+extern struct wim_inode *
+new_inode(void) _malloc_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. */
+#define inode_for_each_dentry(dentry, inode) \
+ 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. */
+#define inode_first_dentry(inode) \
+ 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. */
+#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);
+
+extern void
+inode_remove_ads(struct wim_inode *inode, u16 idx,
+ struct wim_lookup_table *lookup_table);
+
+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);
+}
+
+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);
+}
+
+/* Is the inode a directory?
+ * This doesn't count directories with reparse data.
+ * wimlib only allows inodes of this type to have children.
+ */
+static inline bool
+inode_is_directory(const struct wim_inode *inode)
+{
+ return (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_REPARSE_POINT))
+ == FILE_ATTRIBUTE_DIRECTORY;
+}
+
+/* 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). */
+static inline bool
+inode_is_encrypted_directory(const struct wim_inode *inode)
+{
+ return ((inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_ENCRYPTED))
+ == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ENCRYPTED));
+}
+
+/* Is the inode a symbolic link?
+ * This returns true iff the inode is a reparse point that is either a "real"
+ * symbolic link or a junction point. */
+static inline bool
+inode_is_symlink(const struct wim_inode *inode)
+{
+ return (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+ inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
+}
+
+/* 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. */
+static inline bool
+inode_has_children(const struct wim_inode *inode)
+{
+ return inode->i_children.rb_node != NULL;
+}
+
+extern int
+inode_resolve_streams(struct wim_inode *inode, struct wim_lookup_table *table,
+ bool force);
+
+extern int
+stream_not_found_error(const struct wim_inode *inode, const u8 *hash);
+
+extern void
+inode_unresolve_streams(struct wim_inode *inode);
+
+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);
+}
+
+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)
+{
+ 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;
+}
+
+
+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;
+}
+
+/*
+ * 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);
+}
+
+static inline u16
+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;
+}
+
+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);
+
+extern struct wim_lookup_table_entry *
+inode_unnamed_lte(const struct wim_inode *inode, const struct wim_lookup_table *table);
+
+extern const u8 *
+inode_unnamed_stream_hash(const struct wim_inode *inode);
+
+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);
+
+extern void
+inode_ref_streams(struct wim_inode *inode);
+
+/* inode_fixup.c */
+extern int
+dentry_tree_fix_inodes(struct wim_dentry *root, struct list_head *inode_list);
+
+#endif /* _WIMLIB_INODE_H */
--- /dev/null
+#ifndef _WIMLIB_INODE_TABLE_H
+#define _WIMLIB_INODE_TABLE_H
+
+#include "wimlib/list.h"
+#include "wimlib/types.h"
+
+struct wim_dentry;
+
+/* Hash table to find inodes, given an inode number (in the case of reading
+ * a WIM images), or both an inode number and a device number (in the case of
+ * capturing a WIM image). */
+struct wim_inode_table {
+ /* Fields for the hash table */
+ struct hlist_head *array;
+ size_t num_entries;
+ size_t capacity;
+
+ /*
+ * Linked list of "extra" inodes. These may be:
+ *
+ * - inodes with link count 1, which are all allowed to have 0 for their
+ * inode number, meaning we cannot insert them into the hash table.
+ *
+ * - Groups we create ourselves by splitting a nominal inode due to
+ * inconsistencies in the dentries. These inodes will share an inode
+ * number with some other inode until assign_inode_numbers() is
+ * called.
+ */
+ struct list_head extra_inodes;
+};
+
+
+extern int
+init_inode_table(struct wim_inode_table *table, size_t capacity);
+
+extern int
+inode_table_new_dentry(struct wim_inode_table *table, const tchar *name,
+ u64 ino, u64 devno, bool noshare,
+ struct wim_dentry **dentry_ret);
+
+extern void
+inode_table_prepare_inode_list(struct wim_inode_table *table,
+ struct list_head *head);
+
+extern void
+destroy_inode_table(struct wim_inode_table *table);
+
+#endif /* _WIMLIB_INODE_TABLE_H */
#ifndef _WIMLIB_LOOKUP_TABLE_H
#define _WIMLIB_LOOKUP_TABLE_H
-#include "wimlib/assert.h"
-#include "wimlib/dentry.h"
#include "wimlib/list.h"
+#include "wimlib/resource.h"
#include "wimlib/sha1.h"
#include "wimlib/types.h"
-#include "wimlib/wim.h"
-
-#define LOOKUP_FLAG_ADS_OK 0x00000001
-#define LOOKUP_FLAG_DIRECTORY_OK 0x00000002
-
-
-/* The lookup table of a WIM file maps SHA1 message digests to streams of data.
- * Here, the in-memory structure is implemented as a hash table.
- *
- * Given a SHA1 message digest, the mapped-to stream is specified by an offset
- * in the WIM, an uncompressed and compressed size, and resource flags (see
- * 'struct resource_entry'). But, we associate additional information, such as
- * a reference count, with each stream, so the actual mapping is from SHA1
- * message digests to 'struct wim_lookup_table_entry's, each of which contains
- * an embedded 'struct resource_entry'.
- *
- * Note: Everything will break horribly if there is a SHA1 collision.
- */
-struct wim_lookup_table {
- struct hlist_head *array;
- u64 num_entries;
- u64 capacity;
- struct list_head *unhashed_streams;
-};
-
-#ifdef WITH_NTFS_3G
-
-struct _ntfs_volume;
-
-struct ntfs_location {
- tchar *path;
- utf16lechar *stream_name;
- u16 stream_name_nchars;
- struct _ntfs_volume *ntfs_vol;
- bool is_reparse_point;
-};
-#endif
/* An enumerated type that identifies where the stream corresponding to this
* lookup table entry is actually located.
* RESOURCE_IN_WIM since all the streams will initially be located in the WIM.
* However, to handle situations such as image capture and image mount, we allow
* the actual location of the stream to be somewhere else, such as an external
- * file.
- */
+ * file. */
enum resource_location {
/* The lookup table entry does not yet correspond to a stream; this is a
* temporary state only. */
* appropriate Windows API. */
RESOURCE_WIN32_ENCRYPTED,
#endif
-
};
/* Specification for a stream, which may be the contents of a file (unnamed data
u32 out_refcnt;
#ifdef WITH_FUSE
- /* Number of times this stream has been opened; used only during
- * mounting. */
+ /* Number of open file descriptors to this stream during a FUSE mount of
+ * the containing image. */
u16 num_opened_fds;
#endif
struct list_head unhashed_list;
};
-static inline bool
-lte_is_partial(const struct wim_lookup_table_entry * lte)
-{
- return lte->resource_location == RESOURCE_IN_WIM &&
- lte->size != lte->rspec->uncompressed_size;
-}
-
-static inline bool
-lte_filename_valid(const struct wim_lookup_table_entry *lte)
-{
- return lte->resource_location == RESOURCE_IN_FILE_ON_DISK
- #ifdef __WIN32__
- || lte->resource_location == RESOURCE_WIN32_ENCRYPTED
- #endif
- #ifdef WITH_FUSE
- || lte->resource_location == RESOURCE_IN_STAGING_FILE
- #endif
- ;
-}
+/* Functions to allocate and free lookup tables */
extern struct wim_lookup_table *
new_lookup_table(size_t capacity) _malloc_attribute;
+extern void
+free_lookup_table(struct wim_lookup_table *table);
+
+/* Functions to read or write the lookup table from/to a WIM file */
+
extern int
read_wim_lookup_table(WIMStruct *wim);
struct wim_reshdr *out_reshdr,
int write_resource_flags);
-extern void
-free_lookup_table(struct wim_lookup_table *table);
-
-extern void
-lookup_table_insert(struct wim_lookup_table *table, struct wim_lookup_table_entry *lte);
-
-/* Unlinks a lookup table entry from the table; does not free it. */
-static inline void
-lookup_table_unlink(struct wim_lookup_table *table, struct wim_lookup_table_entry *lte)
-{
- wimlib_assert(!lte->unhashed);
- hlist_del(<e->hash_list);
- wimlib_assert(table->num_entries != 0);
- table->num_entries--;
-}
+/* Functions to create, clone, print, and free lookup table entries */
extern struct wim_lookup_table_entry *
new_lookup_table_entry(void) _malloc_attribute;
extern void
print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out);
+extern void
+lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
+ struct wim_lookup_table *table);
+#ifdef WITH_FUSE
+extern void
+lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte);
+#endif
+
extern void
free_lookup_table_entry(struct wim_lookup_table_entry *lte);
+/* Functions to insert and delete entries from a lookup table */
+
extern void
-lte_to_wimlib_resource_entry(const struct wim_lookup_table_entry *lte,
- struct wimlib_resource_entry *wentry);
+lookup_table_insert(struct wim_lookup_table *table,
+ struct wim_lookup_table_entry *lte);
+
+extern void
+lookup_table_unlink(struct wim_lookup_table *table,
+ struct wim_lookup_table_entry *lte);
+
+/* Function to lookup a stream by SHA1 message digest */
+extern struct wim_lookup_table_entry *
+lookup_stream(const struct wim_lookup_table *table, const u8 hash[]);
+
+/* Functions to iterate through the entries of a lookup table */
extern int
for_lookup_table_entry(struct wim_lookup_table *table,
int (*visitor)(struct wim_lookup_table_entry *, void *),
void *arg);
-extern int
-sort_stream_list(struct list_head *stream_list,
- size_t list_head_offset,
- int (*compar)(const void *, const void*));
-
-extern int
-sort_stream_list_by_sequential_order(struct list_head *stream_list,
- size_t list_head_offset);
-
extern int
for_lookup_table_entry_pos_sorted(struct wim_lookup_table *table,
int (*visitor)(struct wim_lookup_table_entry *,
void *),
void *arg);
-extern struct wim_lookup_table_entry *
-lookup_resource(const struct wim_lookup_table *table, const u8 hash[]);
-extern int
-wim_pathname_to_stream(WIMStruct *wim, const tchar *path,
- int lookup_flags,
- struct wim_dentry **dentry_ret,
- struct wim_lookup_table_entry **lte_ret,
- u16 *stream_idx_ret);
+
+/* Function to get a resource entry in stable format */
+
+struct wimlib_resource_entry;
extern void
-lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
- struct wim_lookup_table *table);
-#ifdef WITH_FUSE
-extern void
-lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte);
-#endif
+lte_to_wimlib_resource_entry(const struct wim_lookup_table_entry *lte,
+ struct wimlib_resource_entry *wentry);
+
+/* Functions to sort a list of lookup table entries */
+extern int
+sort_stream_list(struct list_head *stream_list,
+ size_t list_head_offset,
+ int (*compar)(const void *, const void*));
+
+extern int
+sort_stream_list_by_sequential_order(struct list_head *stream_list,
+ size_t list_head_offset);
+
+/* Utility functions */
extern int
lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore);
extern int
lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore);
+static inline bool
+lte_is_partial(const struct wim_lookup_table_entry * lte)
+{
+ return lte->resource_location == RESOURCE_IN_WIM &&
+ lte->size != lte->rspec->uncompressed_size;
+}
+
+static inline bool
+lte_filename_valid(const struct wim_lookup_table_entry *lte)
+{
+ return lte->resource_location == RESOURCE_IN_FILE_ON_DISK
+ #ifdef __WIN32__
+ || lte->resource_location == RESOURCE_WIN32_ENCRYPTED
+ #endif
+ #ifdef WITH_FUSE
+ || lte->resource_location == RESOURCE_IN_STAGING_FILE
+ #endif
+ ;
+}
+
static inline void
lte_bind_wim_resource_spec(struct wim_lookup_table_entry *lte,
struct wim_resource_spec *rspec)
lte->resource_location = RESOURCE_NONEXISTENT;
}
-extern int
-inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table,
- bool force);
-
-extern int
-resource_not_found_error(const struct wim_inode *inode, const u8 *hash);
-
-extern void
-inode_unresolve_ltes(struct wim_inode *inode);
-
-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)
- return NULL;
- if (stream_idx == 0)
- return lookup_resource(table, inode->i_hash);
- else
- return lookup_resource(table,
- inode->i_ads_entries[
- stream_idx - 1].hash);
-}
-
-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)
-{
- 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;
-}
-
-
-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;
-}
-
-/*
- * 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);
-}
-
-static inline u16
-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;
-}
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);
-
-extern struct wim_lookup_table_entry *
-inode_unnamed_lte(const struct wim_inode *inode, const struct wim_lookup_table *table);
-
-extern const u8 *
-inode_unnamed_stream_hash(const struct wim_inode *inode);
+new_stream_from_data_buffer(const void *buffer, size_t size,
+ struct wim_lookup_table *lookup_table);
static inline void
-lookup_table_insert_unhashed(struct wim_lookup_table *table,
- struct wim_lookup_table_entry *lte,
- struct wim_inode *back_inode,
- u32 back_stream_id)
+add_unhashed_stream(struct wim_lookup_table_entry *lte,
+ struct wim_inode *back_inode,
+ u32 back_stream_id,
+ struct list_head *unhashed_streams)
{
lte->unhashed = 1;
lte->back_inode = back_inode;
lte->back_stream_id = back_stream_id;
- list_add_tail(<e->unhashed_list, table->unhashed_streams);
+ list_add_tail(<e->unhashed_list, unhashed_streams);
}
extern int
struct _ntfs_volume;
#endif
-/* Metadata for a WIM image */
+/* Metadata for a WIM image */
struct wim_image_metadata {
/* Number of WIMStruct's that are sharing this image metadata (from
append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd);
extern struct wim_image_metadata *
-new_image_metadata(void);
+new_image_metadata(void) _malloc_attribute;
extern struct wim_image_metadata **
-new_image_metadata_array(unsigned num_images);
-
+new_image_metadata_array(unsigned num_images) _malloc_attribute;
#endif /* _WIMLIB_METADATA_H */
struct wim_lookup_table_entry;
struct _ntfs_volume;
+#ifdef WITH_NTFS_3G
+struct _ntfs_volume;
+struct ntfs_location {
+ tchar *path;
+ utf16lechar *stream_name;
+ u16 stream_name_nchars;
+ struct _ntfs_volume *ntfs_vol;
+ bool is_reparse_point;
+};
+#endif
+
extern void
libntfs3g_global_init(void);
--- /dev/null
+#ifndef _WIMLIB_UNIX_DATA_H
+#define _WIMLIB_UNIX_DATA_H
+
+#include "wimlib/types.h"
+struct wim_inode;
+struct wim_lookup_table;
+
+#define WIMLIB_UNIX_DATA_TAG "$$__wimlib_UNIX_data"
+#define WIMLIB_UNIX_DATA_TAG_NBYTES (sizeof(WIMLIB_UNIX_DATA_TAG) - 1)
+
+#define WIMLIB_UNIX_DATA_TAG_UTF16LE "$\0$\0_\0_\0w\0i\0m\0l\0i\0b\0_\0U\0N\0I\0X\0_\0d\0a\0t\0a\0"
+#define WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES (sizeof(WIMLIB_UNIX_DATA_TAG_UTF16LE) - 1)
+
+extern bool
+inode_has_unix_data(const struct wim_inode *inode);
+
+#ifndef __WIN32__
+/* Format for special alternate data stream entries to store UNIX data for files
+ * and directories (see: WIMLIB_ADD_FLAG_UNIX_DATA) */
+struct wimlib_unix_data {
+ u16 version; /* Must be 0 */
+ u16 uid;
+ u16 gid;
+ u16 mode;
+} _packed_attribute;
+
+#define NO_UNIX_DATA (-1)
+#define BAD_UNIX_DATA (-2)
+extern int
+inode_get_unix_data(const struct wim_inode *inode,
+ struct wimlib_unix_data *unix_data,
+ u16 *stream_idx_ret);
+
+#define UNIX_DATA_UID 0x1
+#define UNIX_DATA_GID 0x2
+#define UNIX_DATA_MODE 0x4
+#define UNIX_DATA_ALL (UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE)
+#define UNIX_DATA_CREATE 0x8
+extern int
+inode_set_unix_data(struct wim_inode *inode, u16 uid, u16 gid, u16 mode,
+ struct wim_lookup_table *lookup_table, int which);
+
+#endif /* __WIN32__ */
+
+#endif /* _WIMLIB_UNIX_DATA_H */
#include "wimlib/compiler.h"
#include <stdio.h>
-#include <stddef.h>
#ifndef min
#define min(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); \
#endif
#include "wimlib.h"
+#include "wimlib/case.h"
#include "wimlib/dentry.h"
#include "wimlib/encoding.h"
#include "wimlib/endianness.h"
#include <errno.h>
-/* 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;
-
- le64 reserved;
-
- /* SHA1 message digest of the uncompressed stream; or, alternatively,
- * 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.
- * */
- utf16lechar stream_name[];
-} _packed_attribute;
-
-#define WIM_ADS_ENTRY_DISK_SIZE 38
-
/* On-disk format of a WIM dentry (directory entry), located in the metadata
* resource for a WIM image. */
struct wim_dentry_on_disk {
/*utf16lechar short_name[];*/
} _packed_attribute;
-#define WIM_DENTRY_DISK_SIZE 102
-
/* Calculates the unaligned length, in bytes, of an on-disk WIM dentry that has
* a file name and short name that take the specified numbers of bytes. This
* excludes any alternate data stream entries that may follow the dentry. */
return (len + 7) & ~7;
}
-/* Duplicates a string of system-dependent encoding into a UTF-16LE string and
- * returns the string and its length, in bytes, in the pointer arguments. Frees
- * any existing string at the return location before overwriting it. */
-static int
-get_utf16le_name(const tchar *name, utf16lechar **name_utf16le_ret,
- u16 *name_utf16le_nbytes_ret)
-{
- utf16lechar *name_utf16le;
- size_t name_utf16le_nbytes;
- int ret;
-#if TCHAR_IS_UTF16LE
- name_utf16le_nbytes = tstrlen(name) * sizeof(utf16lechar);
- name_utf16le = MALLOC(name_utf16le_nbytes + sizeof(utf16lechar));
- if (name_utf16le == NULL)
- return WIMLIB_ERR_NOMEM;
- memcpy(name_utf16le, name, name_utf16le_nbytes + sizeof(utf16lechar));
- ret = 0;
-#else
-
- ret = tstr_to_utf16le(name, tstrlen(name), &name_utf16le,
- &name_utf16le_nbytes);
- if (ret == 0) {
- if (name_utf16le_nbytes > 0xffff) {
- FREE(name_utf16le);
- ERROR("Multibyte string \"%"TS"\" is too long!", name);
- ret = WIMLIB_ERR_INVALID_UTF8_STRING;
- }
- }
-#endif
- if (ret == 0) {
- FREE(*name_utf16le_ret);
- *name_utf16le_ret = name_utf16le;
- *name_utf16le_nbytes_ret = name_utf16le_nbytes;
- }
- return ret;
-}
-
-/* Sets the name of a WIM dentry from a multibyte string. */
+/* Sets the name of a WIM dentry from a multibyte string.
+ * Only use this on dentries not inserted into the tree. Use rename_wim_path()
+ * to do a real rename. */
int
-set_dentry_name(struct wim_dentry *dentry, const tchar *new_name)
+dentry_set_name(struct wim_dentry *dentry, const tchar *new_name)
{
int ret;
- ret = get_utf16le_name(new_name, &dentry->file_name,
- &dentry->file_name_nbytes);
+ ret = get_utf16le_string(new_name, &dentry->file_name,
+ &dentry->file_name_nbytes);
if (ret == 0) {
/* Clear the short name and recalculate the dentry length */
if (dentry_has_short_name(dentry)) {
* though there is already a field in the dentry itself for the unnamed stream
* reference, which then goes to waste.
*/
-static inline bool inode_needs_dummy_stream(const struct wim_inode *inode)
+static inline bool
+inode_needs_dummy_stream(const struct wim_inode *inode)
{
return (inode->i_num_ads > 0 &&
inode->i_num_ads < 0xffff && /* overflow check */
false);
}
-/* 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)
-{
- return 0 == cmp_utf16le_strings(name,
- name_nbytes / 2,
- entry->stream_name,
- entry->stream_name_nbytes / 2,
- ignore_case);
-}
-
/* Default case sensitivity behavior for searches with
* WIMLIB_CASE_PLATFORM_DEFAULT specified. This can be modified by
* wimlib_global_init(). */
#endif
;
-static bool
-will_ignore_case(CASE_SENSITIVITY_TYPE case_type)
-{
- if (case_type == WIMLIB_CASE_SENSITIVE)
- return false;
- if (case_type == WIMLIB_CASE_INSENSITIVE)
- return true;
-
- return default_ignore_case;
-}
-
-
/* Given a UTF-16LE filename and a directory, look up the dentry for the file.
* Return it if found, otherwise NULL. This is case-sensitive on UNIX and
* case-insensitive on Windows. */
return get_dentry(wim, buf, case_type);
}
+#ifdef WITH_FUSE
+/* Finds the dentry, lookup table entry, and stream index for a WIM file stream,
+ * given a path name.
+ *
+ * Currently, lookups of this type are only needed if FUSE is enabled. */
+int
+wim_pathname_to_stream(WIMStruct *wim,
+ const tchar *path,
+ int lookup_flags,
+ struct wim_dentry **dentry_ret,
+ struct wim_lookup_table_entry **lte_ret,
+ u16 *stream_idx_ret)
+{
+ struct wim_dentry *dentry;
+ struct wim_lookup_table_entry *lte;
+ u16 stream_idx;
+ const tchar *stream_name = NULL;
+ struct wim_inode *inode;
+ tchar *p = NULL;
+
+ if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
+ stream_name = path_stream_name(path);
+ if (stream_name) {
+ p = (tchar*)stream_name - 1;
+ *p = T('\0');
+ }
+ }
+
+ dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
+ if (p)
+ *p = T(':');
+ if (!dentry)
+ return -errno;
+
+ inode = dentry->d_inode;
+
+ if (!inode->i_resolved)
+ if (inode_resolve_streams(inode, wim->lookup_table, false))
+ return -EIO;
+
+ if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
+ && inode_is_directory(inode))
+ return -EISDIR;
+
+ if (stream_name) {
+ struct wim_ads_entry *ads_entry;
+ u16 ads_idx;
+ ads_entry = inode_get_ads_entry(inode, stream_name,
+ &ads_idx);
+ if (ads_entry) {
+ stream_idx = ads_idx + 1;
+ lte = ads_entry->lte;
+ goto out;
+ } else {
+ return -ENOENT;
+ }
+ } else {
+ lte = inode_unnamed_stream_resolved(inode, &stream_idx);
+ }
+out:
+ if (dentry_ret)
+ *dentry_ret = dentry;
+ if (lte_ret)
+ *lte_ret = lte;
+ if (stream_idx_ret)
+ *stream_idx_ret = stream_idx;
+ return 0;
+}
+#endif /* WITH_FUSE */
+
/* Prints the full path of a dentry. */
int
print_dentry_full_path(struct wim_dentry *dentry, void *_ignore)
memset(dentry, 0, sizeof(struct wim_dentry));
}
-struct wim_inode *
-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_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;
-}
-
-static struct wim_inode *
-new_inode(void)
-{
- struct wim_inode *inode = new_timeless_inode();
- if (inode) {
- u64 now = get_wim_timestamp();
- inode->i_creation_time = now;
- inode->i_last_access_time = now;
- inode->i_last_write_time = now;
- }
- return inode;
-}
-
/* Creates an unlinked directory entry. */
int
new_dentry(const tchar *name, struct wim_dentry **dentry_ret)
return WIMLIB_ERR_NOMEM;
dentry_common_init(dentry);
- ret = set_dentry_name(dentry, name);
+ ret = dentry_set_name(dentry, name);
if (ret == 0) {
dentry->parent = dentry;
*dentry_ret = dentry;
return ret;
}
-
static int
_new_dentry_with_inode(const tchar *name, struct wim_dentry **dentry_ret,
bool timeless)
for_dentry_in_tree(root, dentry_clear_inode_visited, NULL);
}
-static int
-init_ads_entry(struct wim_ads_entry *ads_entry, const void *name,
- size_t name_nbytes, bool is_utf16le)
-{
- int ret = 0;
- memset(ads_entry, 0, sizeof(*ads_entry));
-
- if (is_utf16le) {
- utf16lechar *p = MALLOC(name_nbytes + sizeof(utf16lechar));
- if (p == NULL)
- return WIMLIB_ERR_NOMEM;
- memcpy(p, name, name_nbytes);
- p[name_nbytes / 2] = cpu_to_le16(0);
- ads_entry->stream_name = p;
- ads_entry->stream_name_nbytes = name_nbytes;
- } else {
- if (name && *(const tchar*)name != T('\0')) {
- ret = get_utf16le_name(name, &ads_entry->stream_name,
- &ads_entry->stream_name_nbytes);
- }
- }
- return ret;
-}
-
-static void
-destroy_ads_entry(struct wim_ads_entry *ads_entry)
-{
- FREE(ads_entry->stream_name);
-}
-
-/* Frees an inode. */
-void
-free_inode(struct wim_inode *inode)
-{
- if (inode) {
- if (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);
- }
- /* HACK: This may instead delete the inode from i_list, but the
- * hlist_del() behaves the same as list_del(). */
- if (!hlist_unhashed(&inode->i_hlist))
- hlist_del(&inode->i_hlist);
- FREE(inode);
- }
-}
-
-/* Decrements link count on an inode and frees it if the link count reaches 0.
- * */
-static void
-put_inode(struct wim_inode *inode)
-{
- wimlib_assert(inode->i_nlink != 0);
- if (--inode->i_nlink == 0) {
- #ifdef WITH_FUSE
- if (inode->i_num_opened_fds == 0)
- #endif
- {
- free_inode(inode);
- }
- }
-}
-
/* Frees a WIM dentry.
*
* The corresponding inode (if any) is freed only if its link count is
- * decremented to 0.
- */
+ * decremented to 0. */
void
free_dentry(struct wim_dentry *dentry)
{
return -ENOTDIR;
}
- ret = set_dentry_name(src, path_basename(to));
+ ret = dentry_set_name(src, path_basename(to));
if (ret)
return -ENOMEM;
if (dst) {
return 0;
}
-/*
- * 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.
- *
- * If @p stream_name is the empty string, NULL is returned --- that is, this
- * function will not return "unnamed" alternate data stream entries.
- */
-struct wim_ads_entry *
-inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
- u16 *idx_ret)
-{
- if (inode->i_num_ads == 0) {
- return NULL;
- } else {
- size_t stream_name_utf16le_nbytes;
- u16 i;
- struct wim_ads_entry *result;
-
- if (stream_name[0] == T('\0'))
- return NULL;
-
- #if TCHAR_IS_UTF16LE
- const utf16lechar *stream_name_utf16le;
-
- stream_name_utf16le = stream_name;
- stream_name_utf16le_nbytes = tstrlen(stream_name) * sizeof(tchar);
- #else
- utf16lechar *stream_name_utf16le;
-
- {
- int ret = tstr_to_utf16le(stream_name,
- tstrlen(stream_name) *
- sizeof(tchar),
- &stream_name_utf16le,
- &stream_name_utf16le_nbytes);
- if (ret)
- return NULL;
- }
- #endif
- 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 (idx_ret)
- *idx_ret = i;
- result = &inode->i_ads_entries[i];
- break;
- }
- } while (++i != inode->i_num_ads);
- #if !TCHAR_IS_UTF16LE
- FREE(stream_name_utf16le);
- #endif
- return result;
- }
-}
-
-static struct wim_ads_entry *
-do_inode_add_ads(struct wim_inode *inode, const void *stream_name,
- size_t stream_name_nbytes, bool is_utf16le)
-{
- u16 num_ads;
- struct wim_ads_entry *ads_entries;
- struct wim_ads_entry *new_entry;
-
- wimlib_assert(stream_name_nbytes != 0);
-
- if (inode->i_num_ads >= 0xfffe) {
- ERROR("Too many alternate data streams in one inode!");
- 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");
- return NULL;
- }
- inode->i_ads_entries = ads_entries;
-
- new_entry = &inode->i_ads_entries[num_ads - 1];
- if (init_ads_entry(new_entry, stream_name, stream_name_nbytes, is_utf16le))
- return NULL;
- new_entry->stream_id = inode->i_next_stream_id++;
- inode->i_num_ads = num_ads;
- return new_entry;
-}
-
-struct wim_ads_entry *
-inode_add_ads_utf16le(struct wim_inode *inode,
- const utf16lechar *stream_name,
- size_t stream_name_nbytes)
-{
- DEBUG("Add alternate data stream \"%"WS"\"", stream_name);
- return do_inode_add_ads(inode, stream_name, stream_name_nbytes, true);
-}
-
-/*
- * Add an alternate stream entry to a WIM inode. On success, returns a pointer
- * to the new entry; on failure, returns NULL.
- *
- * @stream_name must be a nonempty string.
- */
-struct wim_ads_entry *
-inode_add_ads(struct wim_inode *inode, const tchar *stream_name)
-{
- DEBUG("Add alternate data stream \"%"TS"\"", stream_name);
- return do_inode_add_ads(inode, stream_name,
- tstrlen(stream_name) * sizeof(tchar),
- TCHAR_IS_UTF16LE);
-}
-
-static struct wim_lookup_table_entry *
-add_stream_from_data_buffer(const void *buffer, size_t size,
- struct wim_lookup_table *lookup_table)
-{
- u8 hash[SHA1_HASH_SIZE];
- struct wim_lookup_table_entry *lte, *existing_lte;
-
- sha1_buffer(buffer, size, hash);
- existing_lte = lookup_resource(lookup_table, hash);
- if (existing_lte) {
- wimlib_assert(existing_lte->size == size);
- lte = existing_lte;
- lte->refcnt++;
- } else {
- void *buffer_copy;
- lte = new_lookup_table_entry();
- if (lte == NULL)
- return NULL;
- buffer_copy = memdup(buffer, size);
- if (buffer_copy == NULL) {
- free_lookup_table_entry(lte);
- return NULL;
- }
- lte->resource_location = RESOURCE_IN_ATTACHED_BUFFER;
- lte->attached_buffer = buffer_copy;
- lte->size = size;
- copy_hash(lte->hash, hash);
- lookup_table_insert(lookup_table, lte);
- }
- return lte;
-}
-
-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)
-{
- struct wim_ads_entry *new_ads_entry;
-
- wimlib_assert(inode->i_resolved);
-
- new_ads_entry = inode_add_ads(inode, name);
- if (new_ads_entry == NULL)
- return WIMLIB_ERR_NOMEM;
-
- new_ads_entry->lte = add_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;
- }
- 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)
-{
- inode->i_lte = add_stream_from_data_buffer(data, len, lookup_table);
- if (inode->i_lte == NULL)
- return WIMLIB_ERR_NOMEM;
- inode->i_resolved = 1;
- return 0;
-}
-
-/* Remove an alternate data stream from a WIM inode */
-void
-inode_remove_ads(struct wim_inode *inode, u16 idx,
- struct wim_lookup_table *lookup_table)
-{
- struct wim_ads_entry *ads_entry;
- struct wim_lookup_table_entry *lte;
-
- 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;
- if (lte)
- lte_decrement_refcnt(lte, lookup_table);
-
- destroy_ads_entry(ads_entry);
-
- 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--;
-}
-
-bool
-inode_has_unix_data(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]))
- return true;
- return false;
-}
-
-#ifndef __WIN32__
-int
-inode_get_unix_data(const struct wim_inode *inode,
- struct wimlib_unix_data *unix_data,
- u16 *stream_idx_ret)
-{
- const struct wim_ads_entry *ads_entry;
- const struct wim_lookup_table_entry *lte;
- size_t size;
- int ret;
-
- wimlib_assert(inode->i_resolved);
-
- 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;
- return 0;
-}
-
-int
-inode_set_unix_data(struct wim_inode *inode, uid_t uid, gid_t gid, mode_t 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__ */
-
-/*
- * Reads 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.
- *
- * @remaining_size:
- * Number of bytes of data remaining in the buffer pointed to by @p.
- *
- * 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
- */
-static int
-read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode,
- size_t nbytes_remaining)
-{
- 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 == NULL)
- 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 = MALLOC(cur_entry->stream_name_nbytes + 2);
- if (cur_entry->stream_name == NULL)
- goto out_of_memory;
-
- memcpy(cur_entry->stream_name,
- disk_entry->stream_name,
- cur_entry->stream_name_nbytes);
- cur_entry->stream_name[cur_entry->stream_name_nbytes / 2] = cpu_to_le16(0);
- } else {
- /* Mark inode as having weird stream entries. */
- inode->i_canonical_streams = 0;
- }
-
- /* 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) & ~(u64)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;
- 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;
-}
-
/*
* Reads a WIM directory entry, including all alternate data stream entries that
* follow it, from the WIM image's metadata resource.
/* Recursively write the rest of the dentry tree. */
return write_dentry_tree_recursive(root, p);
}
-
-
-static int
-init_wimlib_dentry(struct wimlib_dir_entry *wdentry,
- struct wim_dentry *dentry,
- const WIMStruct *wim,
- int flags)
-{
- int ret;
- size_t dummy;
- const struct wim_inode *inode = dentry->d_inode;
- struct wim_lookup_table_entry *lte;
- const u8 *hash;
-
-#if TCHAR_IS_UTF16LE
- wdentry->filename = dentry->file_name;
- wdentry->dos_name = dentry->short_name;
-#else
- if (dentry_has_long_name(dentry)) {
- ret = utf16le_to_tstr(dentry->file_name,
- dentry->file_name_nbytes,
- (tchar**)&wdentry->filename,
- &dummy);
- if (ret)
- return ret;
- }
- if (dentry_has_short_name(dentry)) {
- ret = utf16le_to_tstr(dentry->short_name,
- dentry->short_name_nbytes,
- (tchar**)&wdentry->dos_name,
- &dummy);
- if (ret)
- return ret;
- }
-#endif
- ret = calculate_dentry_full_path(dentry);
- if (ret)
- return ret;
- wdentry->full_path = dentry->_full_path;
-
- for (struct wim_dentry *d = dentry; !dentry_is_root(d); d = d->parent)
- wdentry->depth++;
-
- if (inode->i_security_id >= 0) {
- const struct wim_security_data *sd = wim_const_security_data(wim);
- wdentry->security_descriptor = sd->descriptors[inode->i_security_id];
- wdentry->security_descriptor_size = sd->sizes[inode->i_security_id];
- }
- wdentry->reparse_tag = inode->i_reparse_tag;
- wdentry->num_links = inode->i_nlink;
- wdentry->attributes = inode->i_attributes;
- wdentry->hard_link_group_id = inode->i_ino;
- wdentry->creation_time = wim_timestamp_to_timespec(inode->i_creation_time);
- wdentry->last_write_time = wim_timestamp_to_timespec(inode->i_last_write_time);
- wdentry->last_access_time = wim_timestamp_to_timespec(inode->i_last_access_time);
-
- lte = inode_unnamed_lte(inode, wim->lookup_table);
- if (lte) {
- lte_to_wimlib_resource_entry(lte, &wdentry->streams[0].resource);
- } else if (!is_zero_hash(hash = inode_unnamed_stream_hash(inode))) {
- if (flags & WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED)
- return resource_not_found_error(inode, hash);
- copy_hash(wdentry->streams[0].resource.sha1_hash, hash);
- wdentry->streams[0].resource.is_missing = 1;
- }
-
- for (unsigned i = 0; i < inode->i_num_ads; i++) {
- if (!ads_entry_is_named_stream(&inode->i_ads_entries[i]))
- continue;
- lte = inode_stream_lte(inode, i + 1, wim->lookup_table);
- wdentry->num_named_streams++;
- if (lte) {
- lte_to_wimlib_resource_entry(lte, &wdentry->streams[
- wdentry->num_named_streams].resource);
- } else if (!is_zero_hash(hash = inode_stream_hash(inode, i + 1))) {
- if (flags & WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED)
- return resource_not_found_error(inode, hash);
- copy_hash(wdentry->streams[
- wdentry->num_named_streams].resource.sha1_hash, hash);
- wdentry->streams[
- wdentry->num_named_streams].resource.is_missing = 1;
- }
- #if TCHAR_IS_UTF16LE
- wdentry->streams[wdentry->num_named_streams].stream_name =
- inode->i_ads_entries[i].stream_name;
- #else
- size_t dummy;
-
- ret = utf16le_to_tstr(inode->i_ads_entries[i].stream_name,
- inode->i_ads_entries[i].stream_name_nbytes,
- (tchar**)&wdentry->streams[
- wdentry->num_named_streams].stream_name,
- &dummy);
- if (ret)
- return ret;
- #endif
- }
- return 0;
-}
-
-static void
-free_wimlib_dentry(struct wimlib_dir_entry *wdentry)
-{
-#if !TCHAR_IS_UTF16LE
- FREE((tchar*)wdentry->filename);
- FREE((tchar*)wdentry->dos_name);
- for (unsigned i = 1; i <= wdentry->num_named_streams; i++)
- FREE((tchar*)wdentry->streams[i].stream_name);
-#endif
- FREE(wdentry);
-}
-
-struct iterate_dir_tree_ctx {
- WIMStruct *wim;
- int flags;
- wimlib_iterate_dir_tree_callback_t cb;
- void *user_ctx;
-};
-
-static int
-do_iterate_dir_tree(WIMStruct *wim,
- struct wim_dentry *dentry, int flags,
- wimlib_iterate_dir_tree_callback_t cb,
- void *user_ctx);
-
-static int
-call_do_iterate_dir_tree(struct wim_dentry *dentry, void *_ctx)
-{
- struct iterate_dir_tree_ctx *ctx = _ctx;
- return do_iterate_dir_tree(ctx->wim, dentry, ctx->flags,
- ctx->cb, ctx->user_ctx);
-}
-
-static int
-do_iterate_dir_tree(WIMStruct *wim,
- struct wim_dentry *dentry, int flags,
- wimlib_iterate_dir_tree_callback_t cb,
- void *user_ctx)
-{
- struct wimlib_dir_entry *wdentry;
- int ret = WIMLIB_ERR_NOMEM;
-
-
- wdentry = CALLOC(1, sizeof(struct wimlib_dir_entry) +
- (1 + dentry->d_inode->i_num_ads) *
- sizeof(struct wimlib_stream_entry));
- if (wdentry == NULL)
- goto out;
-
- ret = init_wimlib_dentry(wdentry, dentry, wim, flags);
- if (ret)
- goto out_free_wimlib_dentry;
-
- if (!(flags & WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN)) {
- ret = (*cb)(wdentry, user_ctx);
- if (ret)
- goto out_free_wimlib_dentry;
- }
-
- if (flags & (WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE |
- WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN))
- {
- struct iterate_dir_tree_ctx ctx = {
- .wim = wim,
- .flags = flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN,
- .cb = cb,
- .user_ctx = user_ctx,
- };
- ret = for_dentry_child(dentry, call_do_iterate_dir_tree, &ctx);
- }
-out_free_wimlib_dentry:
- free_wimlib_dentry(wdentry);
-out:
- return ret;
-}
-
-struct image_iterate_dir_tree_ctx {
- const tchar *path;
- int flags;
- wimlib_iterate_dir_tree_callback_t cb;
- void *user_ctx;
-};
-
-
-static int
-image_do_iterate_dir_tree(WIMStruct *wim)
-{
- struct image_iterate_dir_tree_ctx *ctx = wim->private;
- struct wim_dentry *dentry;
-
- dentry = get_dentry(wim, ctx->path, WIMLIB_CASE_PLATFORM_DEFAULT);
- if (dentry == NULL)
- return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
- return do_iterate_dir_tree(wim, dentry, ctx->flags, ctx->cb, ctx->user_ctx);
-}
-
-/* API function documented in wimlib.h */
-WIMLIBAPI int
-wimlib_iterate_dir_tree(WIMStruct *wim, int image, const tchar *_path,
- int flags,
- wimlib_iterate_dir_tree_callback_t cb, void *user_ctx)
-{
- tchar *path;
- int ret;
-
- path = canonicalize_wim_path(_path);
- if (path == NULL)
- return WIMLIB_ERR_NOMEM;
- struct image_iterate_dir_tree_ctx ctx = {
- .path = path,
- .flags = flags,
- .cb = cb,
- .user_ctx = user_ctx,
- };
- wim->private = &ctx;
- ret = for_image(wim, image, image_do_iterate_dir_tree);
- FREE(path);
- return ret;
-}
-
-/* Returns %true iff the metadata of @inode and @template_inode are reasonably
- * consistent with them being the same, unmodified file. */
-static bool
-inode_metadata_consistent(const struct wim_inode *inode,
- const struct wim_inode *template_inode,
- const struct wim_lookup_table *template_lookup_table)
-{
- /* Must have exact same creation time and last write time. */
- if (inode->i_creation_time != template_inode->i_creation_time ||
- inode->i_last_write_time != template_inode->i_last_write_time)
- return false;
-
- /* Last access time may have stayed the same or increased, but certainly
- * shouldn't have decreased. */
- if (inode->i_last_access_time < template_inode->i_last_access_time)
- return false;
-
- /* Must have same number of alternate data stream entries. */
- if (inode->i_num_ads != template_inode->i_num_ads)
- return false;
-
- /* If the stream entries for the inode are for some reason not resolved,
- * then the hashes are already available and the point of this function
- * is defeated. */
- if (!inode->i_resolved)
- return false;
-
- /* Iterate through each stream and do some more checks. */
- for (unsigned i = 0; i <= inode->i_num_ads; i++) {
- const struct wim_lookup_table_entry *lte, *template_lte;
-
- lte = inode_stream_lte_resolved(inode, i);
- template_lte = inode_stream_lte(template_inode, i,
- template_lookup_table);
-
- /* Compare stream sizes. */
- if (lte && template_lte) {
- if (lte->size != template_lte->size)
- return false;
-
- /* If hash happens to be available, compare with template. */
- if (!lte->unhashed && !template_lte->unhashed &&
- !hashes_equal(lte->hash, template_lte->hash))
- return false;
-
- } else if (lte && lte->size) {
- return false;
- } else if (template_lte && template_lte->size) {
- return false;
- }
- }
-
- /* All right, barring a full checksum and given that the inodes share a
- * path and the user isn't trying to trick us, these inodes most likely
- * refer to the same file. */
- return true;
-}
-
-/**
- * Given an inode @inode that has been determined to be "the same" as another
- * inode @template_inode in either the same WIM or another WIM, retrieve some
- * useful stream information (e.g. checksums) from @template_inode.
- *
- * This assumes that the streams for @inode have been resolved (to point
- * directly to the appropriate `struct wim_lookup_table_entry's) but do not
- * necessarily have checksum information filled in.
- */
-static int
-inode_copy_checksums(struct wim_inode *inode,
- struct wim_inode *template_inode,
- WIMStruct *wim,
- WIMStruct *template_wim)
-{
- for (unsigned i = 0; i <= inode->i_num_ads; i++) {
- struct wim_lookup_table_entry *lte, *template_lte;
- struct wim_lookup_table_entry *replace_lte;
-
- lte = inode_stream_lte_resolved(inode, i);
- template_lte = inode_stream_lte(template_inode, i,
- template_wim->lookup_table);
-
- /* Only take action if both entries exist, the entry for @inode
- * has no checksum calculated, but the entry for @template_inode
- * does. */
- if (lte == NULL || template_lte == NULL ||
- !lte->unhashed || template_lte->unhashed)
- continue;
-
- wimlib_assert(lte->refcnt == inode->i_nlink);
-
- /* If the WIM of the template image is the same as the WIM of
- * the new image, then @template_lte can be used directly.
- *
- * Otherwise, look for a stream with the same hash in the WIM of
- * the new image. If found, use it; otherwise re-use the entry
- * being discarded, filling in the hash. */
-
- if (wim == template_wim)
- replace_lte = template_lte;
- else
- replace_lte = lookup_resource(wim->lookup_table,
- template_lte->hash);
-
- list_del(<e->unhashed_list);
- if (replace_lte) {
- free_lookup_table_entry(lte);
- } else {
- copy_hash(lte->hash, template_lte->hash);
- lte->unhashed = 0;
- lookup_table_insert(wim->lookup_table, lte);
- lte->refcnt = 0;
- replace_lte = lte;
- }
-
- if (i == 0)
- inode->i_lte = replace_lte;
- else
- inode->i_ads_entries[i - 1].lte = replace_lte;
-
- replace_lte->refcnt += inode->i_nlink;
- }
- return 0;
-}
-
-struct reference_template_args {
- WIMStruct *wim;
- WIMStruct *template_wim;
-};
-
-static int
-dentry_reference_template(struct wim_dentry *dentry, void *_args)
-{
- int ret;
- struct wim_dentry *template_dentry;
- struct wim_inode *inode, *template_inode;
- struct reference_template_args *args = _args;
- WIMStruct *wim = args->wim;
- WIMStruct *template_wim = args->template_wim;
-
- if (dentry->d_inode->i_visited)
- return 0;
-
- ret = calculate_dentry_full_path(dentry);
- if (ret)
- return ret;
-
- template_dentry = get_dentry(template_wim, dentry->_full_path,
- WIMLIB_CASE_SENSITIVE);
- if (template_dentry == NULL) {
- DEBUG("\"%"TS"\": newly added file", dentry->_full_path);
- return 0;
- }
-
- inode = dentry->d_inode;
- template_inode = template_dentry->d_inode;
-
- if (inode_metadata_consistent(inode, template_inode,
- template_wim->lookup_table)) {
- /*DEBUG("\"%"TS"\": No change detected", dentry->_full_path);*/
- ret = inode_copy_checksums(inode, template_inode,
- wim, template_wim);
- inode->i_visited = 1;
- } else {
- DEBUG("\"%"TS"\": change detected!", dentry->_full_path);
- ret = 0;
- }
- return ret;
-}
-
-/* API function documented in wimlib.h */
-WIMLIBAPI int
-wimlib_reference_template_image(WIMStruct *wim, int new_image,
- WIMStruct *template_wim, int template_image,
- int flags, wimlib_progress_func_t progress_func)
-{
- int ret;
- struct wim_image_metadata *new_imd;
-
- if (wim == NULL || template_wim == NULL)
- return WIMLIB_ERR_INVALID_PARAM;
-
- if (wim == template_wim && new_image == template_image)
- return WIMLIB_ERR_INVALID_PARAM;
-
- if (new_image < 1 || new_image > wim->hdr.image_count)
- return WIMLIB_ERR_INVALID_IMAGE;
-
- if (!wim_has_metadata(wim))
- return WIMLIB_ERR_METADATA_NOT_FOUND;
-
- new_imd = wim->image_metadata[new_image - 1];
- if (!new_imd->modified)
- return WIMLIB_ERR_INVALID_PARAM;
-
- ret = select_wim_image(template_wim, template_image);
- if (ret)
- return ret;
-
- struct reference_template_args args = {
- .wim = wim,
- .template_wim = template_wim,
- };
-
- ret = for_dentry_in_tree(new_imd->root_dentry,
- dentry_reference_template, &args);
- dentry_tree_clear_inode_visited(new_imd->root_dentry);
- return ret;
-}
return 0;
return (n1 < n2) ? -1 : 1;
}
+
+/* Duplicates a string of system-dependent encoding into a UTF-16LE string and
+ * returns the string and its length, in bytes, in the pointer arguments. Frees
+ * any existing string at the return location before overwriting it. */
+int
+get_utf16le_string(const tchar *name, utf16lechar **name_utf16le_ret,
+ u16 *name_utf16le_nbytes_ret)
+{
+ utf16lechar *name_utf16le;
+ size_t name_utf16le_nbytes;
+ int ret;
+#if TCHAR_IS_UTF16LE
+ name_utf16le_nbytes = tstrlen(name) * sizeof(utf16lechar);
+ name_utf16le = MALLOC(name_utf16le_nbytes + sizeof(utf16lechar));
+ if (name_utf16le == NULL)
+ return WIMLIB_ERR_NOMEM;
+ memcpy(name_utf16le, name, name_utf16le_nbytes + sizeof(utf16lechar));
+ ret = 0;
+#else
+
+ ret = tstr_to_utf16le(name, tstrlen(name), &name_utf16le,
+ &name_utf16le_nbytes);
+ if (ret == 0) {
+ if (name_utf16le_nbytes > 0xffff) {
+ FREE(name_utf16le);
+ ERROR("Multibyte string \"%"TS"\" is too long!", name);
+ ret = WIMLIB_ERR_INVALID_UTF8_STRING;
+ }
+ }
+#endif
+ if (ret == 0) {
+ FREE(*name_utf16le_ret);
+ *name_utf16le_ret = name_utf16le;
+ *name_utf16le_nbytes_ret = name_utf16le_nbytes;
+ }
+ return ret;
+}
+
#include "wimlib.h"
#include "wimlib/dentry.h"
#include "wimlib/error.h"
+#include "wimlib/inode.h"
#include "wimlib/lookup_table.h"
#include "wimlib/metadata.h"
#include "wimlib/xml.h"
const u8 *hash;
struct wim_lookup_table_entry *src_lte, *dest_lte;
- inode_unresolve_ltes(inode);
+ inode_unresolve_streams(inode);
for (i = 0; i <= inode->i_num_ads; i++) {
/* Retrieve SHA1 message digest of stream to export. */
/* Search for the stream (via SHA1 message digest) in the
* destination WIM. */
- dest_lte = lookup_resource(dest_lookup_table, hash);
+ dest_lte = lookup_stream(dest_lookup_table, hash);
if (!dest_lte) {
/* Stream not yet present in destination WIM. Search
* for it in the source WIM, then export it into the
* destination WIM. */
- src_lte = lookup_resource(src_lookup_table, hash);
+ src_lte = lookup_stream(src_lookup_table, hash);
if (!src_lte)
- return resource_not_found_error(inode, hash);
+ return stream_not_found_error(inode, hash);
dest_lte = clone_lookup_table_entry(src_lte);
if (!dest_lte)
* "resolve" the inode's streams anyway by allocating new entries. */
if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE)
force = true;
- ret = inode_resolve_ltes(inode, ctx->wim->lookup_table, force);
+ ret = inode_resolve_streams(inode, ctx->wim->lookup_table, force);
if (ret)
return ret;
for (unsigned i = 0; i <= inode->i_num_ads; i++) {
if ((found_lte->resource_location != RESOURCE_NONEXISTENT)
&& !(found_lte->flags & WIM_RESHDR_FLAG_METADATA)
- && (needed_lte = lookup_resource(lookup_table, found_lte->hash))
+ && (needed_lte = lookup_stream(lookup_table, found_lte->hash))
&& (needed_lte->out_refcnt))
{
tchar *tmpfile_name = NULL;
--- /dev/null
+/*
+ * inode.c
+ */
+
+/*
+ * Copyright (C) 2012, 2013 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * 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.
+ *
+ * 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
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#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/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"
+
+/* Allocate a new inode. Set the timestamps to the current time. */
+struct wim_inode *
+new_inode(void)
+{
+ struct wim_inode *inode = new_timeless_inode();
+ if (inode) {
+ u64 now = get_wim_timestamp();
+ inode->i_creation_time = now;
+ inode->i_last_access_time = now;
+ inode->i_last_write_time = now;
+ }
+ return inode;
+}
+
+
+/* Allocate a new inode. Leave the timestamps zeroed out. */
+struct wim_inode *
+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_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;
+}
+
+/* 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. */
+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
+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++)
+ destroy_ads_entry(&inode->i_ads_entries[i]);
+ FREE(inode->i_ads_entries);
+ }
+ /* 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);
+}
+
+/* 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)
+{
+ return 0 == cmp_utf16le_strings(name,
+ name_nbytes / 2,
+ entry->stream_name,
+ entry->stream_name_nbytes / 2,
+ ignore_case);
+}
+
+/*
+ * 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.
+ *
+ * If @p stream_name is the empty string, NULL is returned --- that is, this
+ * function will not return "unnamed" alternate data stream entries.
+ */
+struct wim_ads_entry *
+inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
+ u16 *idx_ret)
+{
+ if (inode->i_num_ads == 0) {
+ return NULL;
+ } else {
+ size_t stream_name_utf16le_nbytes;
+ u16 i;
+ struct wim_ads_entry *result;
+
+ if (stream_name[0] == T('\0'))
+ return NULL;
+
+ #if TCHAR_IS_UTF16LE
+ const utf16lechar *stream_name_utf16le;
+
+ stream_name_utf16le = stream_name;
+ stream_name_utf16le_nbytes = tstrlen(stream_name) * sizeof(tchar);
+ #else
+ utf16lechar *stream_name_utf16le;
+
+ {
+ int ret = tstr_to_utf16le(stream_name,
+ tstrlen(stream_name) *
+ sizeof(tchar),
+ &stream_name_utf16le,
+ &stream_name_utf16le_nbytes);
+ if (ret)
+ return NULL;
+ }
+ #endif
+ 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 (idx_ret)
+ *idx_ret = i;
+ result = &inode->i_ads_entries[i];
+ break;
+ }
+ } while (++i != inode->i_num_ads);
+ #if !TCHAR_IS_UTF16LE
+ FREE(stream_name_utf16le);
+ #endif
+ return result;
+ }
+}
+
+static int
+init_ads_entry(struct wim_ads_entry *ads_entry, const void *name,
+ size_t name_nbytes, bool is_utf16le)
+{
+ int ret = 0;
+ memset(ads_entry, 0, sizeof(*ads_entry));
+
+ if (is_utf16le) {
+ utf16lechar *p = MALLOC(name_nbytes + sizeof(utf16lechar));
+ if (p == NULL)
+ return WIMLIB_ERR_NOMEM;
+ memcpy(p, name, name_nbytes);
+ p[name_nbytes / 2] = cpu_to_le16(0);
+ ads_entry->stream_name = p;
+ ads_entry->stream_name_nbytes = name_nbytes;
+ } else {
+ if (name && *(const tchar*)name != T('\0')) {
+ ret = get_utf16le_string(name, &ads_entry->stream_name,
+ &ads_entry->stream_name_nbytes);
+ }
+ }
+ return ret;
+}
+
+static struct wim_ads_entry *
+do_inode_add_ads(struct wim_inode *inode, const void *stream_name,
+ size_t stream_name_nbytes, bool is_utf16le)
+{
+ u16 num_ads;
+ struct wim_ads_entry *ads_entries;
+ struct wim_ads_entry *new_entry;
+
+ wimlib_assert(stream_name_nbytes != 0);
+
+ if (inode->i_num_ads >= 0xfffe) {
+ ERROR("Too many alternate data streams in one inode!");
+ 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");
+ return NULL;
+ }
+ inode->i_ads_entries = ads_entries;
+
+ new_entry = &inode->i_ads_entries[num_ads - 1];
+ if (init_ads_entry(new_entry, stream_name, stream_name_nbytes, is_utf16le))
+ return NULL;
+ new_entry->stream_id = inode->i_next_stream_id++;
+ inode->i_num_ads = num_ads;
+ return new_entry;
+}
+
+struct wim_ads_entry *
+inode_add_ads_utf16le(struct wim_inode *inode,
+ const utf16lechar *stream_name,
+ size_t stream_name_nbytes)
+{
+ DEBUG("Add alternate data stream \"%"WS"\"", stream_name);
+ return do_inode_add_ads(inode, stream_name, stream_name_nbytes, true);
+}
+
+/*
+ * Add an alternate stream entry to a WIM inode. On success, returns a pointer
+ * to the new entry; on failure, returns NULL.
+ *
+ * @stream_name must be a nonempty string.
+ */
+struct wim_ads_entry *
+inode_add_ads(struct wim_inode *inode, const tchar *stream_name)
+{
+ DEBUG("Add alternate data stream \"%"TS"\"", stream_name);
+ return do_inode_add_ads(inode, stream_name,
+ tstrlen(stream_name) * sizeof(tchar),
+ TCHAR_IS_UTF16LE);
+}
+
+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)
+{
+ struct wim_ads_entry *new_ads_entry;
+
+ wimlib_assert(inode->i_resolved);
+
+ new_ads_entry = inode_add_ads(inode, name);
+ if (new_ads_entry == NULL)
+ return WIMLIB_ERR_NOMEM;
+
+ 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;
+ }
+ 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)
+{
+ inode->i_lte = new_stream_from_data_buffer(data, len, lookup_table);
+ if (inode->i_lte == NULL)
+ return WIMLIB_ERR_NOMEM;
+ inode->i_resolved = 1;
+ return 0;
+}
+
+/* Remove an alternate data stream from a WIM inode */
+void
+inode_remove_ads(struct wim_inode *inode, u16 idx,
+ struct wim_lookup_table *lookup_table)
+{
+ struct wim_ads_entry *ads_entry;
+ struct wim_lookup_table_entry *lte;
+
+ 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;
+ if (lte)
+ lte_decrement_refcnt(lte, lookup_table);
+
+ destroy_ads_entry(ads_entry);
+
+ 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--;
+}
+
+bool
+inode_has_unix_data(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]))
+ return true;
+ return false;
+}
+
+#ifndef __WIN32__
+int
+inode_get_unix_data(const struct wim_inode *inode,
+ struct wimlib_unix_data *unix_data,
+ u16 *stream_idx_ret)
+{
+ const struct wim_ads_entry *ads_entry;
+ const struct wim_lookup_table_entry *lte;
+ size_t size;
+ int ret;
+
+ wimlib_assert(inode->i_resolved);
+
+ 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;
+ 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.
+ *
+ * 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.
+ *
+ * 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 @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.
+ */
+int
+inode_resolve_streams(struct wim_inode *inode, struct wim_lookup_table *table,
+ bool force)
+{
+ const u8 *hash;
+
+ if (!inode->i_resolved) {
+ struct wim_lookup_table_entry *lte, *ads_lte;
+
+ /* Resolve the default file 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 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;
+ }
+ return 0;
+
+stream_not_found:
+ return stream_not_found_error(inode, hash);
+}
+
+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);
+ 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;
+ }
+}
+
+/*
+ * 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.
+ *
+ * This works for both resolved and un-resolved inodes.
+ */
+struct wim_lookup_table_entry *
+inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx,
+ const struct wim_lookup_table *table)
+{
+ if (inode->i_resolved)
+ return inode_stream_lte_resolved(inode, stream_idx);
+ else
+ return inode_stream_lte_unresolved(inode, stream_idx, table);
+}
+
+struct wim_lookup_table_entry *
+inode_unnamed_stream_resolved(const struct wim_inode *inode, u16 *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);
+}
+
+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;
+}
+
+/* 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 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.
+ */
+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);
+}
+
+/* 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. */
+const u8 *
+inode_unnamed_stream_hash(const struct wim_inode *inode)
+{
+ const u8 *hash;
+
+ 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;
+}
+
+/* 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)
+{
+ 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;
+}
+
+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(stderr, T(" SHA-1 message digest of missing stream:\n "));
+ print_hash(hash, stderr);
+ tputc(T('\n'), stderr);
+ }
+ return WIMLIB_ERR_RESOURCE_NOT_FOUND;
+}
+
+/*
+ * Reads 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.
+ *
+ * @remaining_size:
+ * Number of bytes of data remaining in the buffer pointed to by @p.
+ *
+ * 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
+ */
+int
+read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode,
+ size_t nbytes_remaining)
+{
+ 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 == NULL)
+ 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 = MALLOC(cur_entry->stream_name_nbytes + 2);
+ if (cur_entry->stream_name == NULL)
+ goto out_of_memory;
+
+ memcpy(cur_entry->stream_name,
+ disk_entry->stream_name,
+ cur_entry->stream_name_nbytes);
+ cur_entry->stream_name[cur_entry->stream_name_nbytes / 2] = cpu_to_le16(0);
+ } else {
+ /* Mark inode as having weird stream entries. */
+ inode->i_canonical_streams = 0;
+ }
+
+ /* 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) & ~(u64)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;
+ 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;
+}
+
+/*
+ * 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)
+{
+ struct wim_dentry *dentry;
+
+ /* 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;
+ }
+
+ /* 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);
+ }
+
+ /* Files cannot have multiple DOS names, even if they have multiple
+ * names in multiple directories (i.e. hard links).
+ * Source: NTFS-3g authors. */
+ struct wim_dentry *dentry_with_dos_name = NULL;
+ inode_for_each_dentry(dentry, inode) {
+ if (dentry_has_short_name(dentry)) {
+ if (dentry_with_dos_name) {
+ /* This was previously an error, but if we
+ * capture a WIM from UDF on Windows, hard links
+ * are supported but DOS names are automatically
+ * generated for all names for an inode. */
+ #if 0
+ ERROR("Hard-linked file has a DOS name at "
+ "both `%"TS"' and `%"TS"'",
+ dentry_full_path(dentry_with_dos_name),
+ dentry_full_path(dentry));
+ return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+ #else
+ dentry->dos_name_invalid = 1;
+ #endif
+ }
+ dentry_with_dos_name = dentry;
+ }
+ }
+ 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++;
+ }
+}
+
+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.
+ */
+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_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;
+}
/*
- * hardlink.c
+ * inode_fixup.c
*
- * Code to deal with hard links in WIMs.
+ * See dentry_tree_fix_inodes() for description.
*/
/*
# include "config.h"
#endif
-#include "wimlib/capture.h"
#include "wimlib/dentry.h"
#include "wimlib/error.h"
+#include "wimlib/inode.h"
+#include "wimlib/inode_table.h"
#include "wimlib/lookup_table.h"
-/* NULL NULL
- * ^ ^
- * dentry | |
- * / \ ----------- -----------
- * | dentry<---| struct | | struct |---> dentry
- * \ / | wim_inode| | wim_inode|
- * dentry ------------ ------------
- * ^ ^
- * | |
- * | | dentry
- * ----------- ----------- / \
- * dentry<---| struct | | struct |---> dentry dentry
- * / | wim_inode| | wim_inode| \ /
- * dentry ------------ ------------ dentry
- * ^ ^
- * | |
- * -----------------
- * wim_inode_table->array | idx 0 | idx 1 |
- * -----------------
- */
-
-
-int
-init_inode_table(struct wim_inode_table *table, size_t capacity)
-{
- table->array = CALLOC(capacity, sizeof(table->array[0]));
- if (!table->array) {
- 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;
-}
-
+/* Manual link count of inode (normally we can just check i_nlink) */
static inline size_t
inode_link_count(const struct wim_inode *inode)
{
return size;
}
-/* Insert a dentry into the inode table based on the inode number of the
- * attached inode (which came from the hard link group ID field of the on-disk
- * WIM dentry) */
-static int
-inode_table_insert(struct wim_dentry *dentry, void *_table)
-{
- struct wim_inode_table *table = _table;
- struct wim_inode *d_inode = dentry->d_inode;
-
- if (d_inode->i_ino == 0) {
- /* A dentry with a hard link group ID of 0 indicates that it's
- * in a hard link group by itself. Add it to the list of extra
- * inodes rather than inserting it into the hash lists. */
- list_add_tail(&d_inode->i_list, &table->extra_inodes);
- } else {
- size_t pos;
- struct wim_inode *inode;
- struct hlist_node *cur;
-
- /* Try adding this dentry to an existing inode */
- pos = d_inode->i_ino % table->capacity;
- hlist_for_each_entry(inode, cur, &table->array[pos], i_hlist) {
- if (inode->i_ino == d_inode->i_ino) {
- if (unlikely((inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) ||
- (d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)))
- {
- ERROR("Unsupported directory hard link "
- "\"%"TS"\" <=> \"%"TS"\"",
- dentry_full_path(dentry),
- dentry_full_path(inode_first_dentry(inode)));
- return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
- }
- inode_add_dentry(dentry, inode);
- return 0;
- }
- }
-
- /* No inode in the table has the same number as this one, so add
- * it to the table. */
- hlist_add_head(&d_inode->i_hlist, &table->array[pos]);
-
- /* XXX Make the table grow when too many entries have been
- * inserted. */
- table->num_entries++;
- }
- return 0;
-}
-
-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;
-}
-
-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++;
- }
-}
-
-/* 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.
- */
-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;
-}
-
static inline void
print_inode_dentries(const struct wim_inode *inode)
{
}
}
+static bool
+ads_entries_have_same_name(const struct wim_ads_entry *entry_1,
+ const struct wim_ads_entry *entry_2)
+{
+ return entry_1->stream_name_nbytes == entry_2->stream_name_nbytes &&
+ memcmp(entry_1->stream_name, entry_2->stream_name,
+ entry_1->stream_name_nbytes) == 0;
+}
+
static bool
ref_inodes_consistent(const struct wim_inode * restrict ref_inode_1,
const struct wim_inode * restrict ref_inode_2)
return 0;
}
+/* Insert a dentry into the inode table based on the inode number of the
+ * attached inode (which came from the hard link group ID field of the on-disk
+ * WIM dentry) */
+static int
+inode_table_insert(struct wim_dentry *dentry, void *_table)
+{
+ struct wim_inode_table *table = _table;
+ struct wim_inode *d_inode = dentry->d_inode;
+
+ if (d_inode->i_ino == 0) {
+ /* A dentry with a hard link group ID of 0 indicates that it's
+ * in a hard link group by itself. Add it to the list of extra
+ * inodes rather than inserting it into the hash lists. */
+ list_add_tail(&d_inode->i_list, &table->extra_inodes);
+ } else {
+ size_t pos;
+ struct wim_inode *inode;
+ struct hlist_node *cur;
+
+ /* Try adding this dentry to an existing inode */
+ pos = d_inode->i_ino % table->capacity;
+ hlist_for_each_entry(inode, cur, &table->array[pos], i_hlist) {
+ if (inode->i_ino == d_inode->i_ino) {
+ if (unlikely((inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) ||
+ (d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)))
+ {
+ ERROR("Unsupported directory hard link "
+ "\"%"TS"\" <=> \"%"TS"\"",
+ dentry_full_path(dentry),
+ dentry_full_path(inode_first_dentry(inode)));
+ return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+ }
+ inode_add_dentry(dentry, inode);
+ return 0;
+ }
+ }
+
+ /* No inode in the table has the same number as this one, so add
+ * it to the table. */
+ hlist_add_head(&d_inode->i_hlist, &table->array[pos]);
+
+ /* XXX Make the table grow when too many entries have been
+ * inserted. */
+ table->num_entries++;
+ }
+ return 0;
+}
+
+
/*
* dentry_tree_fix_inodes():
*
out:
return ret;
}
-
-/* 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_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;
-}
--- /dev/null
+/*
+ * iterate_dir.c
+ *
+ * Iterate through files in a WIM image.
+ * This is the stable API; internal code can just use for_dentry_in_tree().
+ */
+
+/*
+ * Copyright (C) 2013 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * 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.
+ *
+ * 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
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "wimlib.h"
+#include "wimlib/dentry.h"
+#include "wimlib/encoding.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/metadata.h"
+#include "wimlib/paths.h"
+#include "wimlib/security.h"
+#include "wimlib/timestamp.h"
+#include "wimlib/util.h"
+#include "wimlib/wim.h"
+
+static int
+init_wimlib_dentry(struct wimlib_dir_entry *wdentry,
+ struct wim_dentry *dentry,
+ const WIMStruct *wim,
+ int flags)
+{
+ int ret;
+ size_t dummy;
+ const struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+ const u8 *hash;
+
+#if TCHAR_IS_UTF16LE
+ wdentry->filename = dentry->file_name;
+ wdentry->dos_name = dentry->short_name;
+#else
+ if (dentry_has_long_name(dentry)) {
+ ret = utf16le_to_tstr(dentry->file_name,
+ dentry->file_name_nbytes,
+ (tchar**)&wdentry->filename,
+ &dummy);
+ if (ret)
+ return ret;
+ }
+ if (dentry_has_short_name(dentry)) {
+ ret = utf16le_to_tstr(dentry->short_name,
+ dentry->short_name_nbytes,
+ (tchar**)&wdentry->dos_name,
+ &dummy);
+ if (ret)
+ return ret;
+ }
+#endif
+ ret = calculate_dentry_full_path(dentry);
+ if (ret)
+ return ret;
+ wdentry->full_path = dentry->_full_path;
+
+ for (struct wim_dentry *d = dentry; !dentry_is_root(d); d = d->parent)
+ wdentry->depth++;
+
+ if (inode->i_security_id >= 0) {
+ const struct wim_security_data *sd = wim_const_security_data(wim);
+ wdentry->security_descriptor = sd->descriptors[inode->i_security_id];
+ wdentry->security_descriptor_size = sd->sizes[inode->i_security_id];
+ }
+ wdentry->reparse_tag = inode->i_reparse_tag;
+ wdentry->num_links = inode->i_nlink;
+ wdentry->attributes = inode->i_attributes;
+ wdentry->hard_link_group_id = inode->i_ino;
+ wdentry->creation_time = wim_timestamp_to_timespec(inode->i_creation_time);
+ wdentry->last_write_time = wim_timestamp_to_timespec(inode->i_last_write_time);
+ wdentry->last_access_time = wim_timestamp_to_timespec(inode->i_last_access_time);
+
+ lte = inode_unnamed_lte(inode, wim->lookup_table);
+ if (lte) {
+ lte_to_wimlib_resource_entry(lte, &wdentry->streams[0].resource);
+ } else if (!is_zero_hash(hash = inode_unnamed_stream_hash(inode))) {
+ if (flags & WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED)
+ return stream_not_found_error(inode, hash);
+ copy_hash(wdentry->streams[0].resource.sha1_hash, hash);
+ wdentry->streams[0].resource.is_missing = 1;
+ }
+
+ for (unsigned i = 0; i < inode->i_num_ads; i++) {
+ if (!ads_entry_is_named_stream(&inode->i_ads_entries[i]))
+ continue;
+ lte = inode_stream_lte(inode, i + 1, wim->lookup_table);
+ wdentry->num_named_streams++;
+ if (lte) {
+ lte_to_wimlib_resource_entry(lte, &wdentry->streams[
+ wdentry->num_named_streams].resource);
+ } else if (!is_zero_hash(hash = inode_stream_hash(inode, i + 1))) {
+ if (flags & WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED)
+ return stream_not_found_error(inode, hash);
+ copy_hash(wdentry->streams[
+ wdentry->num_named_streams].resource.sha1_hash, hash);
+ wdentry->streams[
+ wdentry->num_named_streams].resource.is_missing = 1;
+ }
+ #if TCHAR_IS_UTF16LE
+ wdentry->streams[wdentry->num_named_streams].stream_name =
+ inode->i_ads_entries[i].stream_name;
+ #else
+ size_t dummy;
+
+ ret = utf16le_to_tstr(inode->i_ads_entries[i].stream_name,
+ inode->i_ads_entries[i].stream_name_nbytes,
+ (tchar**)&wdentry->streams[
+ wdentry->num_named_streams].stream_name,
+ &dummy);
+ if (ret)
+ return ret;
+ #endif
+ }
+ return 0;
+}
+
+static void
+free_wimlib_dentry(struct wimlib_dir_entry *wdentry)
+{
+#if !TCHAR_IS_UTF16LE
+ FREE((tchar*)wdentry->filename);
+ FREE((tchar*)wdentry->dos_name);
+ for (unsigned i = 1; i <= wdentry->num_named_streams; i++)
+ FREE((tchar*)wdentry->streams[i].stream_name);
+#endif
+ FREE(wdentry);
+}
+
+struct iterate_dir_tree_ctx {
+ WIMStruct *wim;
+ int flags;
+ wimlib_iterate_dir_tree_callback_t cb;
+ void *user_ctx;
+};
+
+static int
+do_iterate_dir_tree(WIMStruct *wim,
+ struct wim_dentry *dentry, int flags,
+ wimlib_iterate_dir_tree_callback_t cb,
+ void *user_ctx);
+
+static int
+call_do_iterate_dir_tree(struct wim_dentry *dentry, void *_ctx)
+{
+ struct iterate_dir_tree_ctx *ctx = _ctx;
+ return do_iterate_dir_tree(ctx->wim, dentry, ctx->flags,
+ ctx->cb, ctx->user_ctx);
+}
+
+static int
+do_iterate_dir_tree(WIMStruct *wim,
+ struct wim_dentry *dentry, int flags,
+ wimlib_iterate_dir_tree_callback_t cb,
+ void *user_ctx)
+{
+ struct wimlib_dir_entry *wdentry;
+ int ret = WIMLIB_ERR_NOMEM;
+
+
+ wdentry = CALLOC(1, sizeof(struct wimlib_dir_entry) +
+ (1 + dentry->d_inode->i_num_ads) *
+ sizeof(struct wimlib_stream_entry));
+ if (wdentry == NULL)
+ goto out;
+
+ ret = init_wimlib_dentry(wdentry, dentry, wim, flags);
+ if (ret)
+ goto out_free_wimlib_dentry;
+
+ if (!(flags & WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN)) {
+ ret = (*cb)(wdentry, user_ctx);
+ if (ret)
+ goto out_free_wimlib_dentry;
+ }
+
+ if (flags & (WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE |
+ WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN))
+ {
+ struct iterate_dir_tree_ctx ctx = {
+ .wim = wim,
+ .flags = flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN,
+ .cb = cb,
+ .user_ctx = user_ctx,
+ };
+ ret = for_dentry_child(dentry, call_do_iterate_dir_tree, &ctx);
+ }
+out_free_wimlib_dentry:
+ free_wimlib_dentry(wdentry);
+out:
+ return ret;
+}
+
+struct image_iterate_dir_tree_ctx {
+ const tchar *path;
+ int flags;
+ wimlib_iterate_dir_tree_callback_t cb;
+ void *user_ctx;
+};
+
+
+static int
+image_do_iterate_dir_tree(WIMStruct *wim)
+{
+ struct image_iterate_dir_tree_ctx *ctx = wim->private;
+ struct wim_dentry *dentry;
+
+ dentry = get_dentry(wim, ctx->path, WIMLIB_CASE_PLATFORM_DEFAULT);
+ if (dentry == NULL)
+ return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
+ return do_iterate_dir_tree(wim, dentry, ctx->flags, ctx->cb, ctx->user_ctx);
+}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_iterate_dir_tree(WIMStruct *wim, int image, const tchar *_path,
+ int flags,
+ wimlib_iterate_dir_tree_callback_t cb, void *user_ctx)
+{
+ tchar *path;
+ int ret;
+
+ path = canonicalize_wim_path(_path);
+ if (path == NULL)
+ return WIMLIB_ERR_NOMEM;
+ struct image_iterate_dir_tree_ctx ctx = {
+ .path = path,
+ .flags = flags,
+ .cb = cb,
+ .user_ctx = user_ctx,
+ };
+ wim->private = &ctx;
+ ret = for_image(wim, image, image_do_iterate_dir_tree);
+ FREE(path);
+ return ret;
+}
# include "config.h"
#endif
+#include "wimlib/assert.h"
#include "wimlib/endianness.h"
#include "wimlib/error.h"
-#include "wimlib/file_io.h"
-#include "wimlib/glob.h"
#include "wimlib/lookup_table.h"
#include "wimlib/metadata.h"
-#include "wimlib/paths.h"
+#include "wimlib/ntfs_3g.h"
#include "wimlib/resource.h"
#include "wimlib/util.h"
#include "wimlib/write.h"
-#include <errno.h>
#include <stdlib.h>
-#ifdef WITH_FUSE
-# include <unistd.h> /* for unlink() */
-#endif
+#include <string.h>
+#include <unistd.h> /* for unlink() */
+
+/* WIM lookup table:
+ *
+ * This is a logical mapping from SHA1 message digests to the data streams
+ * contained in a WIM.
+ *
+ * Here it is implemented as a hash table.
+ *
+ * Note: Everything will break horribly if there is a SHA1 collision.
+ */
+struct wim_lookup_table {
+ struct hlist_head *array;
+ size_t num_entries;
+ size_t capacity;
+};
struct wim_lookup_table *
new_lookup_table(size_t capacity)
struct wim_lookup_table *table;
struct hlist_head *array;
- table = CALLOC(1, sizeof(struct wim_lookup_table));
- if (table) {
- array = CALLOC(capacity, sizeof(array[0]));
- if (array) {
- table->num_entries = 0;
- table->capacity = capacity;
- table->array = array;
- } else {
- FREE(table);
- table = NULL;
- ERROR("Failed to allocate memory for lookup table "
- "with capacity %zu", capacity);
- }
+ table = MALLOC(sizeof(struct wim_lookup_table));
+ if (table == NULL)
+ goto oom;
+
+ array = CALLOC(capacity, sizeof(array[0]));
+ if (array == NULL) {
+ FREE(table);
+ goto oom;
}
+
+ table->num_entries = 0;
+ table->capacity = capacity;
+ table->array = array;
return table;
+
+oom:
+ ERROR("Failed to allocate memory for lookup table "
+ "with capacity %zu", capacity);
+ return NULL;
+}
+
+static int
+do_free_lookup_table_entry(struct wim_lookup_table_entry *entry, void *ignore)
+{
+ free_lookup_table_entry(entry);
+ return 0;
+}
+
+void
+free_lookup_table(struct wim_lookup_table *table)
+{
+ DEBUG("Freeing lookup table.");
+ if (table == NULL)
+ return;
+
+ if (table->array) {
+ for_lookup_table_entry(table,
+ do_free_lookup_table_entry,
+ NULL);
+ FREE(table->array);
+ }
+ FREE(table);
}
struct wim_lookup_table_entry *
struct wim_lookup_table_entry *lte;
lte = CALLOC(1, sizeof(struct wim_lookup_table_entry));
- if (lte == NULL) {
- ERROR("Out of memory (tried to allocate %zu bytes for "
- "lookup table entry)",
- sizeof(struct wim_lookup_table_entry));
+ if (lte == NULL)
return NULL;
- }
+
lte->refcnt = 1;
+
+ /* lte->resource_location = RESOURCE_NONEXISTENT */
BUILD_BUG_ON(RESOURCE_NONEXISTENT != 0);
+
return lte;
}
break;
}
return new;
+
out_free:
free_lookup_table_entry(new);
return NULL;
}
}
-static int
-do_free_lookup_table_entry(struct wim_lookup_table_entry *entry, void *ignore)
+/* Decrements the reference count for the lookup table entry @lte. If its
+ * reference count reaches 0, it is unlinked from the lookup table. If,
+ * furthermore, the entry has no opened file descriptors associated with it, the
+ * entry is freed. */
+void
+lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
+ struct wim_lookup_table *table)
{
- free_lookup_table_entry(entry);
- return 0;
-}
+ wimlib_assert(lte->refcnt != 0);
+ if (--lte->refcnt == 0) {
+ if (lte->unhashed) {
+ list_del(<e->unhashed_list);
+ #ifdef WITH_FUSE
+ /* If the stream has been extracted to a staging file
+ * for a FUSE mount, unlink the staging file. (Note
+ * that there still may be open file descriptors to it.)
+ * */
+ if (lte->resource_location == RESOURCE_IN_STAGING_FILE)
+ unlink(lte->staging_file_name);
+ #endif
+ } else {
+ lookup_table_unlink(table, lte);
+ }
+
+ /* If FUSE mounts are enabled, we don't actually free the entry
+ * until the last file descriptor has been closed by
+ * lte_decrement_num_opened_fds(). */
+#ifdef WITH_FUSE
+ if (lte->num_opened_fds == 0)
+#endif
+ free_lookup_table_entry(lte);
+ }
+}
+#ifdef WITH_FUSE
void
-free_lookup_table(struct wim_lookup_table *table)
+lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte)
{
- DEBUG("Freeing lookup table.");
- if (table) {
- if (table->array) {
- for_lookup_table_entry(table,
- do_free_lookup_table_entry,
- NULL);
- FREE(table->array);
- }
- FREE(table);
- }
+ wimlib_assert(lte->num_opened_fds != 0);
+
+ if (--lte->num_opened_fds == 0 && lte->refcnt == 0)
+ free_lookup_table_entry(lte);
}
+#endif
static void
lookup_table_insert_raw(struct wim_lookup_table *table,
FREE(old_array);
}
-
-/*
- * Inserts an entry into the lookup table.
- *
- * @table: A pointer to the lookup table.
- * @lte: A pointer to the entry to insert.
- */
+/* Inserts an entry into the lookup table. */
void
lookup_table_insert(struct wim_lookup_table *table,
struct wim_lookup_table_entry *lte)
enlarge_lookup_table(table);
}
-static void
-finalize_lte(struct wim_lookup_table_entry *lte)
-{
- #ifdef WITH_FUSE
- if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
- unlink(lte->staging_file_name);
- list_del(<e->unhashed_list);
- }
- #endif
- free_lookup_table_entry(lte);
-}
-
-/* Decrements the reference count for the lookup table entry @lte. If its
- * reference count reaches 0, it is unlinked from the lookup table. If,
- * furthermore, the entry has no opened file descriptors associated with it, the
- * entry is freed. */
+/* Unlinks a lookup table entry from the table; does not free it. */
void
-lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
- struct wim_lookup_table *table)
+lookup_table_unlink(struct wim_lookup_table *table,
+ struct wim_lookup_table_entry *lte)
{
- wimlib_assert(lte != NULL);
- wimlib_assert(lte->refcnt != 0);
- if (--lte->refcnt == 0) {
- if (lte->unhashed)
- list_del(<e->unhashed_list);
- else
- lookup_table_unlink(table, lte);
- #ifdef WITH_FUSE
- if (lte->num_opened_fds == 0)
- #endif
- finalize_lte(lte);
- }
+ wimlib_assert(!lte->unhashed);
+ wimlib_assert(table->num_entries != 0);
+
+ hlist_del(<e->hash_list);
+ table->num_entries--;
}
-#ifdef WITH_FUSE
-void
-lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte)
+/* Given a SHA1 message digest, return the corresponding entry in the WIM's
+ * lookup table, or NULL if there is none. */
+struct wim_lookup_table_entry *
+lookup_stream(const struct wim_lookup_table *table, const u8 hash[])
{
- if (lte->num_opened_fds != 0)
- if (--lte->num_opened_fds == 0 && lte->refcnt == 0)
- finalize_lte(lte);
+ size_t i;
+ struct wim_lookup_table_entry *lte;
+ struct hlist_node *pos;
+
+ i = *(size_t*)hash % table->capacity;
+ hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
+ if (hashes_equal(hash, lte->hash))
+ return lte;
+ return NULL;
}
-#endif
/* Calls a function on all the entries in the WIM lookup table. Stop early and
* return nonzero if any call to the function returns nonzero. */
/* Lookup table entry for a stream that is not a metadata
* resource. */
- duplicate_entry = lookup_resource(table, cur_entry->hash);
+ duplicate_entry = lookup_stream(table, cur_entry->hash);
if (duplicate_entry) {
if (wimlib_print_errors) {
WARNING("The WIM lookup table contains two entries with the "
return 0;
}
+/* Allocate a stream entry for the contents of the buffer, or re-use an existing
+ * entry in @lookup_table for the same stream. */
+struct wim_lookup_table_entry *
+new_stream_from_data_buffer(const void *buffer, size_t size,
+ struct wim_lookup_table *lookup_table)
+{
+ u8 hash[SHA1_HASH_SIZE];
+ struct wim_lookup_table_entry *lte, *existing_lte;
+
+ sha1_buffer(buffer, size, hash);
+ existing_lte = lookup_stream(lookup_table, hash);
+ if (existing_lte) {
+ wimlib_assert(existing_lte->size == size);
+ lte = existing_lte;
+ lte->refcnt++;
+ } else {
+ void *buffer_copy;
+ lte = new_lookup_table_entry();
+ if (lte == NULL)
+ return NULL;
+ buffer_copy = memdup(buffer, size);
+ if (buffer_copy == NULL) {
+ free_lookup_table_entry(lte);
+ return NULL;
+ }
+ lte->resource_location = RESOURCE_IN_ATTACHED_BUFFER;
+ lte->attached_buffer = buffer_copy;
+ lte->size = size;
+ copy_hash(lte->hash, hash);
+ lookup_table_insert(lookup_table, lte);
+ }
+ return lte;
+}
+
+/* Calculate the SHA1 message digest of a stream and move it from the list of
+ * unhashed streams to the stream lookup table, possibly joining it with an
+ * existing lookup table entry for an identical stream.
+ *
+ * @lte: An unhashed lookup table entry.
+ * @lookup_table: Lookup table for the WIM.
+ * @lte_ret: On success, write a pointer to the resulting lookup table
+ * entry to this location. This will be the same as @lte
+ * if it was inserted into the lookup table, or different if
+ * a duplicate stream was found.
+ *
+ * Returns 0 on success; nonzero if there is an error reading the stream.
+ */
+int
+hash_unhashed_stream(struct wim_lookup_table_entry *lte,
+ struct wim_lookup_table *lookup_table,
+ struct wim_lookup_table_entry **lte_ret)
+{
+ int ret;
+ struct wim_lookup_table_entry *duplicate_lte;
+ struct wim_lookup_table_entry **back_ptr;
+
+ wimlib_assert(lte->unhashed);
+
+ /* back_ptr must be saved because @back_inode and @back_stream_id are in
+ * union with the SHA1 message digest and will no longer be valid once
+ * the SHA1 has been calculated. */
+ back_ptr = retrieve_lte_pointer(lte);
+
+ ret = sha1_stream(lte);
+ if (ret)
+ return ret;
+
+ /* Look for a duplicate stream */
+ duplicate_lte = lookup_stream(lookup_table, lte->hash);
+ list_del(<e->unhashed_list);
+ if (duplicate_lte) {
+ /* We have a duplicate stream. Transfer the reference counts
+ * from this stream to the duplicate and update the reference to
+ * this stream (in an inode or ads_entry) to point to the
+ * duplicate. The caller is responsible for freeing @lte if
+ * needed. */
+ wimlib_assert(!(duplicate_lte->unhashed));
+ wimlib_assert(duplicate_lte->size == lte->size);
+ duplicate_lte->refcnt += lte->refcnt;
+ lte->refcnt = 0;
+ *back_ptr = duplicate_lte;
+ lte = duplicate_lte;
+ } else {
+ /* No duplicate stream, so we need to insert this stream into
+ * the lookup table and treat it as a hashed stream. */
+ lookup_table_insert(lookup_table, lte);
+ lte->unhashed = 0;
+ }
+ *lte_ret = lte;
+ return 0;
+}
+
void
print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out)
{
}
return for_lookup_table_entry(wim->lookup_table, do_iterate_lte, &ctx);
}
-
-/* Given a SHA1 message digest, return the corresponding entry in the WIM's
- * lookup table, or NULL if there is none. */
-struct wim_lookup_table_entry *
-lookup_resource(const struct wim_lookup_table *table, const u8 hash[])
-{
- size_t i;
- struct wim_lookup_table_entry *lte;
- struct hlist_node *pos;
-
- wimlib_assert(table != NULL);
- wimlib_assert(hash != NULL);
-
- i = *(size_t*)hash % table->capacity;
- hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
- if (hashes_equal(hash, lte->hash))
- return lte;
- return NULL;
-}
-
-#ifdef WITH_FUSE
-/*
- * Finds the dentry, lookup table entry, and stream index for a WIM file stream,
- * given a path name.
- *
- * This is only for pre-resolved inodes.
- */
-int
-wim_pathname_to_stream(WIMStruct *wim,
- const tchar *path,
- int lookup_flags,
- struct wim_dentry **dentry_ret,
- struct wim_lookup_table_entry **lte_ret,
- u16 *stream_idx_ret)
-{
- struct wim_dentry *dentry;
- struct wim_lookup_table_entry *lte;
- u16 stream_idx;
- const tchar *stream_name = NULL;
- struct wim_inode *inode;
- tchar *p = NULL;
-
- if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
- stream_name = path_stream_name(path);
- if (stream_name) {
- p = (tchar*)stream_name - 1;
- *p = T('\0');
- }
- }
-
- dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
- if (p)
- *p = T(':');
- if (!dentry)
- return -errno;
-
- inode = dentry->d_inode;
-
- if (!inode->i_resolved)
- if (inode_resolve_ltes(inode, wim->lookup_table, false))
- return -EIO;
-
- if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
- && inode_is_directory(inode))
- return -EISDIR;
-
- if (stream_name) {
- struct wim_ads_entry *ads_entry;
- u16 ads_idx;
- ads_entry = inode_get_ads_entry(inode, stream_name,
- &ads_idx);
- if (ads_entry) {
- stream_idx = ads_idx + 1;
- lte = ads_entry->lte;
- goto out;
- } else {
- return -ENOENT;
- }
- } else {
- lte = inode_unnamed_stream_resolved(inode, &stream_idx);
- }
-out:
- if (dentry_ret)
- *dentry_ret = dentry;
- if (lte_ret)
- *lte_ret = lte;
- if (stream_idx_ret)
- *stream_idx_ret = stream_idx;
- return 0;
-}
-#endif
-
-int
-resource_not_found_error(const struct wim_inode *inode, const u8 *hash)
-{
- if (wimlib_print_errors) {
- ERROR("\"%"TS"\": resource not found", inode_first_full_path(inode));
- tfprintf(stderr, T(" SHA-1 message digest of missing resource:\n "));
- print_hash(hash, stderr);
- tputc(T('\n'), stderr);
- }
- return WIMLIB_ERR_RESOURCE_NOT_FOUND;
-}
-
-/*
- * Resolve an inode's lookup table entries.
- *
- * 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.
- *
- * 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 @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.
- */
-int
-inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table,
- bool force)
-{
- const u8 *hash;
-
- if (!inode->i_resolved) {
- struct wim_lookup_table_entry *lte, *ads_lte;
-
- /* Resolve the default file stream */
- lte = NULL;
- hash = inode->i_hash;
- if (!is_zero_hash(hash)) {
- lte = lookup_resource(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 resource_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_resource(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 resource_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;
- }
- return 0;
-
-resource_not_found:
- return resource_not_found_error(inode, hash);
-}
-
-void
-inode_unresolve_ltes(struct wim_inode *inode)
-{
- if (inode->i_resolved) {
- 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;
- }
-}
-
-/*
- * 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.
- *
- * This works for both resolved and un-resolved inodes.
- */
-struct wim_lookup_table_entry *
-inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx,
- const struct wim_lookup_table *table)
-{
- if (inode->i_resolved)
- return inode_stream_lte_resolved(inode, stream_idx);
- else
- return inode_stream_lte_unresolved(inode, stream_idx, table);
-}
-
-struct wim_lookup_table_entry *
-inode_unnamed_stream_resolved(const struct wim_inode *inode, u16 *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);
-}
-
-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;
-}
-
-/* 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 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.
- */
-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);
-}
-
-/* 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. */
-const u8 *
-inode_unnamed_stream_hash(const struct wim_inode *inode)
-{
- const u8 *hash;
-
- 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;
-}
-
-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;
-}
-
-/* Calculate the SHA1 message digest of a stream and move it from the list of
- * unhashed streams to the stream lookup table, possibly joining it with an
- * existing lookup table entry for an identical stream.
- *
- * @lte: An unhashed lookup table entry.
- * @lookup_table: Lookup table for the WIM.
- * @lte_ret: On success, write a pointer to the resulting lookup table
- * entry to this location. This will be the same as @lte
- * if it was inserted into the lookup table, or different if
- * a duplicate stream was found.
- *
- * Returns 0 on success; nonzero if there is an error reading the stream.
- */
-int
-hash_unhashed_stream(struct wim_lookup_table_entry *lte,
- struct wim_lookup_table *lookup_table,
- struct wim_lookup_table_entry **lte_ret)
-{
- int ret;
- struct wim_lookup_table_entry *duplicate_lte;
- struct wim_lookup_table_entry **back_ptr;
-
- wimlib_assert(lte->unhashed);
-
- /* back_ptr must be saved because @back_inode and @back_stream_id are in
- * union with the SHA1 message digest and will no longer be valid once
- * the SHA1 has been calculated. */
- back_ptr = retrieve_lte_pointer(lte);
-
- ret = sha1_stream(lte);
- if (ret)
- return ret;
-
- /* Look for a duplicate stream */
- duplicate_lte = lookup_resource(lookup_table, lte->hash);
- list_del(<e->unhashed_list);
- if (duplicate_lte) {
- /* We have a duplicate stream. Transfer the reference counts
- * from this stream to the duplicate and update the reference to
- * this stream (in an inode or ads_entry) to point to the
- * duplicate. The caller is responsible for freeing @lte if
- * needed. */
- wimlib_assert(!(duplicate_lte->unhashed));
- wimlib_assert(duplicate_lte->size == lte->size);
- duplicate_lte->refcnt += lte->refcnt;
- lte->refcnt = 0;
- *back_ptr = duplicate_lte;
- lte = duplicate_lte;
- } else {
- /* No duplicate stream, so we need to insert this stream into
- * the lookup table and treat it as a hashed stream. */
- lookup_table_insert(lookup_table, lte);
- lte->unhashed = 0;
- }
- *lte_ret = lte;
- return 0;
-}
-
-static int
-lte_clone_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
-{
- struct wim_lookup_table *lookup_table = _lookup_table;
-
- if (lookup_resource(lookup_table, lte->hash))
- return 0; /* Resource already present. */
-
- lte = clone_lookup_table_entry(lte);
- if (lte == NULL)
- return WIMLIB_ERR_NOMEM;
- lte->out_refcnt = 1;
- lookup_table_insert(lookup_table, lte);
- return 0;
-}
-
-static int
-lte_delete_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
-{
- struct wim_lookup_table *lookup_table = _lookup_table;
-
- if (lte->out_refcnt) {
- lookup_table_unlink(lookup_table, lte);
- free_lookup_table_entry(lte);
- }
- return 0;
-}
-
-/* API function documented in wimlib.h */
-WIMLIBAPI int
-wimlib_reference_resources(WIMStruct *wim,
- WIMStruct **resource_wims, unsigned num_resource_wims,
- int ref_flags)
-{
- int ret;
- unsigned i;
-
- if (wim == NULL)
- return WIMLIB_ERR_INVALID_PARAM;
-
- if (num_resource_wims != 0 && resource_wims == NULL)
- return WIMLIB_ERR_INVALID_PARAM;
-
- for (i = 0; i < num_resource_wims; i++)
- if (resource_wims[i] == NULL)
- return WIMLIB_ERR_INVALID_PARAM;
-
- for_lookup_table_entry(wim->lookup_table, lte_zero_out_refcnt, NULL);
-
- for (i = 0; i < num_resource_wims; i++) {
- ret = for_lookup_table_entry(resource_wims[i]->lookup_table,
- lte_clone_if_new,
- wim->lookup_table);
- if (ret)
- goto out_rollback;
- }
- return 0;
-
-out_rollback:
- for_lookup_table_entry(wim->lookup_table, lte_delete_if_new,
- wim->lookup_table);
- return ret;
-}
-
-static int
-reference_resource_paths(WIMStruct *wim,
- const tchar * const *resource_wimfiles,
- unsigned num_resource_wimfiles,
- int ref_flags,
- int open_flags,
- wimlib_progress_func_t progress_func)
-{
- WIMStruct **resource_wims;
- unsigned i;
- int ret;
-
- resource_wims = CALLOC(num_resource_wimfiles, sizeof(resource_wims[0]));
- if (!resource_wims)
- return WIMLIB_ERR_NOMEM;
-
- for (i = 0; i < num_resource_wimfiles; i++) {
- DEBUG("Referencing resources from path \"%"TS"\"",
- resource_wimfiles[i]);
- ret = wimlib_open_wim(resource_wimfiles[i], open_flags,
- &resource_wims[i], progress_func);
- if (ret)
- goto out_free_resource_wims;
- }
-
- ret = wimlib_reference_resources(wim, resource_wims,
- num_resource_wimfiles, ref_flags);
- if (ret)
- goto out_free_resource_wims;
-
- for (i = 0; i < num_resource_wimfiles; i++)
- list_add_tail(&resource_wims[i]->subwim_node, &wim->subwims);
-
- ret = 0;
- goto out_free_array;
-
-out_free_resource_wims:
- for (i = 0; i < num_resource_wimfiles; i++)
- wimlib_free(resource_wims[i]);
-out_free_array:
- FREE(resource_wims);
- return ret;
-}
-
-static int
-reference_resource_glob(WIMStruct *wim, const tchar *refglob,
- int ref_flags, int open_flags,
- wimlib_progress_func_t progress_func)
-{
- glob_t globbuf;
- int ret;
-
- /* Note: glob() is replaced in Windows native builds. */
- ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
- if (ret) {
- if (ret == GLOB_NOMATCH) {
- if (ref_flags & WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH) {
- ERROR("Found no files for glob \"%"TS"\"", refglob);
- return WIMLIB_ERR_GLOB_HAD_NO_MATCHES;
- } else {
- return reference_resource_paths(wim,
- &refglob,
- 1,
- ref_flags,
- open_flags,
- progress_func);
- }
- } else {
- ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob);
- if (ret == GLOB_NOSPACE)
- return WIMLIB_ERR_NOMEM;
- else
- return WIMLIB_ERR_READ;
- }
- }
-
- ret = reference_resource_paths(wim,
- (const tchar * const *)globbuf.gl_pathv,
- globbuf.gl_pathc,
- ref_flags,
- open_flags,
- progress_func);
- globfree(&globbuf);
- return ret;
-}
-
-/* API function documented in wimlib.h */
-WIMLIBAPI int
-wimlib_reference_resource_files(WIMStruct *wim,
- const tchar * const * resource_wimfiles_or_globs,
- unsigned count,
- int ref_flags,
- int open_flags,
- wimlib_progress_func_t progress_func)
-{
- unsigned i;
- int ret;
-
- if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE) {
- for (i = 0; i < count; i++) {
- ret = reference_resource_glob(wim,
- resource_wimfiles_or_globs[i],
- ref_flags,
- open_flags,
- progress_func);
- if (ret)
- return ret;
- }
- return 0;
- } else {
- return reference_resource_paths(wim, resource_wimfiles_or_globs,
- count, ref_flags,
- open_flags, progress_func);
- }
-}
#include "wimlib/encoding.h"
#include "wimlib/file_io.h"
+#include "wimlib/dentry.h"
+#include "wimlib/inode.h"
#include "wimlib/lookup_table.h"
#include "wimlib/metadata.h"
#include "wimlib/paths.h"
return wimfs_get_context()->wim;
}
-static inline bool
-wimfs_ctx_readonly(const struct wimfs_context *ctx)
-{
- return (ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) == 0;
-}
-
static inline int
get_lookup_flags(const struct wimfs_context *ctx)
{
* @stream_id: ID of the stream we're opening
* @lte: Lookup table entry for the stream (may be NULL)
* @fd_ret: Return the allocated file descriptor if successful.
- * @readonly: True if this is a read-only mount.
*
* Return 0 iff successful or error code if unsuccessful.
*/
alloc_wimfs_fd(struct wim_inode *inode,
u32 stream_id,
struct wim_lookup_table_entry *lte,
- struct wimfs_fd **fd_ret,
- bool readonly)
+ struct wimfs_fd **fd_ret)
{
static const u16 fds_per_alloc = 8;
static const u16 max_fds = 0xffff;
*fd_ret = fd;
inode->i_fds[i] = fd;
inode->i_num_opened_fds++;
- if (lte && !readonly)
+ if (lte)
lte->num_opened_fds++;
DEBUG("Allocated fd (idx = %u)", fd->idx);
ret = 0;
new_lte->staging_file_name = staging_file_name;
new_lte->size = size;
- lookup_table_insert_unhashed(ctx->wim->lookup_table, new_lte,
- inode, stream_id);
+ add_unhashed_stream(new_lte, inode, stream_id,
+ &wim_get_current_image_metadata(ctx->wim)->unhashed_streams);
*retrieve_lte_pointer(new_lte) = new_lte;
*lte = new_lte;
return 0;
*back_ptr = lte;
}
- ret = alloc_wimfs_fd(inode, stream_id, lte, &fd,
- wimfs_ctx_readonly(ctx));
+ ret = alloc_wimfs_fd(inode, stream_id, lte, &fd);
if (ret)
return ret;
return -errno;
if (!inode_is_directory(inode))
return -ENOTDIR;
- ret = alloc_wimfs_fd(inode, 0, NULL, &fd, wimfs_ctx_readonly(ctx));
+ ret = alloc_wimfs_fd(inode, 0, NULL, &fd);
fi->fh = (uintptr_t)fd;
return ret;
}
imd = wim_get_current_image_metadata(wim);
- if (imd->refcnt != 1) {
- ERROR("Cannot mount image that was just exported with "
- "wimlib_export_image()");
- return WIMLIB_ERR_INVALID_PARAM;
- }
-
if (imd->modified) {
- ERROR("Cannot mount image that was added "
- "with wimlib_add_image()");
+ /* wimfs_read() only supports a limited number of stream
+ * locations, not including RESOURCE_IN_FILE_ON_DISK,
+ * RESOURCE_IN_NTFS_VOLUME, etc. that might appear if files were
+ * added to the WIM image. */
+ ERROR("Cannot mount an image with newly added files!");
return WIMLIB_ERR_INVALID_PARAM;
}
ctx.default_uid = getuid();
ctx.default_gid = getgid();
wimlib_assert(list_empty(&imd->unhashed_streams));
- ctx.wim->lookup_table->unhashed_streams = &imd->unhashed_streams;
if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK;
}
-/* Load the streams from a file or reparse point in the NTFS volume into the WIM
- * lookup table */
+/* Load the streams from a file or reparse point in the NTFS volume */
static int
capture_ntfs_streams(struct wim_inode *inode,
ntfs_inode *ni,
char *path,
size_t path_len,
- struct wim_lookup_table *lookup_table,
+ struct list_head *unhashed_streams,
ntfs_volume *vol,
ATTR_TYPES type)
{
new_ads_entry->lte = lte;
}
if (lte) {
- lookup_table_insert_unhashed(lookup_table, lte,
- inode, stream_id);
+ add_unhashed_stream(lte, inode,
+ stream_id, unhashed_streams);
}
}
if (errno == ENOENT) {
* - Reparse points: capture reparse data only
*/
ret = capture_ntfs_streams(inode, ni, path, path_len,
- params->lookup_table, vol, stream_type);
+ params->unhashed_streams, vol, stream_type);
if (ret)
goto out;
--- /dev/null
+/*
+ * reference.c
+ *
+ * Reference resources from external WIM file(s).
+ */
+
+/*
+ * Copyright (C) 2013 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * 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.
+ *
+ * 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
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "wimlib.h"
+#include "wimlib/error.h"
+#include "wimlib/glob.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/wim.h"
+
+static int
+lte_clone_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
+{
+ struct wim_lookup_table *lookup_table = _lookup_table;
+
+ if (lookup_stream(lookup_table, lte->hash))
+ return 0; /* Resource already present. */
+
+ lte = clone_lookup_table_entry(lte);
+ if (lte == NULL)
+ return WIMLIB_ERR_NOMEM;
+ lte->out_refcnt = 1;
+ lookup_table_insert(lookup_table, lte);
+ return 0;
+}
+
+static int
+lte_delete_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
+{
+ struct wim_lookup_table *lookup_table = _lookup_table;
+
+ if (lte->out_refcnt) {
+ lookup_table_unlink(lookup_table, lte);
+ free_lookup_table_entry(lte);
+ }
+ return 0;
+}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_reference_resources(WIMStruct *wim,
+ WIMStruct **resource_wims, unsigned num_resource_wims,
+ int ref_flags)
+{
+ int ret;
+ unsigned i;
+
+ if (wim == NULL)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ if (num_resource_wims != 0 && resource_wims == NULL)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ for (i = 0; i < num_resource_wims; i++)
+ if (resource_wims[i] == NULL)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ for_lookup_table_entry(wim->lookup_table, lte_zero_out_refcnt, NULL);
+
+ for (i = 0; i < num_resource_wims; i++) {
+ ret = for_lookup_table_entry(resource_wims[i]->lookup_table,
+ lte_clone_if_new,
+ wim->lookup_table);
+ if (ret)
+ goto out_rollback;
+ }
+ return 0;
+
+out_rollback:
+ for_lookup_table_entry(wim->lookup_table, lte_delete_if_new,
+ wim->lookup_table);
+ return ret;
+}
+
+static int
+reference_resource_paths(WIMStruct *wim,
+ const tchar * const *resource_wimfiles,
+ unsigned num_resource_wimfiles,
+ int ref_flags,
+ int open_flags,
+ wimlib_progress_func_t progress_func)
+{
+ WIMStruct **resource_wims;
+ unsigned i;
+ int ret;
+
+ resource_wims = CALLOC(num_resource_wimfiles, sizeof(resource_wims[0]));
+ if (!resource_wims)
+ return WIMLIB_ERR_NOMEM;
+
+ for (i = 0; i < num_resource_wimfiles; i++) {
+ DEBUG("Referencing resources from path \"%"TS"\"",
+ resource_wimfiles[i]);
+ ret = wimlib_open_wim(resource_wimfiles[i], open_flags,
+ &resource_wims[i], progress_func);
+ if (ret)
+ goto out_free_resource_wims;
+ }
+
+ ret = wimlib_reference_resources(wim, resource_wims,
+ num_resource_wimfiles, ref_flags);
+ if (ret)
+ goto out_free_resource_wims;
+
+ for (i = 0; i < num_resource_wimfiles; i++)
+ list_add_tail(&resource_wims[i]->subwim_node, &wim->subwims);
+
+ ret = 0;
+ goto out_free_array;
+
+out_free_resource_wims:
+ for (i = 0; i < num_resource_wimfiles; i++)
+ wimlib_free(resource_wims[i]);
+out_free_array:
+ FREE(resource_wims);
+ return ret;
+}
+
+static int
+reference_resource_glob(WIMStruct *wim, const tchar *refglob,
+ int ref_flags, int open_flags,
+ wimlib_progress_func_t progress_func)
+{
+ glob_t globbuf;
+ int ret;
+
+ /* Note: glob() is replaced in Windows native builds. */
+ ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
+ if (ret) {
+ if (ret == GLOB_NOMATCH) {
+ if (ref_flags & WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH) {
+ ERROR("Found no files for glob \"%"TS"\"", refglob);
+ return WIMLIB_ERR_GLOB_HAD_NO_MATCHES;
+ } else {
+ return reference_resource_paths(wim,
+ &refglob,
+ 1,
+ ref_flags,
+ open_flags,
+ progress_func);
+ }
+ } else {
+ ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob);
+ if (ret == GLOB_NOSPACE)
+ return WIMLIB_ERR_NOMEM;
+ else
+ return WIMLIB_ERR_READ;
+ }
+ }
+
+ ret = reference_resource_paths(wim,
+ (const tchar * const *)globbuf.gl_pathv,
+ globbuf.gl_pathc,
+ ref_flags,
+ open_flags,
+ progress_func);
+ globfree(&globbuf);
+ return ret;
+}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_reference_resource_files(WIMStruct *wim,
+ const tchar * const * resource_wimfiles_or_globs,
+ unsigned count,
+ int ref_flags,
+ int open_flags,
+ wimlib_progress_func_t progress_func)
+{
+ unsigned i;
+ int ret;
+
+ if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE) {
+ for (i = 0; i < count; i++) {
+ ret = reference_resource_glob(wim,
+ resource_wimfiles_or_globs[i],
+ ref_flags,
+ open_flags,
+ progress_func);
+ if (ret)
+ return ret;
+ }
+ return 0;
+ } else {
+ return reference_resource_paths(wim, resource_wimfiles_or_globs,
+ count, ref_flags,
+ open_flags, progress_func);
+ }
+}
#endif
#include "wimlib.h"
+#include "wimlib/assert.h"
#include "wimlib/endianness.h"
#include "wimlib/error.h"
#include "wimlib/file_io.h"
#include "wimlib/lookup_table.h"
#include "wimlib/resource.h"
#include "wimlib/sha1.h"
+#include "wimlib/wim.h"
#ifdef __WIN32__
/* for read_win32_file_prefix(), read_win32_encrypted_file_prefix() */
--- /dev/null
+/*
+ * template.c
+ *
+ * API to reference a template image to optimize later writing of a WIM file.
+ */
+
+/*
+ * Copyright (C) 2013 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * 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.
+ *
+ * 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
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "wimlib.h"
+#include "wimlib/dentry.h"
+#include "wimlib/error.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/metadata.h"
+#include "wimlib/util.h"
+
+/* Returns %true iff the metadata of @inode and @template_inode are reasonably
+ * consistent with them being the same, unmodified file. */
+static bool
+inode_metadata_consistent(const struct wim_inode *inode,
+ const struct wim_inode *template_inode,
+ const struct wim_lookup_table *template_lookup_table)
+{
+ /* Must have exact same creation time and last write time. */
+ if (inode->i_creation_time != template_inode->i_creation_time ||
+ inode->i_last_write_time != template_inode->i_last_write_time)
+ return false;
+
+ /* Last access time may have stayed the same or increased, but certainly
+ * shouldn't have decreased. */
+ if (inode->i_last_access_time < template_inode->i_last_access_time)
+ return false;
+
+ /* Must have same number of alternate data stream entries. */
+ if (inode->i_num_ads != template_inode->i_num_ads)
+ return false;
+
+ /* If the stream entries for the inode are for some reason not resolved,
+ * then the hashes are already available and the point of this function
+ * is defeated. */
+ if (!inode->i_resolved)
+ return false;
+
+ /* Iterate through each stream and do some more checks. */
+ for (unsigned i = 0; i <= inode->i_num_ads; i++) {
+ const struct wim_lookup_table_entry *lte, *template_lte;
+
+ lte = inode_stream_lte_resolved(inode, i);
+ template_lte = inode_stream_lte(template_inode, i,
+ template_lookup_table);
+
+ /* Compare stream sizes. */
+ if (lte && template_lte) {
+ if (lte->size != template_lte->size)
+ return false;
+
+ /* If hash happens to be available, compare with template. */
+ if (!lte->unhashed && !template_lte->unhashed &&
+ !hashes_equal(lte->hash, template_lte->hash))
+ return false;
+
+ } else if (lte && lte->size) {
+ return false;
+ } else if (template_lte && template_lte->size) {
+ return false;
+ }
+ }
+
+ /* All right, barring a full checksum and given that the inodes share a
+ * path and the user isn't trying to trick us, these inodes most likely
+ * refer to the same file. */
+ return true;
+}
+
+/**
+ * Given an inode @inode that has been determined to be "the same" as another
+ * inode @template_inode in either the same WIM or another WIM, retrieve some
+ * useful stream information (e.g. checksums) from @template_inode.
+ *
+ * This assumes that the streams for @inode have been resolved (to point
+ * directly to the appropriate `struct wim_lookup_table_entry's) but do not
+ * necessarily have checksum information filled in.
+ */
+static int
+inode_copy_checksums(struct wim_inode *inode,
+ struct wim_inode *template_inode,
+ WIMStruct *wim,
+ WIMStruct *template_wim)
+{
+ for (unsigned i = 0; i <= inode->i_num_ads; i++) {
+ struct wim_lookup_table_entry *lte, *template_lte;
+ struct wim_lookup_table_entry *replace_lte;
+
+ lte = inode_stream_lte_resolved(inode, i);
+ template_lte = inode_stream_lte(template_inode, i,
+ template_wim->lookup_table);
+
+ /* Only take action if both entries exist, the entry for @inode
+ * has no checksum calculated, but the entry for @template_inode
+ * does. */
+ if (lte == NULL || template_lte == NULL ||
+ !lte->unhashed || template_lte->unhashed)
+ continue;
+
+ wimlib_assert(lte->refcnt == inode->i_nlink);
+
+ /* If the WIM of the template image is the same as the WIM of
+ * the new image, then @template_lte can be used directly.
+ *
+ * Otherwise, look for a stream with the same hash in the WIM of
+ * the new image. If found, use it; otherwise re-use the entry
+ * being discarded, filling in the hash. */
+
+ if (wim == template_wim)
+ replace_lte = template_lte;
+ else
+ replace_lte = lookup_stream(wim->lookup_table,
+ template_lte->hash);
+
+ list_del(<e->unhashed_list);
+ if (replace_lte) {
+ free_lookup_table_entry(lte);
+ } else {
+ copy_hash(lte->hash, template_lte->hash);
+ lte->unhashed = 0;
+ lookup_table_insert(wim->lookup_table, lte);
+ lte->refcnt = 0;
+ replace_lte = lte;
+ }
+
+ if (i == 0)
+ inode->i_lte = replace_lte;
+ else
+ inode->i_ads_entries[i - 1].lte = replace_lte;
+
+ replace_lte->refcnt += inode->i_nlink;
+ }
+ return 0;
+}
+
+struct reference_template_args {
+ WIMStruct *wim;
+ WIMStruct *template_wim;
+};
+
+static int
+dentry_reference_template(struct wim_dentry *dentry, void *_args)
+{
+ int ret;
+ struct wim_dentry *template_dentry;
+ struct wim_inode *inode, *template_inode;
+ struct reference_template_args *args = _args;
+ WIMStruct *wim = args->wim;
+ WIMStruct *template_wim = args->template_wim;
+
+ if (dentry->d_inode->i_visited)
+ return 0;
+
+ ret = calculate_dentry_full_path(dentry);
+ if (ret)
+ return ret;
+
+ template_dentry = get_dentry(template_wim, dentry->_full_path,
+ WIMLIB_CASE_SENSITIVE);
+ if (template_dentry == NULL) {
+ DEBUG("\"%"TS"\": newly added file", dentry->_full_path);
+ return 0;
+ }
+
+ inode = dentry->d_inode;
+ template_inode = template_dentry->d_inode;
+
+ if (inode_metadata_consistent(inode, template_inode,
+ template_wim->lookup_table)) {
+ /*DEBUG("\"%"TS"\": No change detected", dentry->_full_path);*/
+ ret = inode_copy_checksums(inode, template_inode,
+ wim, template_wim);
+ inode->i_visited = 1;
+ } else {
+ DEBUG("\"%"TS"\": change detected!", dentry->_full_path);
+ ret = 0;
+ }
+ return ret;
+}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_reference_template_image(WIMStruct *wim, int new_image,
+ WIMStruct *template_wim, int template_image,
+ int flags, wimlib_progress_func_t progress_func)
+{
+ int ret;
+ struct wim_image_metadata *new_imd;
+
+ if (wim == NULL || template_wim == NULL)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ if (wim == template_wim && new_image == template_image)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ if (new_image < 1 || new_image > wim->hdr.image_count)
+ return WIMLIB_ERR_INVALID_IMAGE;
+
+ if (!wim_has_metadata(wim))
+ return WIMLIB_ERR_METADATA_NOT_FOUND;
+
+ new_imd = wim->image_metadata[new_image - 1];
+ if (!new_imd->modified)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ ret = select_wim_image(template_wim, template_image);
+ if (ret)
+ return ret;
+
+ struct reference_template_args args = {
+ .wim = wim,
+ .template_wim = template_wim,
+ };
+
+ ret = for_dentry_in_tree(new_imd->root_dentry,
+ dentry_reference_template, &args);
+ dentry_tree_clear_inode_visited(new_imd->root_dentry);
+ return ret;
+}
#include "wimlib/apply.h"
#include "wimlib/error.h"
-#include "wimlib/lookup_table.h"
#include "wimlib/resource.h"
#include "wimlib/timestamp.h"
+#include "wimlib/unix_data.h"
#include <errno.h>
#include <fcntl.h>
unix_capture_regular_file(const char *path,
u64 size,
struct wim_inode *inode,
- struct wim_lookup_table *lookup_table)
+ struct list_head *unhashed_streams)
{
inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
lte->file_on_disk = file_on_disk;
lte->resource_location = RESOURCE_IN_FILE_ON_DISK;
lte->size = size;
- lookup_table_insert_unhashed(lookup_table, lte, inode, 0);
+ add_unhashed_stream(lte, inode, 0, unhashed_streams);
inode->i_lte = lte;
}
return 0;
params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT;
if (S_ISREG(stbuf.st_mode))
ret = unix_capture_regular_file(path, stbuf.st_size,
- inode, params->lookup_table);
+ inode, params->unhashed_streams);
else if (S_ISDIR(stbuf.st_mode))
ret = unix_capture_directory(root, path, path_len, params);
else {
goto out_destroy_inode_table;
INIT_LIST_HEAD(&unhashed_streams);
- wim->lookup_table->unhashed_streams = &unhashed_streams;
params.lookup_table = wim->lookup_table;
+ params.unhashed_streams = &unhashed_streams;
params.config = config;
params.add_flags = add_flags;
params.extra_arg = extra_arg;
* the root of each branch from a capture
* source. (This will also set the root dentry
* of the entire image to be unnamed.) */
- ret = set_dentry_name(branch,
+ ret = dentry_set_name(branch,
path_basename(wim_target_path));
if (ret)
goto out_ntfs_umount;
/*
* verify.c
*
- * Verify WIM inodes and stream reference counts.
+ * Verify stream reference counts.
*/
/*
#include "wimlib/metadata.h"
#include "wimlib/security.h"
-/*
- * 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)
-{
- struct wim_dentry *dentry;
-
- /* 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;
- }
-
- /* 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);
- }
-
- /* Files cannot have multiple DOS names, even if they have multiple
- * names in multiple directories (i.e. hard links).
- * Source: NTFS-3g authors. */
- struct wim_dentry *dentry_with_dos_name = NULL;
- inode_for_each_dentry(dentry, inode) {
- if (dentry_has_short_name(dentry)) {
- if (dentry_with_dos_name) {
- /* This was previously an error, but if we
- * capture a WIM from UDF on Windows, hard links
- * are supported but DOS names are automatically
- * generated for all names for an inode. */
- #if 0
- ERROR("Hard-linked file has a DOS name at "
- "both `%"TS"' and `%"TS"'",
- dentry_full_path(dentry_with_dos_name),
- dentry_full_path(dentry));
- return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
- #else
- dentry->dos_name_invalid = 1;
- #endif
- }
- dentry_with_dos_name = dentry;
- }
- }
- return 0;
-}
-
static int
lte_fix_refcnt(struct wim_lookup_table_entry *lte, void *ctr)
{
#include "wimlib/win32_common.h"
#include "wimlib/capture.h"
+#include "wimlib/dentry.h"
#include "wimlib/endianness.h"
#include "wimlib/error.h"
#include "wimlib/lookup_table.h"
*
* @inode: WIM inode to save the stream into.
*
- * @lookup_table: Stream lookup table for the WIM.
+ * @unhashed_streams: List of unhashed streams that have been added to the WIM
+ * image.
*
* @dat: A `WIN32_FIND_STREAM_DATA' structure that specifies the
* stream name.
win32_capture_stream(const wchar_t *path,
size_t path_num_chars,
struct wim_inode *inode,
- struct wim_lookup_table *lookup_table,
+ struct list_head *unhashed_streams,
WIN32_FIND_STREAM_DATA *dat)
{
struct wim_ads_entry *ads_entry;
stream_id = 0;
inode->i_lte = lte;
}
- lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id);
+ add_unhashed_stream(lte, inode, stream_id, unhashed_streams);
ret = 0;
out_free_spath:
FREE(spath);
const wchar_t *path,
size_t path_num_chars,
struct wim_inode *inode,
- struct wim_lookup_table *lookup_table,
+ struct list_head *unhashed_streams,
u64 file_size,
unsigned vol_flags)
{
/* Capture the stream. */
ret = win32_capture_stream(path, path_num_chars, inode,
- lookup_table, &dat);
+ unhashed_streams, &dat);
if (ret)
goto out_free_buf;
}
do {
ret = win32_capture_stream(path,
path_num_chars,
- inode, lookup_table,
+ inode, unhashed_streams,
&dat);
if (ret)
goto out_find_close;
wcscpy(dat.cStreamName, L"::$DATA");
dat.StreamSize.QuadPart = file_size;
return win32_capture_stream(path, path_num_chars,
- inode, lookup_table, &dat);
+ inode, unhashed_streams, &dat);
}
static int
path,
path_num_chars,
inode,
- params->lookup_table,
+ params->unhashed_streams,
file_size,
vol_flags);
if (ret)
#include "wimlib/error.h"
#include "wimlib/file_io.h"
#include "wimlib/header.h"
+#include "wimlib/inode.h"
#include "wimlib/integrity.h"
#include "wimlib/lookup_table.h"
#include "wimlib/metadata.h"
struct stream_size_table tab;
struct wim_lookup_table_entry *lte;
- ret = init_stream_size_table(&tab, lt->capacity);
+ ret = init_stream_size_table(&tab, 9001);
if (ret)
return ret;
setfattr -n user.ads3 anotherfile -v 33;
echo -n > emptyfile;
setfattr -n user.ads emptyfile -v 8;
- setfattr -n user.ads5 emptyfile -v"`cat $srcdir/src/hardlink.c`"
+ setfattr -n user.ads5 emptyfile -v"`cat $srcdir/src/sha1.c`"
mkdir dir/subdir;
ln file dir/subdir/file;
echo -n 8 > dir/subdir/file2;