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
Unified · Split
| @@ -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 replaced | 346 | * 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 | */ |
| 351 | int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry, | 352 | void 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 | |||
| 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; | ||
| 416 | } | 408 | } |
| 417 | 409 | ||
| 418 | /** | 410 | /** |
| @@ -3247,7 +3247,7 @@ struct offset_ctx { · 3246 unmodified lines | |||
| 3247 | void simple_offset_init(struct offset_ctx *octx); | 3247 | void simple_offset_init(struct offset_ctx *octx); |
| 3248 | int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry); | 3248 | int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry); |
| 3249 | void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry); | 3249 | 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, |
| 3251 | struct inode *new_dir, struct dentry *new_dentry); | 3251 | struct inode *new_dir, struct dentry *new_dentry); |
| 3252 | int simple_offset_rename_exchange(struct inode *old_dir, | 3252 | int simple_offset_rename_exchange(struct inode *old_dir, |
| 3253 | struct dentry *old_dentry, | 3253 | struct dentry *old_dentry, |
| @@ -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) { |