Unverified Commit 23cdd0ee authored by Chuck Lever's avatar Chuck Lever Committed by Christian Brauner
Browse files

libfs: Fix simple_offset_rename_exchange()



User space expects the replacement (old) directory entry to have
the same directory offset after the rename.

Suggested-by: default avatarChristian Brauner <brauner@kernel.org>
Fixes: a2e45955 ("shmem: stable directory offsets")
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Link: https://lore.kernel.org/r/20240415152057.4605-2-cel@kernel.org


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 4cece764
Loading
Loading
Loading
Loading
+19 −6
Original line number Diff line number Diff line
@@ -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;
}