The ext2fs_link function has the unfortunate habit of converting hashed directories into unhashed directories. It doesn't notice that it's slicing and dicing directory entries from a former dx_{root,node} block, and therefore doesn't write a protective dirent into the end of the block to store the checksum. Teach it to do this. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- lib/ext2fs/link.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c index e3ff450..24fa083 100644 --- a/lib/ext2fs/link.c +++ b/lib/ext2fs/link.c @@ -42,6 +42,7 @@ static int link_proc(struct ext2_dir_entry *dirent, unsigned int rec_len, min_rec_len, curr_rec_len; int ret = 0; int csum_size = 0; + struct ext2_dir_entry_tail *t; if (ls->done) return 0; @@ -71,6 +72,40 @@ static int link_proc(struct ext2_dir_entry *dirent, } /* + * Since ext2fs_link blows away htree data, we need to be careful -- + * if metadata_csum is enabled and we're passed in a dirent that + * contains htree data, we need to create the fake entry at the end + * of the block that hides the checksum. + */ + + /* De-convert a dx_node block */ + if (csum_size && + curr_rec_len == ls->fs->blocksize && + !dirent->inode) { + curr_rec_len -= csum_size; + ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); + if (ls->err) + return DIRENT_ABORT; + t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize); + ext2fs_initialize_dirent_tail(ls->fs, t); + ret = DIRENT_CHANGED; + } + + /* De-convert a dx_root block */ + if (csum_size && + curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) && + offset == EXT2_DIR_REC_LEN(1) && + dirent->name[0] == '.' && dirent->name[1] == '.') { + curr_rec_len -= csum_size; + ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); + if (ls->err) + return DIRENT_ABORT; + t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize); + ext2fs_initialize_dirent_tail(ls->fs, t); + ret = DIRENT_CHANGED; + } + + /* * If the directory entry is used, see if we can split the * directory entry to make room for the new name. If so, * truncate it and return. @@ -152,6 +187,11 @@ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) return retval; + /* + * If this function changes to preserve the htree, remove the two + * hunks in link_proc that shove checksum tails into the former + * dx_root/dx_node blocks. + */ if (inode.i_flags & EXT2_INDEX_FL) { inode.i_flags &= ~EXT2_INDEX_FL; if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html