Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+39 -51
+21 -29
fs/libfs.c
··· 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 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 /**
+1 -1
include/linux/fs.h
··· 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,
+17 -21
mm/shmem.c
··· 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 /* ··· 4038 4050 { 4039 4051 struct inode *inode = d_inode(old_dentry); 4040 4052 int they_are_dirs = S_ISDIR(inode->i_mode); 4053 + bool had_offset = false; 4041 4054 int error; 4042 4055 4043 4056 if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) ··· 4051 4062 if (!simple_empty(new_dentry)) 4052 4063 return -ENOTEMPTY; 4053 4064 4054 - if (flags & RENAME_WHITEOUT) { 4055 - error = shmem_whiteout(idmap, old_dir, old_dentry); 4056 - if (error) 4057 - return error; 4058 - } 4059 - 4060 - error = simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry); 4061 - if (error) 4065 + error = simple_offset_add(shmem_get_offset_ctx(new_dir), new_dentry); 4066 + if (error == -EBUSY) 4067 + had_offset = true; 4068 + else if (unlikely(error)) 4062 4069 return error; 4063 4070 4071 + if (flags & RENAME_WHITEOUT) { 4072 + error = shmem_whiteout(idmap, old_dir, old_dentry); 4073 + if (error) { 4074 + if (!had_offset) 4075 + simple_offset_remove(shmem_get_offset_ctx(new_dir), 4076 + new_dentry); 4077 + return error; 4078 + } 4079 + } 4080 + 4081 + simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry); 4064 4082 if (d_really_is_positive(new_dentry)) { 4065 4083 (void) shmem_unlink(new_dir, new_dentry); 4066 4084 if (they_are_dirs) {