libfs: Fix simple_offset_rename_exchange()
authorChuck Lever <chuck.lever@oracle.com>
Mon, 15 Apr 2024 15:20:54 +0000 (11:20 -0400)
committerChristian Brauner <brauner@kernel.org>
Wed, 17 Apr 2024 11:49:43 +0000 (13:49 +0200)
User space expects the replacement (old) directory entry to have
the same directory offset after the rename.

Suggested-by: Christian Brauner <brauner@kernel.org>
Fixes: a2e459555c5f ("shmem: stable directory offsets")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Link: https://lore.kernel.org/r/20240415152057.4605-2-cel@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/libfs.c

index 3a6f2cb364f8cbcc9fa78d6a0a45519ad6fcd696..ab61fae92cde8b304dae01817a7f819304904680 100644 (file)
@@ -295,6 +295,18 @@ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
        return 0;
 }
 
+static int simple_offset_replace(struct offset_ctx *octx, struct dentry *dentry,
+                                long offset)
+{
+       int ret;
+
+       ret = mtree_store(&octx->mt, offset, dentry, GFP_KERNEL);
+       if (ret)
+               return ret;
+       offset_set(dentry, offset);
+       return 0;
+}
+
 /**
  * simple_offset_remove - Remove an entry to a directory's offset map
  * @octx: directory offset ctx to be updated
@@ -352,6 +364,9 @@ int simple_offset_empty(struct dentry *dentry)
  * @new_dir: destination parent
  * @new_dentry: destination dentry
  *
+ * This API preserves the directory offset values. Caller provides
+ * appropriate serialization.
+ *
  * Returns zero on success. Otherwise a negative errno is returned and the
  * rename is rolled back.
  */
@@ -369,11 +384,11 @@ int simple_offset_rename_exchange(struct inode *old_dir,
        simple_offset_remove(old_ctx, old_dentry);
        simple_offset_remove(new_ctx, new_dentry);
 
-       ret = simple_offset_add(new_ctx, old_dentry);
+       ret = simple_offset_replace(new_ctx, old_dentry, new_index);
        if (ret)
                goto out_restore;
 
-       ret = simple_offset_add(old_ctx, new_dentry);
+       ret = simple_offset_replace(old_ctx, new_dentry, old_index);
        if (ret) {
                simple_offset_remove(new_ctx, old_dentry);
                goto out_restore;
@@ -388,10 +403,8 @@ int simple_offset_rename_exchange(struct inode *old_dir,
        return 0;
 
 out_restore:
-       offset_set(old_dentry, old_index);
-       mtree_store(&old_ctx->mt, old_index, old_dentry, GFP_KERNEL);
-       offset_set(new_dentry, new_index);
-       mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
+       (void)simple_offset_replace(old_ctx, old_dentry, old_index);
+       (void)simple_offset_replace(new_ctx, new_dentry, new_index);
        return ret;
 }