X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fdentry.c;h=f986f9df12fbfe02624b70bbc66dee964565028c;hb=e9fa56fa6e181ae15282a2fcf0dbfa0b7c59cd81;hp=9411f85dba11f4e9964a10f1e932147e4e85dcbc;hpb=b8df27e5fdd4dae472ababcec11d04eafea8830b;p=wimlib diff --git a/src/dentry.c b/src/dentry.c index 9411f85d..f986f9df 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -70,6 +70,8 @@ struct wim_ads_entry_on_disk { utf16lechar stream_name[]; } _packed_attribute; +#define WIM_ADS_ENTRY_DISK_SIZE 38 + /* WIM directory entry (on-disk format) */ struct wim_dentry_on_disk { le64 length; @@ -105,6 +107,8 @@ 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. */ @@ -1025,6 +1029,7 @@ dentry_add_child(struct wim_dentry * restrict parent, struct wim_dentry * restrict child) { wimlib_assert(dentry_is_directory(parent)); + wimlib_assert(parent != child); struct rb_root *root = &parent->d_inode->i_children; struct rb_node **new = &(root->rb_node); @@ -1179,12 +1184,11 @@ add_stream_from_data_buffer(const void *buffer, size_t size, lte = new_lookup_table_entry(); if (!lte) return NULL; - buffer_copy = MALLOC(size); + buffer_copy = memdup(buffer, size); if (!buffer_copy) { free_lookup_table_entry(lte); return NULL; } - memcpy(buffer_copy, buffer, size); lte->resource_location = RESOURCE_IN_ATTACHED_BUFFER; lte->attached_buffer = buffer_copy; lte->resource_entry.original_size = size; @@ -1368,11 +1372,11 @@ replace_forbidden_characters(utf16lechar *name) * @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. + * @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. + * to by @p. * * * Return 0 on success or nonzero on failure. On success, inode->i_ads_entries @@ -1387,11 +1391,16 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, 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) 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; @@ -1410,7 +1419,7 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, 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, or so large it overflows + * 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) @@ -1419,9 +1428,8 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, /* 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(cur_entry->stream_name_nbytes); + 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 @@ -1430,8 +1438,6 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, * the fact that the real unnamed stream entry has a nonzero * hash field. */ if (cur_entry->stream_name_nbytes) { - u64 length_no_padding; - /* 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... */ @@ -1452,7 +1458,7 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, memcpy(cur_entry->stream_name, disk_entry->stream_name, cur_entry->stream_name_nbytes); - cur_entry->stream_name[cur_entry->stream_name_nbytes / 2] = 0; + cur_entry->stream_name[cur_entry->stream_name_nbytes / 2] = cpu_to_le16(0); replace_forbidden_characters(cur_entry->stream_name); } @@ -1472,9 +1478,7 @@ read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, nbytes_remaining -= length; } inode->i_ads_entries = ads_entries; -#ifdef WITH_FUSE inode->i_next_stream_id = inode->i_num_ads + 1; -#endif ret = 0; goto out; out_of_memory: @@ -1512,6 +1516,10 @@ out: * buffers. On success, the dentry->length field must be examined. If zero, * this was a special "end of directory" dentry and not a real dentry. If * nonzero, this was a real dentry. + * + * Possible errors include: + * WIMLIB_ERR_NOMEM + * WIMLIB_ERR_INVALID_DENTRY */ int read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, @@ -1525,44 +1533,43 @@ read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, u16 file_name_nbytes; int ret; struct wim_inode *inode; - const struct wim_dentry_on_disk *disk_dentry; const u8 *p = &metadata_resource[offset]; + const struct wim_dentry_on_disk *disk_dentry = + (const struct wim_dentry_on_disk*)p; + + BUILD_BUG_ON(sizeof(struct wim_dentry_on_disk) != WIM_DENTRY_DISK_SIZE); if ((uintptr_t)p & 7) WARNING("WIM dentry is not 8-byte aligned"); dentry_common_init(dentry); - /*Make sure the dentry really fits into the metadata resource.*/ - if (offset + sizeof(u64) < offset || - offset + sizeof(u64) > metadata_resource_len) + /* Before reading the whole dentry, we need to read just the length. + * This is because a dentry of length 8 (that is, just the length field) + * terminates the list of sibling directory entries. */ + if (offset + sizeof(u64) > metadata_resource_len || + offset + sizeof(u64) < offset) { ERROR("Directory entry starting at %"PRIu64" ends past the " "end of the metadata resource (size %"PRIu64")", offset, metadata_resource_len); return WIMLIB_ERR_INVALID_DENTRY; } - - disk_dentry = (const struct wim_dentry_on_disk*)p; - - /* Before reading the whole dentry, we need to read just the length. - * This is because a dentry of length 8 (that is, just the length field) - * terminates the list of sibling directory entries. */ dentry->length = le64_to_cpu(disk_dentry->length); /* A zero length field (really a length of 8, since that's how big the * directory entry is...) indicates that this is the end of directory * dentry. We do not read it into memory as an actual dentry, so just - * return successfully in that case. */ + * return successfully in this case. */ if (dentry->length == 8) dentry->length = 0; if (dentry->length == 0) return 0; /* Now that we have the actual length provided in the on-disk structure, - * make sure it doesn't overflow the metadata buffer. */ - if (offset + dentry->length >= metadata_resource_len - || offset + dentry->length < offset) + * again make sure it doesn't overflow the metadata resource buffer. */ + if (offset + dentry->length > metadata_resource_len || + offset + dentry->length < offset) { ERROR("Directory entry at offset %"PRIu64" and with size " "%"PRIu64" ends past the end of the metadata resource " @@ -1706,7 +1713,6 @@ read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, goto out_free_short_name; } } -out_success: /* We've read all the data for this dentry. Set the names and their * lengths, and we've done. */ dentry->d_inode = inode; @@ -1761,12 +1767,12 @@ read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len, return 0; /* Find and read all the children of @dentry. */ - while (1) { + for (;;) { /* Read next child of @dentry into @cur_child. */ ret = read_dentry(metadata_resource, metadata_resource_len, cur_offset, &cur_child); - if (ret != 0) + if (ret) break; /* Check for end of directory. */ @@ -1775,32 +1781,43 @@ read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len, /* Not end of directory. Allocate this child permanently and * link it to the parent and previous child. */ - child = MALLOC(sizeof(struct wim_dentry)); + child = memdup(&cur_child, sizeof(struct wim_dentry)); if (!child) { - ERROR("Failed to allocate %zu bytes for new dentry", - sizeof(struct wim_dentry)); + ERROR("Failed to allocate new dentry!"); ret = WIMLIB_ERR_NOMEM; break; } - memcpy(child, &cur_child, sizeof(struct wim_dentry)); - dentry_add_child(dentry, child); - inode_add_dentry(child, child->d_inode); - - /* If there are children of this child, call this procedure - * recursively. */ - if (child->subdir_offset != 0) { - ret = read_dentry_tree(metadata_resource, - metadata_resource_len, child); - if (ret != 0) - break; - } /* Advance to the offset of the next child. Note: We need to * advance by the TOTAL length of the dentry, not by the length - * child->length, which although it does take into account the - * padding, it DOES NOT take into account alternate stream + * cur_child.length, which although it does take into account + * the padding, it DOES NOT take into account alternate stream * entries. */ cur_offset += dentry_total_length(child); + + if (dentry_add_child(dentry, child)) { + WARNING("Ignoring duplicate dentry \"%"WS"\"", + child->file_name); + WARNING("(In directory \"%"TS"\")", dentry_full_path(dentry)); + free_dentry(child); + } else { + inode_add_dentry(child, child->d_inode); + /* If there are children of this child, call this + * procedure recursively. */ + if (child->subdir_offset != 0) { + if (dentry_is_directory(child)) { + ret = read_dentry_tree(metadata_resource, + metadata_resource_len, + child); + if (ret) + break; + } else { + WARNING("Ignoring children of non-directory \"%"TS"\"", + dentry_full_path(child)); + } + } + + } } return ret; } @@ -1812,7 +1829,7 @@ read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len, * @p: The memory location to write the data to. * * Returns the pointer to the byte after the last byte we wrote as part of the - * dentry, including any alternate data streams entry. + * dentry, including any alternate data stream entries. */ static u8 * write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) @@ -1836,12 +1853,8 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) disk_dentry->creation_time = cpu_to_le64(inode->i_creation_time); disk_dentry->last_access_time = cpu_to_le64(inode->i_last_access_time); disk_dentry->last_write_time = cpu_to_le64(inode->i_last_write_time); - - if (inode->i_resolved) - hash = inode->i_lte->hash; - else - hash = inode->i_hash; - copy_hash(disk_dentry->unnamed_stream_hash, inode_stream_hash(inode, 0)); + hash = inode_stream_hash(inode, 0); + copy_hash(disk_dentry->unnamed_stream_hash, hash); if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { disk_dentry->reparse.rp_unknown_1 = cpu_to_le32(inode->i_rp_unknown_1); disk_dentry->reparse.reparse_tag = cpu_to_le32(inode->i_reparse_tag); @@ -1870,19 +1883,19 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) /* We calculate the correct length of the dentry ourselves because the * dentry->length field may been set to an unexpected value from when we * read the dentry in (for example, there may have been unknown data - * appended to the end of the dentry...) */ + * appended to the end of the dentry...). Furthermore, the dentry may + * have been renamed, thus changing its needed length. */ disk_dentry->length = cpu_to_le64(p - orig_p); - /* Write the alternate data streams entries, if there are any. */ - for (u16 i = 0; i < inode->i_num_ads; i++) - { + /* Write the alternate data streams entries, if any. */ + for (u16 i = 0; i < inode->i_num_ads; i++) { const struct wim_ads_entry *ads_entry = - &inode->i_ads_entries[i]; + &inode->i_ads_entries[i]; struct wim_ads_entry_on_disk *disk_ads_entry = (struct wim_ads_entry_on_disk*)p; + orig_p = p; disk_ads_entry->reserved = cpu_to_le64(ads_entry->reserved); - orig_p = p; hash = inode_stream_hash(inode, i + 1); copy_hash(disk_ads_entry->hash, hash);