History

Merge tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull shmem rename fixes from Al Viro: "A couple of shmem rename fixes - recent regression from tree-in-dcache series and older breakage from stable directory offsets stuff" * tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: shmem: fix recovery on rename failures shmem_whiteout(): fix regression from tree-in-dcache series

Linus Torvalds committed 11 months ago

Commit 40fbbd62 parents 53ec4a7 e1b4c6a3 files · +36 −48Browse files

Unified · Split

3 files
  1. fs/libfs.c +21 −29
  2. include/linux/fs.h +1 −1
  3. mm/shmem.c +14 −18
fs/libfs.c+21−29
@@ -346,22 +346,22 @@ void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry) · 345 unmodified lines
346 * User space expects the directory offset value of the replaced346 * User space expects the directory offset value of the replaced
347 * (new) directory entry to be unchanged after a rename.347 * (new) directory entry to be unchanged after a rename.
348 *348 *
349 * Returns zero on success, a negative errno value on failure.349 * Caller must have grabbed a slot for new_dentry in the maple_tree
350 * associated with new_dir, even if dentry is negative.
350 */351 */
351int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,352void simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
352 struct inode *new_dir, struct dentry *new_dentry)353 struct inode *new_dir, struct dentry *new_dentry)
353{354{
354 struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);355 struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
355 struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);356 struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
356 long new_offset = dentry2offset(new_dentry);357 long new_offset = dentry2offset(new_dentry);
357 358
358 simple_offset_remove(old_ctx, old_dentry);359 if (WARN_ON(!new_offset))
360 return;
359 361
360 if (new_offset) {362 simple_offset_remove(old_ctx, old_dentry);
361 offset_set(new_dentry, 0);363 offset_set(new_dentry, 0);
362 return simple_offset_replace(new_ctx, old_dentry, new_offset);364 WARN_ON(simple_offset_replace(new_ctx, old_dentry, new_offset));
363 }
364 return simple_offset_add(new_ctx, old_dentry);
365}365}
366 366
367/**367/**
@@ -388,31 +388,23 @@ int simple_offset_rename_exchange(struct inode *old_dir, · 20 unmodified lines
388 long new_index = dentry2offset(new_dentry);388 long new_index = dentry2offset(new_dentry);
389 int ret;389 int ret;
390 390
391 simple_offset_remove(old_ctx, old_dentry);391 if (WARN_ON(!old_index || !new_index))
392 simple_offset_remove(new_ctx, new_dentry);392 return -EINVAL;
393 393
394 ret = simple_offset_replace(new_ctx, old_dentry, new_index);394 ret = mtree_store(&new_ctx->mt, new_index, old_dentry, GFP_KERNEL);
395 if (ret)395 if (WARN_ON(ret))
396 goto out_restore;396 return ret;
397 397
398 ret = simple_offset_replace(old_ctx, new_dentry, old_index);398 ret = mtree_store(&old_ctx->mt, old_index, new_dentry, GFP_KERNEL);
399 if (ret) {399 if (WARN_ON(ret)) {
400 simple_offset_remove(new_ctx, old_dentry);400 mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
401 goto out_restore;401 return ret;
402 }402 }
403 403
404 ret = simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);404 offset_set(old_dentry, new_index);
405 if (ret) {405 offset_set(new_dentry, old_index);
406 simple_offset_remove(new_ctx, old_dentry);406 simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);
407 simple_offset_remove(old_ctx, new_dentry);
408 goto out_restore;
409 }
410 return 0;407 return 0;
411
412out_restore:
413 (void)simple_offset_replace(old_ctx, old_dentry, old_index);
414 (void)simple_offset_replace(new_ctx, new_dentry, new_index);
415 return ret;
416}408}
417 409
418/**410/**
include/linux/fs.h+1−1
@@ -3247,7 +3247,7 @@ struct offset_ctx { · 3246 unmodified lines
3247void simple_offset_init(struct offset_ctx *octx);3247void simple_offset_init(struct offset_ctx *octx);
3248int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);3248int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
3249void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);3249void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
3250int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,3250void simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
3251 struct inode *new_dir, struct dentry *new_dentry);3251 struct inode *new_dir, struct dentry *new_dentry);
3252int simple_offset_rename_exchange(struct inode *old_dir,3252int simple_offset_rename_exchange(struct inode *old_dir,
3253 struct dentry *old_dentry,3253 struct dentry *old_dentry,
mm/shmem.c+14−18
@@ -4019,22 +4019,10 @@ static int shmem_whiteout(struct mnt_idmap *idmap, · 4018 unmodified lines
4019 whiteout = d_alloc(old_dentry->d_parent, &old_dentry->d_name);4019 whiteout = d_alloc(old_dentry->d_parent, &old_dentry->d_name);
4020 if (!whiteout)4020 if (!whiteout)
4021 return -ENOMEM;4021 return -ENOMEM;
4022
4023 error = shmem_mknod(idmap, old_dir, whiteout,4022 error = shmem_mknod(idmap, old_dir, whiteout,
4024 S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);4023 S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
4025 dput(whiteout);4024 dput(whiteout);
4026 if (error)4025 return error;
4027 return error;
4028
4029 /*
4030 * Cheat and hash the whiteout while the old dentry is still in
4031 * place, instead of playing games with FS_RENAME_DOES_D_MOVE.
4032 *
4033 * d_lookup() will consistently find one of them at this point,
4034 * not sure which one, but that isn't even important.
4035 */
4036 d_rehash(whiteout);
4037 return 0;
4038}4026}
4039 4027
4040/*4028/*
@@ -4050,6 +4038,7 @@ static int shmem_rename2(struct mnt_idmap *idmap, · 9 unmodified lines
4050{4038{
4051 struct inode *inode = d_inode(old_dentry);4039 struct inode *inode = d_inode(old_dentry);
4052 int they_are_dirs = S_ISDIR(inode->i_mode);4040 int they_are_dirs = S_ISDIR(inode->i_mode);
4041 bool had_offset = false;
4053 int error;4042 int error;
4054 4043
4055 if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))4044 if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
@@ -4062,16 +4051,23 @@ static int shmem_rename2(struct mnt_idmap *idmap, · 6 unmodified lines
4062 if (!simple_empty(new_dentry))4051 if (!simple_empty(new_dentry))
4063 return -ENOTEMPTY;4052 return -ENOTEMPTY;
4064 4053
4054 error = simple_offset_add(shmem_get_offset_ctx(new_dir), new_dentry);
4055 if (error == -EBUSY)
4056 had_offset = true;
4057 else if (unlikely(error))
4058 return error;
4059
4065 if (flags & RENAME_WHITEOUT) {4060 if (flags & RENAME_WHITEOUT) {
4066 error = shmem_whiteout(idmap, old_dir, old_dentry);4061 error = shmem_whiteout(idmap, old_dir, old_dentry);
4067 if (error)4062 if (error) {
4063 if (!had_offset)
4064 simple_offset_remove(shmem_get_offset_ctx(new_dir),
4065 new_dentry);
4068 return error;4066 return error;
4067 }
4069 }4068 }
4070 4069
4071 error = simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);4070 simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);
4072 if (error)
4073 return error;
4074
4075 if (d_really_is_positive(new_dentry)) {4071 if (d_really_is_positive(new_dentry)) {
4076 (void) shmem_unlink(new_dir, new_dentry);4072 (void) shmem_unlink(new_dir, new_dentry);
4077 if (they_are_dirs) {4073 if (they_are_dirs) {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t