X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fdentry.c;h=49c0cdf14784337e49b289b298eb4bff198ee251;hp=6a7593887caeb2c042d8ae1ca93e5b3f5ae3eefa;hb=ee547cc83f231d727e4d9984c23e86d96d3da769;hpb=be5acf31aa8292dcd4a2829492faefb0b200d28f diff --git a/src/dentry.c b/src/dentry.c index 6a759388..49c0cdf1 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -129,41 +129,33 @@ struct wim_dentry_on_disk { */ u8 default_hash[SHA1_HASH_SIZE]; - /* The format of the following data is not yet completely known and they - * do not correspond to Microsoft's documentation. + /* Unknown field (maybe accidental padding) */ + le32 unknown_0x54; + + /* + * The following 8-byte union contains either information about the + * reparse point (for files with FILE_ATTRIBUTE_REPARSE_POINT set), or + * the "hard link group ID" (for other files). * - * If this directory entry is for a reparse point (has - * FILE_ATTRIBUTE_REPARSE_POINT set in the 'attributes' field), then the - * version of the following fields containing the reparse tag is valid. - * Furthermore, the field notated as not_rpfixed, as far as I can tell, - * is supposed to be set to 1 if reparse point fixups (a.k.a. fixing the - * targets of absolute symbolic links) were *not* done, and otherwise 0. + * The reparse point information contains ReparseTag and ReparseReserved + * from the header of the reparse point buffer. It also contains a flag + * that indicates whether a reparse point fixup (for the target of an + * absolute symbolic link or junction) was done or not. * - * If this directory entry is not for a reparse point, then the version - * of the following fields containing the hard_link_group_id is valid. - * All MS says about this field is that "If this file is part of a hard - * link set, all the directory entries in the set will share the same - * value in this field.". However, more specifically I have observed - * the following: - * - If the file is part of a hard link set of size 1, then the - * hard_link_group_id should be set to either 0, which is treated - * specially as indicating "not hardlinked", or any unique value. - * - The specific nonzero values used to identity hard link sets do - * not matter, as long as they are unique. - * - However, due to bugs in Microsoft's software, it is actually NOT - * guaranteed that directory entries that share the same hard link - * group ID are actually hard linked to each either. See - * inode_fixup.c for the code that handles this. + * The "hard link group ID" is like an inode number; all dentries for + * the same inode share the same value. See inode_fixup.c for more + * information. + * + * Note that this union creates the limitation that reparse point files + * cannot have multiple names (hard links). */ union { struct { - le32 rp_unknown_1; le32 reparse_tag; - le16 rp_unknown_2; - le16 not_rpfixed; + le16 rp_reserved; + le16 rp_flags; } _packed_attribute reparse; struct { - le32 rp_unknown_1; le64 hard_link_group_id; } _packed_attribute nonreparse; }; @@ -183,14 +175,14 @@ struct wim_dentry_on_disk { * encoded "long" name, excluding the null terminator. If zero, then * this file has no long name. The root dentry should not have a long * name, but all other dentries in the image should have long names. */ - le16 file_name_nbytes; + le16 name_nbytes; /* Beginning of optional, variable-length fields */ - /* If file_name_nbytes != 0, the next field will be the UTF-16LE encoded - * long file name. This will be null-terminated, so the size of this - * field will really be file_name_nbytes + 2. */ - /*utf16lechar file_name[];*/ + /* If name_nbytes != 0, the next field will be the UTF-16LE encoded long + * name. This will be null-terminated, so the size of this field will + * really be name_nbytes + 2. */ + /*utf16lechar name[];*/ /* If short_name_nbytes != 0, the next field will be the UTF-16LE * encoded short name. This will be null-terminated, so the size of @@ -236,17 +228,17 @@ struct wim_extra_stream_entry_on_disk { } _packed_attribute; static void -do_dentry_set_name(struct wim_dentry *dentry, utf16lechar *file_name, - size_t file_name_nbytes) +do_dentry_set_name(struct wim_dentry *dentry, utf16lechar *name, + size_t name_nbytes) { - FREE(dentry->file_name); - dentry->file_name = file_name; - dentry->file_name_nbytes = file_name_nbytes; + FREE(dentry->d_name); + dentry->d_name = name; + dentry->d_name_nbytes = name_nbytes; if (dentry_has_short_name(dentry)) { - FREE(dentry->short_name); - dentry->short_name = NULL; - dentry->short_name_nbytes = 0; + FREE(dentry->d_short_name); + dentry->d_short_name = NULL; + dentry->d_short_name_nbytes = 0; } } @@ -323,11 +315,11 @@ dentry_set_name(struct wim_dentry *dentry, const tchar *name) * tagged metadata items as well as any extra stream entries that may need to * follow the dentry. */ static size_t -dentry_min_len_with_names(u16 file_name_nbytes, u16 short_name_nbytes) +dentry_min_len_with_names(u16 name_nbytes, u16 short_name_nbytes) { size_t length = sizeof(struct wim_dentry_on_disk); - if (file_name_nbytes) - length += (u32)file_name_nbytes + 2; + if (name_nbytes) + length += (u32)name_nbytes + 2; if (short_name_nbytes) length += (u32)short_name_nbytes + 2; return length; @@ -347,7 +339,7 @@ stream_out_total_length(const struct wim_inode_stream *strm) len += utf16le_len_bytes(strm->stream_name) + 2; /* Account for any necessary padding to the next 8-byte boundary. */ - return (len + 7) & ~7; + return ALIGN(len, 8); } /* @@ -362,14 +354,11 @@ dentry_out_total_length(const struct wim_dentry *dentry) const struct wim_inode *inode = dentry->d_inode; size_t len; - len = dentry_min_len_with_names(dentry->file_name_nbytes, - dentry->short_name_nbytes); - len = (len + 7) & ~7; + len = dentry_min_len_with_names(dentry->d_name_nbytes, + dentry->d_short_name_nbytes); + len = ALIGN(len, 8); - if (inode->i_extra_size) { - len += inode->i_extra_size; - len = (len + 7) & ~7; - } + len += ALIGN(inode->i_extra_size, 8); if (!(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) { /* @@ -396,8 +385,8 @@ dentry_out_total_length(const struct wim_dentry *dentry) if (have_named_data_stream || have_reparse_point_stream) { if (have_reparse_point_stream) - len += (sizeof(struct wim_extra_stream_entry_on_disk) + 7) & ~7; - len += (sizeof(struct wim_extra_stream_entry_on_disk) + 7) & ~7; + len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8); + len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8); } } @@ -483,10 +472,10 @@ for_dentry_in_tree_depth(struct wim_dentry *root, /* * Calculate the full path to @dentry within the WIM image, if not already done. * - * The full name will be saved in the cached value 'dentry->_full_path'. + * The full name will be saved in the cached value 'dentry->d_full_path'. * * Whenever possible, use dentry_full_path() instead of calling this and - * accessing _full_path directly. + * accessing d_full_path directly. * * Returns 0 or an error code resulting from a failed string conversion. */ @@ -497,13 +486,13 @@ calculate_dentry_full_path(struct wim_dentry *dentry) size_t dummy; const struct wim_dentry *d; - if (dentry->_full_path) + if (dentry->d_full_path) return 0; ulen = 0; d = dentry; do { - ulen += d->file_name_nbytes / sizeof(utf16lechar); + ulen += d->d_name_nbytes / sizeof(utf16lechar); ulen++; d = d->d_parent; /* assumes d == d->d_parent for root */ } while (!dentry_is_root(d)); @@ -513,8 +502,8 @@ calculate_dentry_full_path(struct wim_dentry *dentry) d = dentry; do { - p -= d->file_name_nbytes / sizeof(utf16lechar); - memcpy(p, d->file_name, d->file_name_nbytes); + p -= d->d_name_nbytes / sizeof(utf16lechar); + memcpy(p, d->d_name, d->d_name_nbytes); *--p = cpu_to_le16(WIM_PATH_SEPARATOR); d = d->d_parent; /* assumes d == d->d_parent for root */ } while (!dentry_is_root(d)); @@ -522,7 +511,7 @@ calculate_dentry_full_path(struct wim_dentry *dentry) wimlib_assert(p == ubuf); return utf16le_to_tstr(ubuf, ulen * sizeof(utf16lechar), - &dentry->_full_path, &dummy); + &dentry->d_full_path, &dummy); } /* @@ -536,7 +525,7 @@ tchar * dentry_full_path(struct wim_dentry *dentry) { calculate_dentry_full_path(dentry); - return dentry->_full_path; + return dentry->d_full_path; } static int @@ -547,7 +536,7 @@ dentry_calculate_subdir_offset(struct wim_dentry *dentry, void *_subdir_offset_p struct wim_dentry *child; /* Set offset of directory's child dentries */ - dentry->subdir_offset = *subdir_offset_p; + dentry->d_subdir_offset = *subdir_offset_p; /* Account for child dentries */ for_dentry_child(child, dentry) @@ -556,8 +545,8 @@ dentry_calculate_subdir_offset(struct wim_dentry *dentry, void *_subdir_offset_p /* Account for end-of-directory entry */ *subdir_offset_p += 8; } else { - /* Not a directory; set subdir_offset to 0 */ - dentry->subdir_offset = 0; + /* Not a directory; set the subdir offset to 0 */ + dentry->d_subdir_offset = 0; } return 0; } @@ -587,10 +576,10 @@ static int dentry_compare_names_case_insensitive(const struct wim_dentry *d1, const struct wim_dentry *d2) { - return cmp_utf16le_strings(d1->file_name, - d1->file_name_nbytes / 2, - d2->file_name, - d2->file_name_nbytes / 2, + return cmp_utf16le_strings(d1->d_name, + d1->d_name_nbytes / 2, + d2->d_name, + d2->d_name_nbytes / 2, true); } @@ -599,10 +588,10 @@ static int dentry_compare_names_case_sensitive(const struct wim_dentry *d1, const struct wim_dentry *d2) { - return cmp_utf16le_strings(d1->file_name, - d1->file_name_nbytes / 2, - d2->file_name, - d2->file_name_nbytes / 2, + return cmp_utf16le_strings(d1->d_name, + d1->d_name_nbytes / 2, + d2->d_name, + d2->d_name_nbytes / 2, false); } @@ -640,8 +629,8 @@ bool default_ignore_case = #endif ; -/* Case-sensitive dentry lookup. Only @file_name and @file_name_nbytes of - * @dummy must be valid. */ +/* Case-sensitive dentry lookup. Only @d_name and @d_name_nbytes of @dummy must + * be valid. */ static struct wim_dentry * dir_lookup(const struct wim_inode *dir, const struct wim_dentry *dummy) { @@ -655,8 +644,8 @@ dir_lookup(const struct wim_inode *dir, const struct wim_dentry *dummy) return avl_tree_entry(node, struct wim_dentry, d_index_node); } -/* Case-insensitive dentry lookup. Only @file_name and @file_name_nbytes of - * @dummy must be valid. */ +/* Case-insensitive dentry lookup. Only @d_name and @d_name_nbytes of @dummy + * must be valid. */ static struct wim_dentry * dir_lookup_ci(const struct wim_inode *dir, const struct wim_dentry *dummy) { @@ -684,8 +673,8 @@ get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry, struct wim_dentry dummy; struct wim_dentry *child; - dummy.file_name = (utf16lechar*)name; - dummy.file_name_nbytes = name_nbytes; + dummy.d_name = (utf16lechar*)name; + dummy.d_name_nbytes = name_nbytes; if (!ignore_case) /* Case-sensitive lookup. */ @@ -1017,9 +1006,9 @@ free_dentry(struct wim_dentry *dentry) { if (dentry) { d_disassociate(dentry); - FREE(dentry->file_name); - FREE(dentry->short_name); - FREE(dentry->_full_path); + FREE(dentry->d_name); + FREE(dentry->d_short_name); + FREE(dentry->d_full_path); FREE(dentry); } } @@ -1331,10 +1320,7 @@ setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode, disk_strm = (const struct wim_extra_stream_entry_on_disk *)p; /* Read the length field */ - length = le64_to_cpu(disk_strm->length); - - /* 8-byte align the length */ - length = (length + 7) & ~7; + length = ALIGN(le64_to_cpu(disk_strm->length), 8); /* Make sure the length field is neither so small it doesn't * include all the fixed-length data nor so large it overflows @@ -1404,7 +1390,7 @@ read_dentry(const u8 * restrict buf, size_t buf_len, struct wim_dentry *dentry; struct wim_inode *inode; u16 short_name_nbytes; - u16 file_name_nbytes; + u16 name_nbytes; u64 calculated_size; int ret; @@ -1424,7 +1410,7 @@ read_dentry(const u8 * restrict buf, size_t buf_len, disk_dentry = (const struct wim_dentry_on_disk*)p; /* Get dentry length. */ - length = (le64_to_cpu(disk_dentry->length) + 7) & ~7; + length = ALIGN(le64_to_cpu(disk_dentry->length), 8); /* Check for end-of-directory. */ if (length <= 8) { @@ -1451,24 +1437,19 @@ read_dentry(const u8 * restrict buf, size_t buf_len, /* Read more fields: some into the dentry, and some into the inode. */ inode->i_attributes = le32_to_cpu(disk_dentry->attributes); inode->i_security_id = le32_to_cpu(disk_dentry->security_id); - dentry->subdir_offset = le64_to_cpu(disk_dentry->subdir_offset); + dentry->d_subdir_offset = le64_to_cpu(disk_dentry->subdir_offset); inode->i_creation_time = le64_to_cpu(disk_dentry->creation_time); inode->i_last_access_time = le64_to_cpu(disk_dentry->last_access_time); inode->i_last_write_time = le64_to_cpu(disk_dentry->last_write_time); + inode->i_unknown_0x54 = le32_to_cpu(disk_dentry->unknown_0x54); - /* I don't know what's going on here. It seems like M$ screwed up the - * reparse points, then put the fields in the same place and didn't - * document it. So we have some fields we read for reparse points, and - * some fields in the same place for non-reparse-points. */ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - inode->i_rp_unknown_1 = le32_to_cpu(disk_dentry->reparse.rp_unknown_1); inode->i_reparse_tag = le32_to_cpu(disk_dentry->reparse.reparse_tag); - inode->i_rp_unknown_2 = le16_to_cpu(disk_dentry->reparse.rp_unknown_2); - inode->i_not_rpfixed = le16_to_cpu(disk_dentry->reparse.not_rpfixed); + inode->i_rp_reserved = le16_to_cpu(disk_dentry->reparse.rp_reserved); + inode->i_rp_flags = le16_to_cpu(disk_dentry->reparse.rp_flags); /* Leave inode->i_ino at 0. Note: this means that WIM cannot * represent multiple hard links to a reparse point file. */ } else { - inode->i_rp_unknown_1 = le32_to_cpu(disk_dentry->nonreparse.rp_unknown_1); inode->i_ino = le64_to_cpu(disk_dentry->nonreparse.hard_link_group_id); } @@ -1476,16 +1457,16 @@ read_dentry(const u8 * restrict buf, size_t buf_len, * name, and the short name. */ short_name_nbytes = le16_to_cpu(disk_dentry->short_name_nbytes); - file_name_nbytes = le16_to_cpu(disk_dentry->file_name_nbytes); + name_nbytes = le16_to_cpu(disk_dentry->name_nbytes); - if (unlikely((short_name_nbytes & 1) | (file_name_nbytes & 1))) { + if (unlikely((short_name_nbytes & 1) | (name_nbytes & 1))) { ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto err_free_dentry; } /* We now know the length of the file name and short name. Make sure * the length of the dentry is large enough to actually hold them. */ - calculated_size = dentry_min_len_with_names(file_name_nbytes, + calculated_size = dentry_min_len_with_names(name_nbytes, short_name_nbytes); if (unlikely(length < calculated_size)) { @@ -1498,25 +1479,25 @@ read_dentry(const u8 * restrict buf, size_t buf_len, /* Read the filename if present. Note: if the filename is empty, there * is no null terminator following it. */ - if (file_name_nbytes) { - dentry->file_name = utf16le_dupz(p, file_name_nbytes); - if (dentry->file_name == NULL) { + if (name_nbytes) { + dentry->d_name = utf16le_dupz(p, name_nbytes); + if (unlikely(!dentry->d_name)) { ret = WIMLIB_ERR_NOMEM; goto err_free_dentry; } - dentry->file_name_nbytes = file_name_nbytes; - p += (u32)file_name_nbytes + 2; + dentry->d_name_nbytes = name_nbytes; + p += (u32)name_nbytes + 2; } /* Read the short filename if present. Note: if there is no short * filename, there is no null terminator following it. */ if (short_name_nbytes) { - dentry->short_name = utf16le_dupz(p, short_name_nbytes); - if (dentry->short_name == NULL) { + dentry->d_short_name = utf16le_dupz(p, short_name_nbytes); + if (unlikely(!dentry->d_short_name)) { ret = WIMLIB_ERR_NOMEM; goto err_free_dentry; } - dentry->short_name_nbytes = short_name_nbytes; + dentry->d_short_name_nbytes = short_name_nbytes; p += (u32)short_name_nbytes + 2; } @@ -1551,13 +1532,13 @@ err_free_dentry: static bool dentry_is_dot_or_dotdot(const struct wim_dentry *dentry) { - if (dentry->file_name_nbytes <= 4) { - if (dentry->file_name_nbytes == 4) { - if (dentry->file_name[0] == cpu_to_le16('.') && - dentry->file_name[1] == cpu_to_le16('.')) + if (dentry->d_name_nbytes <= 4) { + if (dentry->d_name_nbytes == 4) { + if (dentry->d_name[0] == cpu_to_le16('.') && + dentry->d_name[1] == cpu_to_le16('.')) return true; - } else if (dentry->file_name_nbytes == 2) { - if (dentry->file_name[0] == cpu_to_le16('.')) + } else if (dentry->d_name_nbytes == 2) { + if (dentry->d_name[0] == cpu_to_le16('.')) return true; } } @@ -1568,14 +1549,14 @@ static int read_dentry_tree_recursive(const u8 * restrict buf, size_t buf_len, struct wim_dentry * restrict dir) { - u64 cur_offset = dir->subdir_offset; + u64 cur_offset = dir->d_subdir_offset; /* Check for cyclic directory structure, which would cause infinite * recursion if not handled. */ for (struct wim_dentry *d = dir->d_parent; !dentry_is_root(d); d = d->d_parent) { - if (unlikely(d->subdir_offset == cur_offset)) { + if (unlikely(d->d_subdir_offset == cur_offset)) { ERROR("Cyclic directory structure detected: children " "of \"%"TS"\" coincide with children of \"%"TS"\"", dentry_full_path(dir), dentry_full_path(d)); @@ -1629,7 +1610,7 @@ read_dentry_tree_recursive(const u8 * restrict buf, size_t buf_len, /* If this child is a directory that itself has children, call * this procedure recursively. */ - if (child->subdir_offset != 0) { + if (child->d_subdir_offset != 0) { if (likely(dentry_is_directory(child))) { ret = read_dentry_tree_recursive(buf, buf_len, @@ -1695,7 +1676,7 @@ read_dentry_tree(const u8 *buf, size_t buf_len, goto err_free_dentry_tree; } - if (likely(root->subdir_offset != 0)) { + if (likely(root->d_subdir_offset != 0)) { ret = read_dentry_tree_recursive(buf, buf_len, root); if (ret) goto err_free_dentry_tree; @@ -1767,7 +1748,7 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) disk_dentry->attributes = cpu_to_le32(inode->i_attributes); disk_dentry->security_id = cpu_to_le32(inode->i_security_id); - disk_dentry->subdir_offset = cpu_to_le64(dentry->subdir_offset); + disk_dentry->subdir_offset = cpu_to_le64(dentry->d_subdir_offset); disk_dentry->unused_1 = cpu_to_le64(0); disk_dentry->unused_2 = cpu_to_le64(0); @@ -1775,28 +1756,27 @@ 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); + disk_dentry->unknown_0x54 = cpu_to_le32(inode->i_unknown_0x54); 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); - disk_dentry->reparse.rp_unknown_2 = cpu_to_le16(inode->i_rp_unknown_2); - disk_dentry->reparse.not_rpfixed = cpu_to_le16(inode->i_not_rpfixed); + disk_dentry->reparse.rp_reserved = cpu_to_le16(inode->i_rp_reserved); + disk_dentry->reparse.rp_flags = cpu_to_le16(inode->i_rp_flags); } else { - disk_dentry->nonreparse.rp_unknown_1 = cpu_to_le32(inode->i_rp_unknown_1); disk_dentry->nonreparse.hard_link_group_id = cpu_to_le64((inode->i_nlink == 1) ? 0 : inode->i_ino); } - disk_dentry->short_name_nbytes = cpu_to_le16(dentry->short_name_nbytes); - disk_dentry->file_name_nbytes = cpu_to_le16(dentry->file_name_nbytes); + disk_dentry->short_name_nbytes = cpu_to_le16(dentry->d_short_name_nbytes); + disk_dentry->name_nbytes = cpu_to_le16(dentry->d_name_nbytes); p += sizeof(struct wim_dentry_on_disk); wimlib_assert(dentry_is_root(dentry) != dentry_has_long_name(dentry)); if (dentry_has_long_name(dentry)) - p = mempcpy(p, dentry->file_name, (u32)dentry->file_name_nbytes + 2); + p = mempcpy(p, dentry->d_name, (u32)dentry->d_name_nbytes + 2); if (dentry_has_short_name(dentry)) - p = mempcpy(p, dentry->short_name, (u32)dentry->short_name_nbytes + 2); + p = mempcpy(p, dentry->d_short_name, (u32)dentry->d_short_name_nbytes + 2); /* Align to 8-byte boundary */ while ((uintptr_t)p & 7) @@ -1889,7 +1869,7 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) static int write_dir_dentries(struct wim_dentry *dir, void *_pp) { - if (dir->subdir_offset != 0) { + if (dir->d_subdir_offset != 0) { u8 **pp = _pp; u8 *p = *pp; struct wim_dentry *child;