return __dentry_total_length(dentry, dentry->length);
}
-/* Transfers file attributes from a `stat' buffer to a struct dentry. */
-void stbuf_to_dentry(const struct stat *stbuf, struct dentry *dentry)
+/* Transfers file attributes from a `stat' buffer to an inode. */
+void stbuf_to_inode(const struct stat *stbuf, struct inode *inode)
{
- struct inode *inode = dentry->inode;
if (S_ISLNK(stbuf->st_mode)) {
inode->attributes = FILE_ATTRIBUTE_REPARSE_POINT;
inode->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
inode->attributes = FILE_ATTRIBUTE_NORMAL;
}
if (sizeof(ino_t) >= 8)
- dentry->link_group_id = (u64)stbuf->st_ino;
+ inode->ino = (u64)stbuf->st_ino;
else
- dentry->link_group_id = (u64)stbuf->st_ino |
+ inode->ino = (u64)stbuf->st_ino |
((u64)stbuf->st_dev << (sizeof(ino_t) * 8));
/* Set timestamps */
inode->creation_time = timespec_to_wim_timestamp(&stbuf->st_mtim);
file_attr_flags[i].name);
printf("Security ID = %d\n", inode->security_id);
printf("Subdir offset = %"PRIu64"\n", dentry->subdir_offset);
-#if 0
- printf("Unused1 = 0x%"PRIu64"\n", dentry->unused1);
- printf("Unused2 = %"PRIu64"\n", dentry->unused2);
-#endif
-#if 0
- printf("Creation Time = 0x%"PRIx64"\n");
- printf("Last Access Time = 0x%"PRIx64"\n");
- printf("Last Write Time = 0x%"PRIx64"\n");
-#endif
/* Translate the timestamps into something readable */
time = wim_timestamp_to_unix(inode->creation_time);
printf("Last Write Time = %s UTC\n", p);
printf("Reparse Tag = 0x%"PRIx32"\n", inode->reparse_tag);
- printf("Hard Link Group = 0x%"PRIx64"\n", dentry->link_group_id);
+ printf("Hard Link Group = 0x%"PRIx64"\n", inode->ino);
+ printf("Hard Link Group Size = %"PRIu32"\n", inode->link_count);
printf("Number of Alternate Data Streams = %hu\n", inode->num_ads);
printf("Filename = \"");
print_string(dentry->file_name, dentry->file_name_len);
dentry->refcnt = 1;
}
-struct inode *new_inode()
+struct inode *new_timeless_inode()
{
struct inode *inode = CALLOC(1, sizeof(struct inode));
- u64 now = get_wim_timestamp();
if (!inode)
return NULL;
inode->security_id = -1;
inode->link_count = 1;
+ INIT_LIST_HEAD(&inode->dentry_list);
+ return inode;
+}
+
+struct inode *new_inode()
+{
+ struct inode *inode = new_timeless_inode();
+ if (!inode)
+ return NULL;
+ u64 now = get_wim_timestamp();
inode->creation_time = now;
inode->last_access_time = now;
inode->last_write_time = now;
- INIT_LIST_HEAD(&inode->dentry_list);
return inode;
}
void free_inode(struct inode *inode)
{
- wimlib_assert(inode);
- inode_free_ads_entries(inode);
- FREE(inode);
+ if (inode) {
+ inode_free_ads_entries(inode);
+ FREE(inode);
+ }
}
void put_inode(struct inode *inode)
void free_dentry(struct dentry *dentry)
{
wimlib_assert(dentry);
+
FREE(dentry->file_name);
FREE(dentry->file_name_utf8);
FREE(dentry->short_name);
return WIMLIB_ERR_INVALID_DENTRY;
}
- inode = new_inode();
+ inode = new_timeless_inode();
if (!inode)
return WIMLIB_ERR_NOMEM;
p += 4;
} else {
p = get_u32(p, &inode->reparse_tag);
- p = get_u64(p, &dentry->link_group_id);
+ p = get_u64(p, &inode->ino);
}
/* By the way, the reparse_reserved field does not actually exist (at
/* We've read all the data for this dentry. Set the names and their
* lengths, and we've done. */
+ dentry->inode = inode;
dentry->file_name = file_name;
dentry->file_name_utf8 = file_name_utf8;
dentry->short_name = short_name;
} else {
u64 link_group_id;
p = put_u32(p, 0);
- if (dentry->inode->link_count == 1)
+ if (inode->link_count == 1)
link_group_id = 0;
else
- link_group_id = dentry->inode->ino;
+ link_group_id = inode->ino;
p = put_u64(p, link_group_id);
}
p = put_u16(p, inode->num_ads);
child->parent = dentry;
prev_child = child;
+ list_add(&child->inode_dentry_list, &child->inode->dentry_list);
/* If there are children of this child, call this procedure
* recursively. */
int ret;
struct inode *inode = dentry->inode;
- if (!(extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE)) {
- /* This dentry is one of a hard link set of at least 2 dentries.
- * If one of the other dentries has already been extracted, make
- * a hard link to the file corresponding to this
- * already-extracted directory. Otherwise, extract the
- * file, and set the dentry->extracted_file field so that other
+ if (!((extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE)
+ && (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
+ WIMLIB_EXTRACT_FLAG_HARDLINK))))
+ {
+ /* If the dentry is one of a hard link set of at least 2
+ * dentries and one of the other dentries has already been
+ * extracted, make a hard link to the file corresponding to this
+ * already-extracted directory. Otherwise, extract the file,
+ * and set the inode->extracted_file field so that other
* dentries in the hard link group can link to it. */
- struct dentry *other;
- if (inode->extracted_file) {
- DEBUG("Extracting hard link `%s' => `%s'",
- output_path, inode->extracted_file);
- if (link(inode->extracted_file, output_path) != 0) {
- ERROR_WITH_ERRNO("Failed to hard link "
- "`%s' to `%s'",
- output_path,
- inode->extracted_file);
- return WIMLIB_ERR_LINK;
- }
- return 0;
- }
if (inode->link_count > 1) {
+ if (inode->extracted_file) {
+ DEBUG("Extracting hard link `%s' => `%s'",
+ output_path, inode->extracted_file);
+ if (link(inode->extracted_file, output_path) != 0) {
+ ERROR_WITH_ERRNO("Failed to hard link "
+ "`%s' to `%s'",
+ output_path,
+ inode->extracted_file);
+ return WIMLIB_ERR_LINK;
+ }
+ return 0;
+ }
FREE(inode->extracted_file);
inode->extracted_file = STRDUP(output_path);
if (!inode->extracted_file) {
/* Empty file with no lookup table entry */
DEBUG("Empty file `%s'.", output_path);
ret = 0;
- goto done;
+ goto out;
}
ret = extract_full_wim_resource_to_fd(lte, out_fd);
if (ret != 0) {
ERROR("Failed to extract resource to `%s'", output_path);
- goto done;
+ goto out;
}
-done:
+out:
if (close(out_fd) != 0) {
ERROR_WITH_ERRNO("Failed to close file `%s'", output_path);
ret = WIMLIB_ERR_WRITE;
* -----------------
*/
-/* Hash table to find inodes, identified by their inode ID.
- * */
-struct 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
- * before calling assign_inode_numbers().
- *
- * - Groups we create ourselves by splitting a nominal inode due to
- * inconsistencies in the dentries. These inodes will share a inode
- * ID with some other inode until assign_inode_numbers() is called.
- */
- struct hlist_head extra_inodes;
-};
-
-/* Returns pointer to a new inode table having the specified capacity */
-struct inode_table *new_inode_table(size_t capacity)
+
+int init_inode_table(struct inode_table *table, size_t capacity)
{
- struct inode_table *table;
- struct hlist_head *array;
-
- table = MALLOC(sizeof(struct inode_table));
- if (!table)
- goto err;
- array = CALLOC(capacity, sizeof(array[0]));
- if (!array) {
- FREE(table);
- goto err;
+ 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;
- table->array = array;
INIT_HLIST_HEAD(&table->extra_inodes);
- return table;
-err:
- ERROR("Failed to allocate memory for inode table with capacity %zu",
- capacity);
- return NULL;
+ return 0;
+}
+
+
+static size_t inode_link_count(const struct inode *inode)
+{
+ const struct list_head *cur;
+ size_t size = 0;
+ list_for_each(cur, &inode->dentry_list)
+ size++;
+ return size;
+}
+
+static struct dentry *inode_first_dentry(struct inode *inode)
+{
+ return container_of(inode->dentry_list.next, struct dentry,
+ inode_dentry_list);
}
/*
int inode_table_insert(struct dentry *dentry, void *__table)
{
struct inode_table *table = __table;
- size_t pos;
- struct inode *inode;
struct inode *d_inode = dentry->inode;
if (d_inode->ino == 0) {
-
/* Single inode--- Add to the list of extra inodes (we can't put
* it in the table itself because all the singles have a link
* inode ID of 0) */
- list_add(&dentry->inode_dentry_list, &d_inode->dentry_list);
hlist_add_head(&d_inode->hlist, &table->extra_inodes);
+
+ wimlib_assert(d_inode->dentry_list.next == &dentry->inode_dentry_list);
+ wimlib_assert(d_inode->dentry_list.prev == &dentry->inode_dentry_list);
+ wimlib_assert(d_inode->link_count == 1);
} else {
- /* Hard inode that may contain multiple dentries (the code
- * will work even if the inode actually contains only 1 dentry
- * though) */
+ /* Inode that may have multiple corresponding dentries (the code
+ * will work even if the inode actually contains only 1 dentry
+ * though) */
+
+ size_t pos;
+ struct inode *inode;
struct hlist_node *cur;
/* Try adding to existing inode */
if (inode->ino == d_inode->ino) {
list_add(&dentry->inode_dentry_list,
&inode->dentry_list);
+ inode->link_count++;
+ return 0;
}
}
/* Add new inode to the table */
- list_add(&dentry->inode_dentry_list, &d_inode->dentry_list);
hlist_add_head(&d_inode->hlist, &table->array[pos]);
+ wimlib_assert(d_inode->dentry_list.next == &dentry->inode_dentry_list);
+ wimlib_assert(d_inode->dentry_list.prev == &dentry->inode_dentry_list);
+ wimlib_assert(d_inode->link_count == 1);
+
/* XXX Make the table grow when too many entries have been
* inserted. */
table->num_entries++;
return 0;
}
-/* Frees a inode table. */
-void free_inode_table(struct inode_table *table)
-{
- if (table) {
- FREE(table->array);
- FREE(table);
- }
-}
-
-static u64
-assign_inos_to_list(struct hlist_head *head, u64 cur_ino)
-{
- struct inode *inode;
- struct hlist_node *cur;
- struct dentry *dentry;
- hlist_for_each_entry(inode, cur, head, hlist) {
- }
- return cur_ino;
-}
-
/* Assign the inode numbers to dentries in a inode table, and return the
* next available inode ID. */
u64 assign_inode_numbers(struct hlist_head *inode_list)
u64 cur_ino = 1;
struct dentry *dentry;
hlist_for_each_entry(inode, cur, inode_list, hlist) {
- list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list)
- dentry->link_group_id = cur_ino;
inode->ino = cur_ino;
cur_ino++;
}
print_inode_dentries(inode);
}
-static bool ref_dentries_consistent(const struct dentry * restrict ref_dentry_1,
- const struct dentry * restrict ref_dentry_2)
+static bool ref_inodes_consistent(const struct inode * restrict ref_inode_1,
+ const struct inode * restrict ref_inode_2)
{
- wimlib_assert(ref_dentry_1 != ref_dentry_2);
+ wimlib_assert(ref_inode_1 != ref_inode_2);
- if (ref_dentry_1->inode->num_ads != ref_dentry_2->inode->num_ads)
+ if (ref_inode_1->num_ads != ref_inode_2->num_ads)
return false;
- if (ref_dentry_1->inode->security_id != ref_dentry_2->inode->security_id
- || ref_dentry_1->inode->attributes != ref_dentry_2->inode->attributes)
+ if (ref_inode_1->security_id != ref_inode_2->security_id
+ || ref_inode_1->attributes != ref_inode_2->attributes)
return false;
- for (unsigned i = 0; i <= ref_dentry_1->inode->num_ads; i++) {
+ for (unsigned i = 0; i <= ref_inode_1->num_ads; i++) {
const u8 *ref_1_hash, *ref_2_hash;
- ref_1_hash = inode_stream_hash(ref_dentry_1->inode, i);
- ref_2_hash = inode_stream_hash(ref_dentry_2->inode, i);
+ ref_1_hash = inode_stream_hash(ref_inode_1, i);
+ ref_2_hash = inode_stream_hash(ref_inode_2, i);
if (!hashes_equal(ref_1_hash, ref_2_hash))
return false;
- if (i && !ads_entries_have_same_name(ref_dentry_1->inode->ads_entries[i - 1],
- ref_dentry_2->inode->ads_entries[i - 1]))
+ if (i && !ads_entries_have_same_name(ref_inode_1->ads_entries[i - 1],
+ ref_inode_2->ads_entries[i - 1]))
return false;
}
return true;
}
-static bool dentries_consistent(const struct dentry * restrict ref_dentry,
- const struct dentry * restrict dentry)
+static bool inodes_consistent(const struct inode * restrict ref_inode,
+ const struct inode * restrict inode)
{
- wimlib_assert(ref_dentry != dentry);
+ wimlib_assert(ref_inode != inode);
- if (ref_dentry->inode->num_ads != dentry->inode->num_ads &&
- dentry->inode->num_ads != 0)
+ if (ref_inode->num_ads != inode->num_ads &&
+ inode->num_ads != 0)
return false;
- if (ref_dentry->inode->security_id != dentry->inode->security_id
- || ref_dentry->inode->attributes != dentry->inode->attributes)
+ if (ref_inode->security_id != inode->security_id
+ || ref_inode->attributes != inode->attributes)
return false;
- for (unsigned i = 0; i <= min(ref_dentry->inode->num_ads, dentry->inode->num_ads); i++) {
+ for (unsigned i = 0; i <= min(ref_inode->num_ads, inode->num_ads); i++) {
const u8 *ref_hash, *hash;
- ref_hash = inode_stream_hash(ref_dentry->inode, i);
- hash = inode_stream_hash(dentry->inode, i);
+ ref_hash = inode_stream_hash(ref_inode, i);
+ hash = inode_stream_hash(inode, i);
if (!hashes_equal(ref_hash, hash) && !is_zero_hash(hash))
return false;
- if (i && !ads_entries_have_same_name(ref_dentry->inode->ads_entries[i - 1],
- dentry->inode->ads_entries[i - 1]))
+ if (i && !ads_entries_have_same_name(ref_inode->ads_entries[i - 1],
+ inode->ads_entries[i - 1]))
return false;
}
return true;
#endif
-static size_t inode_link_count(const struct inode *inode)
-{
- const struct list_head *cur;
- size_t size = 0;
- list_for_each(cur, &inode->dentry_list)
- size++;
- return size;
-}
-
-static struct dentry *inode_first_dentry(struct inode *inode)
-{
- return container_of(inode->dentry_list.next, struct dentry,
- inode_dentry_list);
-}
/* Fix up a "true" inode and check for inconsistencies */
static int fix_true_inode(struct inode *inode)
{
struct dentry *dentry;
struct dentry *ref_dentry = NULL;
+ struct inode *ref_inode;
u64 last_ctime = 0;
u64 last_mtime = 0;
u64 last_atime = 0;
last_atime = dentry->inode->last_access_time;
}
+ ref_inode = ref_dentry->inode;
+ ref_inode->link_count = 1;
+
list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) {
if (dentry != ref_dentry) {
- if (!dentries_consistent(ref_dentry, dentry)) {
- inconsistent_inode(inode);
+ if (!inodes_consistent(ref_inode, dentry->inode)) {
+ inconsistent_inode(dentry->inode);
return WIMLIB_ERR_INVALID_DENTRY;
}
/* Free the unneeded `struct inode'. */
free_inode(dentry->inode);
- dentry->inode = ref_dentry->inode;
- ref_dentry->inode->link_count++;
+ dentry->inode = ref_inode;
+ ref_inode->link_count++;
}
}
- ref_dentry->inode->creation_time = last_ctime;
- ref_dentry->inode->last_write_time = last_mtime;
- ref_dentry->inode->last_access_time = last_atime;
- wimlib_assert(inode_link_count(inode) == inode->link_count);
+ ref_inode->creation_time = last_ctime;
+ ref_inode->last_write_time = last_mtime;
+ ref_inode->last_access_time = last_atime;
+ wimlib_assert(inode_link_count(ref_inode) == ref_inode->link_count);
return 0;
}
static int
fix_nominal_inode(struct inode *inode, struct hlist_head *inode_list)
{
- struct dentry *tmp, *dentry, *ref_dentry;
- struct hlist_node *cur;
+ struct dentry *dentry, *ref_dentry;
+ struct hlist_node *cur, *tmp;
int ret;
size_t num_true_inodes;
+ wimlib_assert(inode->link_count == inode_link_count(inode));
+
LIST_HEAD(dentries_with_data_streams);
LIST_HEAD(dentries_with_no_data_streams);
HLIST_HEAD(true_inodes);
/* If there are no dentries with data streams, we require the nominal
* inode to be a true inode */
if (list_empty(&dentries_with_data_streams)) {
+ DEBUG("No data streams");
#ifdef ENABLE_DEBUG
{
if (inode->link_count > 1) {
* of the true inodes are consistent with this
* dentry, make a new one. */
hlist_for_each_entry(inode, cur, &true_inodes, hlist) {
- if (ref_dentries_consistent(inode_first_dentry(inode), dentry)) {
+ if (ref_inodes_consistent(inode, dentry->inode)) {
list_add(&dentry->inode_dentry_list,
&inode->dentry_list);
goto next_dentry_2;
}
}
num_true_inodes++;
- hlist_add_head(&dentry->inode->hlist, &true_inodes);
INIT_LIST_HEAD(&dentry->inode->dentry_list);
list_add(&dentry->inode_dentry_list, &dentry->inode->dentry_list);
+ hlist_add_head(&dentry->inode->hlist, &true_inodes);
next_dentry_2:
;
}
ERROR("inode to assign them to.");
return WIMLIB_ERR_INVALID_DENTRY;
}
+ inode = container_of(true_inodes.first,
+ struct inode,
+ hlist);
/* Assign the streamless dentries to the one and only true link
* inode. */
- ref_dentry = inode_first_dentry(inode);
list_for_each_entry(dentry, &dentries_with_no_data_streams, tmp_list)
list_add(&dentry->inode_dentry_list, &inode->dentry_list);
}
#endif
}
- hlist_for_each_entry(inode, cur, &true_inodes, hlist) {
+ hlist_for_each_entry_safe(inode, cur, tmp, &true_inodes, hlist) {
hlist_add_head(&inode->hlist, inode_list);
ret = fix_true_inode(inode);
if (ret != 0)
{
struct inode *inode;
struct hlist_node *cur, *tmp;
- int ret = 0;
+ int ret;
INIT_HLIST_HEAD(inode_list);
for (u64 i = 0; i < table->capacity; i++) {
hlist_for_each_entry_safe(inode, cur, tmp, &table->array[i], hlist) {
ret = fix_nominal_inode(inode, inode_list);
if (ret != 0)
- break;
+ return ret;
}
}
- return ret;
+ hlist_for_each_safe(cur, tmp, &table->extra_inodes)
+ hlist_add_head(cur, inode_list);
+ return 0;
}