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 | 346 | * User space expects the directory offset value of the replaced |
| 347 | 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 | − 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) | |
| 353 | 354 | { |
| 354 | 355 | struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir); |
| 355 | 356 | struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir); |
| 356 | 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) { | |
| 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)); | |
| 365 | 365 | } |
| 366 | 366 | |
| 367 | 367 | /** |
| @@ -388,31 +388,23 @@ int simple_offset_rename_exchange(struct inode *old_dir, · 20 unmodified lines | ||
| 388 | 388 | long new_index = dentry2offset(new_dentry); |
| 389 | 389 | int ret; |
| 390 | 390 | |
| 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; | |
| 393 | 393 | |
| 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; | |
| 397 | 397 | |
| 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; | |
| 402 | 402 | } |
| 403 | 403 | |
| 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); | |
| 410 | 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 | 3247 | void simple_offset_init(struct offset_ctx *octx); |
| 3248 | 3248 | int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry); |
| 3249 | 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 | 3251 | struct inode *new_dir, struct dentry *new_dentry); |
| 3252 | 3252 | int simple_offset_rename_exchange(struct inode *old_dir, |
| 3253 | 3253 | struct dentry *old_dentry, |
| @@ -4019,22 +4019,10 @@ static int shmem_whiteout(struct mnt_idmap *idmap, · 4018 unmodified lines | ||
| 4019 | 4019 | whiteout = d_alloc(old_dentry->d_parent, &old_dentry->d_name); |
| 4020 | 4020 | if (!whiteout) |
| 4021 | 4021 | return -ENOMEM; |
| 4022 | − | |
| 4023 | 4022 | error = shmem_mknod(idmap, old_dir, whiteout, |
| 4024 | 4023 | S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV); |
| 4025 | 4024 | 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; | |
| 4038 | 4026 | } |
| 4039 | 4027 | |
| 4040 | 4028 | /* |
| @@ -4050,6 +4038,7 @@ static int shmem_rename2(struct mnt_idmap *idmap, · 9 unmodified lines | ||
| 4050 | 4038 | { |
| 4051 | 4039 | struct inode *inode = d_inode(old_dentry); |
| 4052 | 4040 | int they_are_dirs = S_ISDIR(inode->i_mode); |
| 4041 | + bool had_offset = false; | |
| 4053 | 4042 | int error; |
| 4054 | 4043 | |
| 4055 | 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 | 4051 | if (!simple_empty(new_dentry)) |
| 4063 | 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 | 4060 | if (flags & RENAME_WHITEOUT) { |
| 4066 | 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 | 4066 | return error; |
| 4067 | + } | |
| 4069 | 4068 | } |
| 4070 | 4069 | |
| 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); | |
| 4075 | 4071 | if (d_really_is_positive(new_dentry)) { |
| 4076 | 4072 | (void) shmem_unlink(new_dir, new_dentry); |
| 4077 | 4073 | if (they_are_dirs) { |