[PATCH 05/31] libext2fs: Add space for metadata checksum when unconverting a hashed directory block

Darrick J. Wong
2013-10-01T01:27:19Z

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@oracle.com>
---
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)