From 8a9a7fd3666237fc1f11a7feb947287b167042a5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sat, 12 Oct 2013 23:14:40 -0400 Subject: [PATCH] libext2fs: add space for checksum when unconverting a hashed directory block 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 Signed-off-by: "Theodore Ts'o" --- lib/ext2fs/link.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c index e3ff450..2a44575 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) -- 1.8.3.1