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
346346 * User space expects the directory offset value of the replaced
347347 * (new) directory entry to be unchanged after a rename.
348348 *
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.
350351 */
351−int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
352− struct inode *new_dir, struct dentry *new_dentry)
352+void simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
353+ struct inode *new_dir, struct dentry *new_dentry)
353354 {
354355 struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
355356 struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
356357 long new_offset = dentry2offset(new_dentry);
357358
358− simple_offset_remove(old_ctx, old_dentry);
359+ if (WARN_ON(!new_offset))
360+ return;
359361
360− if (new_offset) {
361− offset_set(new_dentry, 0);
362− return simple_offset_replace(new_ctx, old_dentry, new_offset);
363− }
364− return simple_offset_add(new_ctx, old_dentry);
362+ simple_offset_remove(old_ctx, old_dentry);
363+ offset_set(new_dentry, 0);
364+ WARN_ON(simple_offset_replace(new_ctx, old_dentry, new_offset));
365365 }
366366
367367 /**
@@ -388,31 +388,23 @@ int simple_offset_rename_exchange(struct inode *old_dir, · 20 unmodified lines
388388 long new_index = dentry2offset(new_dentry);
389389 int ret;
390390
391− simple_offset_remove(old_ctx, old_dentry);
392− simple_offset_remove(new_ctx, new_dentry);
391+ if (WARN_ON(!old_index || !new_index))
392+ return -EINVAL;
393393
394− ret = simple_offset_replace(new_ctx, old_dentry, new_index);
395− if (ret)
396− goto out_restore;
394+ ret = mtree_store(&new_ctx->mt, new_index, old_dentry, GFP_KERNEL);
395+ if (WARN_ON(ret))
396+ return ret;
397397
398− ret = simple_offset_replace(old_ctx, new_dentry, old_index);
399− if (ret) {
400− simple_offset_remove(new_ctx, old_dentry);
401− goto out_restore;
398+ ret = mtree_store(&old_ctx->mt, old_index, new_dentry, GFP_KERNEL);
399+ if (WARN_ON(ret)) {
400+ mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
401+ return ret;
402402 }
403403
404− ret = simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);
405− if (ret) {
406− simple_offset_remove(new_ctx, old_dentry);
407− simple_offset_remove(old_ctx, new_dentry);
408− goto out_restore;
409− }
404+ offset_set(old_dentry, new_index);
405+ offset_set(new_dentry, old_index);
406+ simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);
410407 return 0;
411−
412−out_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;
416408 }
417409
418410 /**
include/linux/fs.h+1−1
@@ -3247,7 +3247,7 @@ struct offset_ctx { · 3246 unmodified lines
32473247 void simple_offset_init(struct offset_ctx *octx);
32483248 int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
32493249 void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
3250−int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
3250+void simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
32513251 struct inode *new_dir, struct dentry *new_dentry);
32523252 int simple_offset_rename_exchange(struct inode *old_dir,
32533253 struct dentry *old_dentry,
mm/shmem.c+14−18
@@ -4019,22 +4019,10 @@ static int shmem_whiteout(struct mnt_idmap *idmap, · 4018 unmodified lines
40194019 whiteout = d_alloc(old_dentry->d_parent, &old_dentry->d_name);
40204020 if (!whiteout)
40214021 return -ENOMEM;
4022−
40234022 error = shmem_mknod(idmap, old_dir, whiteout,
40244023 S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
40254024 dput(whiteout);
4026− if (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;
4025+ return error;
40384026 }
40394027
40404028 /*
@@ -4050,6 +4038,7 @@ static int shmem_rename2(struct mnt_idmap *idmap, · 9 unmodified lines
40504038 {
40514039 struct inode *inode = d_inode(old_dentry);
40524040 int they_are_dirs = S_ISDIR(inode->i_mode);
4041+ bool had_offset = false;
40534042 int error;
40544043
40554044 if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
@@ -4062,16 +4051,23 @@ static int shmem_rename2(struct mnt_idmap *idmap, · 6 unmodified lines
40624051 if (!simple_empty(new_dentry))
40634052 return -ENOTEMPTY;
40644053
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+
40654060 if (flags & RENAME_WHITEOUT) {
40664061 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);
40684066 return error;
4067+ }
40694068 }
40704069
4071− error = simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);
4072− if (error)
4073− return error;
4074−
4070+ simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);
40754071 if (d_really_is_positive(new_dentry)) {
40764072 (void) shmem_unlink(new_dir, new_dentry);
40774073 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