Viewing: ext4-hash-indexed-dir-dotdot-update.patch
commit 0536b2a26992f5d2ef9e3537a196afac81281f60
Author: Li Dongyang <dongyangli@ddn.com>
AuthorDate: Wed Apr 17 15:36:55 2024 +1000
LU-17711 osd-ldiskfs: do not delete dotdot during rename
Since upstream kernel commit v5.12-rc4-32-g6c0912739699
ext4_dir_entry_2 after rec_len will be wiped when deleting
the entry.
This creates a problem with rename, when we delete dotdot
first and if it's a dx dir, kernel will wipe entire dx_root
in the block after dotdot entry.
We can just update the dotdot entry in-place without deleting.
For dx dirs, ext4_update_dotdot() takes care of dotdot and
inserting dotdot is an update, use it for linear dirs also.
Rewrite ext4_update_dotdot() to get a few fixes:
*use ext4_read_dirblock to get the first block.
*do not assert on data read from disk, we check the dot and
dotdot entry and if anything looks wrong, we return -EFSCORRUPTED.
*make sure the change is journalled.
*set metadata_csum correctly for dx dirs.
Update ext4-data-in-dirent.patch, if dotdot entry has no space
for dirdata, try to expand the dotdot entry by moving the
entries behind it, or move the dx_root for dx dirs.
Add conf-sanity/154 to verify that the ".." entry was updated
properly after restore, including with an htree split directory
with dx_root entry.
Signed-off-by: Li Dongyang <dongyangli@ddn.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Shaun Tancheff <shaun.tancheff@hpe.com>
Reviewed-by: Jian Yu <yujian@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Change-Id: I33e862739fa44f583aaa4369190d6d80271db13b
Reviewed-on: https://review.whamcloud.com/54723
Index: linux-3.10.0-123.9.3.el7.x86_64/fs/ext4/namei.c
===================================================================
--- linux-3.10.0-123.9.3.el7.x86_64.orig/fs/ext4/namei.c
+++ linux-3.10.0-123.9.3.el7.x86_64/fs/ext4/namei.c
@@ -1894,6 +1894,67 @@ out_frames:
return retval;
}
+/* update ".." entry */
+static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
+ struct inode *inode)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+ struct buffer_head *bh;
+ struct ext4_dir_entry_2 *dot_de, *dotdot_de;
+ unsigned int offset;
+ int retval = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_DIRSYNC(dir))
+ handle->h_sync = 1;
+
+ bh = ext4_read_dirblock(dir, 0, EITHER);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+
+ dot_de = (struct ext4_dir_entry_2 *) bh->b_data;
+ if (ext4_check_dir_entry(dir, NULL, dot_de, bh, bh->b_data,
+ bh->b_size, 0) ||
+ le32_to_cpu(dot_de->inode) != dir->i_ino ||
+ strcmp(".", dot_de->name)) {
+ EXT4_ERROR_INODE(dir, "directory missing '.'");
+ retval = -EFSCORRUPTED;
+ goto out;
+ }
+ offset = ext4_rec_len_from_disk(dot_de->rec_len,
+ dir->i_sb->s_blocksize);
+ dotdot_de = ext4_next_entry(dot_de, dir->i_sb->s_blocksize);
+ if (ext4_check_dir_entry(dir, NULL, dotdot_de, bh, bh->b_data,
+ bh->b_size, offset) ||
+ le32_to_cpu(dotdot_de->inode) == 0 ||
+ strcmp("..", dotdot_de->name)) {
+ EXT4_ERROR_INODE(dir, "directory missing '..'");
+ retval = -EFSCORRUPTED;
+ goto out;
+ }
+
+ BUFFER_TRACE(dir_block, "get_write_access");
+ retval = ext4_journal_get_write_access(handle, bh);
+ if (retval)
+ goto out;
+
+ dotdot_de->inode = cpu_to_le32(inode->i_ino);
+
+ ext4_mark_inode_dirty(handle, dir);
+ BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
+ if (is_dx(dir)) {
+ retval = ext4_handle_dirty_dx_node(handle, dir, bh);
+ } else {
+ retval = ext4_handle_dirty_dirent_node(handle, dir, bh);
+ }
+
+out:
+ brelse(bh);
+ return retval;
+}
+
/*
* ext4_add_entry()
*
@@ -1937,6 +2003,10 @@ static int ext4_add_entry(handle_t *hand
}
}
+ if (dentry->d_name.len == 2 &&
+ memcmp(dentry->d_name.name, "..", 2) == 0)
+ return ext4_update_dotdot(handle, dentry, inode);
+
if (is_dx(dir)) {
retval = ext4_dx_add_entry(handle, dentry, inode);
if (!retval || (retval != ERR_BAD_DX_DIR))