Skip to content

Commit 193feb6

Browse files
committed
Merge patch series 'Fix shmem_rename2 directory offset calculation' of https://lore.kernel.org/r/[email protected]
Pull shmem_rename2() offset fixes from Chuck Lever: The existing code in shmem_rename2() allocates a fresh directory offset value when renaming over an existing destination entry. User space does not expect this behavior. In particular, applications that rename while walking a directory can loop indefinitely because they never reach the end of the directory. * 'Fix shmem_rename2 directory offset calculation' of https://lore.kernel.org/r/[email protected]: (3 commits) shmem: Fix shmem_rename2() libfs: Add simple_offset_rename() API libfs: Fix simple_offset_rename_exchange() fs/libfs.c | 55 +++++++++++++++++++++++++++++++++++++++++----- include/linux/fs.h | 2 ++ mm/shmem.c | 3 +-- 3 files changed, 52 insertions(+), 8 deletions(-) Signed-off-by: Christian Brauner <[email protected]>
2 parents c6854e5 + ad191eb commit 193feb6

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

fs/libfs.c

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,18 @@ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
295295
return 0;
296296
}
297297

298+
static int simple_offset_replace(struct offset_ctx *octx, struct dentry *dentry,
299+
long offset)
300+
{
301+
int ret;
302+
303+
ret = mtree_store(&octx->mt, offset, dentry, GFP_KERNEL);
304+
if (ret)
305+
return ret;
306+
offset_set(dentry, offset);
307+
return 0;
308+
}
309+
298310
/**
299311
* simple_offset_remove - Remove an entry to a directory's offset map
300312
* @octx: directory offset ctx to be updated
@@ -345,13 +357,46 @@ int simple_offset_empty(struct dentry *dentry)
345357
return ret;
346358
}
347359

360+
/**
361+
* simple_offset_rename - handle directory offsets for rename
362+
* @old_dir: parent directory of source entry
363+
* @old_dentry: dentry of source entry
364+
* @new_dir: parent_directory of destination entry
365+
* @new_dentry: dentry of destination
366+
*
367+
* Caller provides appropriate serialization.
368+
*
369+
* User space expects the directory offset value of the replaced
370+
* (new) directory entry to be unchanged after a rename.
371+
*
372+
* Returns zero on success, a negative errno value on failure.
373+
*/
374+
int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
375+
struct inode *new_dir, struct dentry *new_dentry)
376+
{
377+
struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
378+
struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
379+
long new_offset = dentry2offset(new_dentry);
380+
381+
simple_offset_remove(old_ctx, old_dentry);
382+
383+
if (new_offset) {
384+
offset_set(new_dentry, 0);
385+
return simple_offset_replace(new_ctx, old_dentry, new_offset);
386+
}
387+
return simple_offset_add(new_ctx, old_dentry);
388+
}
389+
348390
/**
349391
* simple_offset_rename_exchange - exchange rename with directory offsets
350392
* @old_dir: parent of dentry being moved
351393
* @old_dentry: dentry being moved
352394
* @new_dir: destination parent
353395
* @new_dentry: destination dentry
354396
*
397+
* This API preserves the directory offset values. Caller provides
398+
* appropriate serialization.
399+
*
355400
* Returns zero on success. Otherwise a negative errno is returned and the
356401
* rename is rolled back.
357402
*/
@@ -369,11 +414,11 @@ int simple_offset_rename_exchange(struct inode *old_dir,
369414
simple_offset_remove(old_ctx, old_dentry);
370415
simple_offset_remove(new_ctx, new_dentry);
371416

372-
ret = simple_offset_add(new_ctx, old_dentry);
417+
ret = simple_offset_replace(new_ctx, old_dentry, new_index);
373418
if (ret)
374419
goto out_restore;
375420

376-
ret = simple_offset_add(old_ctx, new_dentry);
421+
ret = simple_offset_replace(old_ctx, new_dentry, old_index);
377422
if (ret) {
378423
simple_offset_remove(new_ctx, old_dentry);
379424
goto out_restore;
@@ -388,10 +433,8 @@ int simple_offset_rename_exchange(struct inode *old_dir,
388433
return 0;
389434

390435
out_restore:
391-
offset_set(old_dentry, old_index);
392-
mtree_store(&old_ctx->mt, old_index, old_dentry, GFP_KERNEL);
393-
offset_set(new_dentry, new_index);
394-
mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
436+
(void)simple_offset_replace(old_ctx, old_dentry, old_index);
437+
(void)simple_offset_replace(new_ctx, new_dentry, new_index);
395438
return ret;
396439
}
397440

include/linux/fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3353,6 +3353,8 @@ void simple_offset_init(struct offset_ctx *octx);
33533353
int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
33543354
void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
33553355
int simple_offset_empty(struct dentry *dentry);
3356+
int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
3357+
struct inode *new_dir, struct dentry *new_dentry);
33563358
int simple_offset_rename_exchange(struct inode *old_dir,
33573359
struct dentry *old_dentry,
33583360
struct inode *new_dir,

mm/shmem.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3473,8 +3473,7 @@ static int shmem_rename2(struct mnt_idmap *idmap,
34733473
return error;
34743474
}
34753475

3476-
simple_offset_remove(shmem_get_offset_ctx(old_dir), old_dentry);
3477-
error = simple_offset_add(shmem_get_offset_ctx(new_dir), old_dentry);
3476+
error = simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);
34783477
if (error)
34793478
return error;
34803479

0 commit comments

Comments
 (0)