Skip to content

Commit f046fbb

Browse files
neilbrownbrauner
authored andcommitted
ecryptfs: use new start_creating/start_removing APIs
This requires the addition of start_creating_dentry() which is given the dentry which has already been found, and asks for it to be locked and its parent validated. Reviewed-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20251113002050.676694-14-neilb@ownmail.net Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 833d2b3 commit f046fbb

File tree

3 files changed

+107
-81
lines changed

3 files changed

+107
-81
lines changed

fs/ecryptfs/inode.c

Lines changed: 72 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,26 @@
2424
#include <linux/unaligned.h>
2525
#include "ecryptfs_kernel.h"
2626

27-
static int lock_parent(struct dentry *dentry,
28-
struct dentry **lower_dentry,
29-
struct inode **lower_dir)
27+
static struct dentry *ecryptfs_start_creating_dentry(struct dentry *dentry)
3028
{
31-
struct dentry *lower_dir_dentry;
29+
struct dentry *parent = dget_parent(dentry);
30+
struct dentry *ret;
3231

33-
lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
34-
*lower_dir = d_inode(lower_dir_dentry);
35-
*lower_dentry = ecryptfs_dentry_to_lower(dentry);
32+
ret = start_creating_dentry(ecryptfs_dentry_to_lower(parent),
33+
ecryptfs_dentry_to_lower(dentry));
34+
dput(parent);
35+
return ret;
36+
}
3637

37-
inode_lock_nested(*lower_dir, I_MUTEX_PARENT);
38-
return (*lower_dentry)->d_parent == lower_dir_dentry ? 0 : -EINVAL;
38+
static struct dentry *ecryptfs_start_removing_dentry(struct dentry *dentry)
39+
{
40+
struct dentry *parent = dget_parent(dentry);
41+
struct dentry *ret;
42+
43+
ret = start_removing_dentry(ecryptfs_dentry_to_lower(parent),
44+
ecryptfs_dentry_to_lower(dentry));
45+
dput(parent);
46+
return ret;
3947
}
4048

4149
static int ecryptfs_inode_test(struct inode *inode, void *lower_inode)
@@ -141,15 +149,12 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
141149
struct inode *lower_dir;
142150
int rc;
143151

144-
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
145-
dget(lower_dentry); // don't even try to make the lower negative
146-
if (!rc) {
147-
if (d_unhashed(lower_dentry))
148-
rc = -EINVAL;
149-
else
150-
rc = vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry,
151-
NULL);
152-
}
152+
lower_dentry = ecryptfs_start_removing_dentry(dentry);
153+
if (IS_ERR(lower_dentry))
154+
return PTR_ERR(lower_dentry);
155+
156+
lower_dir = lower_dentry->d_parent->d_inode;
157+
rc = vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);
153158
if (rc) {
154159
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
155160
goto out_unlock;
@@ -158,8 +163,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
158163
set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
159164
inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
160165
out_unlock:
161-
dput(lower_dentry);
162-
inode_unlock(lower_dir);
166+
end_removing(lower_dentry);
163167
if (!rc)
164168
d_drop(dentry);
165169
return rc;
@@ -186,10 +190,12 @@ ecryptfs_do_create(struct inode *directory_inode,
186190
struct inode *lower_dir;
187191
struct inode *inode;
188192

189-
rc = lock_parent(ecryptfs_dentry, &lower_dentry, &lower_dir);
190-
if (!rc)
191-
rc = vfs_create(&nop_mnt_idmap, lower_dir,
192-
lower_dentry, mode, true);
193+
lower_dentry = ecryptfs_start_creating_dentry(ecryptfs_dentry);
194+
if (IS_ERR(lower_dentry))
195+
return ERR_CAST(lower_dentry);
196+
lower_dir = lower_dentry->d_parent->d_inode;
197+
rc = vfs_create(&nop_mnt_idmap, lower_dir,
198+
lower_dentry, mode, true);
193199
if (rc) {
194200
printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
195201
"rc = [%d]\n", __func__, rc);
@@ -205,7 +211,7 @@ ecryptfs_do_create(struct inode *directory_inode,
205211
fsstack_copy_attr_times(directory_inode, lower_dir);
206212
fsstack_copy_inode_size(directory_inode, lower_dir);
207213
out_lock:
208-
inode_unlock(lower_dir);
214+
end_creating(lower_dentry, NULL);
209215
return inode;
210216
}
211217

@@ -433,10 +439,12 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
433439

434440
file_size_save = i_size_read(d_inode(old_dentry));
435441
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
436-
rc = lock_parent(new_dentry, &lower_new_dentry, &lower_dir);
437-
if (!rc)
438-
rc = vfs_link(lower_old_dentry, &nop_mnt_idmap, lower_dir,
439-
lower_new_dentry, NULL);
442+
lower_new_dentry = ecryptfs_start_creating_dentry(new_dentry);
443+
if (IS_ERR(lower_new_dentry))
444+
return PTR_ERR(lower_new_dentry);
445+
lower_dir = lower_new_dentry->d_parent->d_inode;
446+
rc = vfs_link(lower_old_dentry, &nop_mnt_idmap, lower_dir,
447+
lower_new_dentry, NULL);
440448
if (rc || d_really_is_negative(lower_new_dentry))
441449
goto out_lock;
442450
rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb);
@@ -448,7 +456,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
448456
ecryptfs_inode_to_lower(d_inode(old_dentry))->i_nlink);
449457
i_size_write(d_inode(new_dentry), file_size_save);
450458
out_lock:
451-
inode_unlock(lower_dir);
459+
end_creating(lower_new_dentry, NULL);
452460
return rc;
453461
}
454462

@@ -468,9 +476,11 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap,
468476
size_t encoded_symlen;
469477
struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;
470478

471-
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
472-
if (rc)
473-
goto out_lock;
479+
lower_dentry = ecryptfs_start_creating_dentry(dentry);
480+
if (IS_ERR(lower_dentry))
481+
return PTR_ERR(lower_dentry);
482+
lower_dir = lower_dentry->d_parent->d_inode;
483+
474484
mount_crypt_stat = &ecryptfs_superblock_to_private(
475485
dir->i_sb)->mount_crypt_stat;
476486
rc = ecryptfs_encrypt_and_encode_filename(&encoded_symname,
@@ -490,7 +500,7 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap,
490500
fsstack_copy_attr_times(dir, lower_dir);
491501
fsstack_copy_inode_size(dir, lower_dir);
492502
out_lock:
493-
inode_unlock(lower_dir);
503+
end_creating(lower_dentry, NULL);
494504
if (d_really_is_negative(dentry))
495505
d_drop(dentry);
496506
return rc;
@@ -501,12 +511,14 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
501511
{
502512
int rc;
503513
struct dentry *lower_dentry;
514+
struct dentry *lower_dir_dentry;
504515
struct inode *lower_dir;
505516

506-
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
507-
if (rc)
508-
goto out;
509-
517+
lower_dentry = ecryptfs_start_creating_dentry(dentry);
518+
if (IS_ERR(lower_dentry))
519+
return lower_dentry;
520+
lower_dir_dentry = dget(lower_dentry->d_parent);
521+
lower_dir = lower_dir_dentry->d_inode;
510522
lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
511523
lower_dentry, mode);
512524
rc = PTR_ERR(lower_dentry);
@@ -522,7 +534,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
522534
fsstack_copy_inode_size(dir, lower_dir);
523535
set_nlink(dir, lower_dir->i_nlink);
524536
out:
525-
inode_unlock(lower_dir);
537+
end_creating(lower_dentry, lower_dir_dentry);
526538
if (d_really_is_negative(dentry))
527539
d_drop(dentry);
528540
return ERR_PTR(rc);
@@ -534,21 +546,18 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
534546
struct inode *lower_dir;
535547
int rc;
536548

537-
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
538-
dget(lower_dentry); // don't even try to make the lower negative
539-
if (!rc) {
540-
if (d_unhashed(lower_dentry))
541-
rc = -EINVAL;
542-
else
543-
rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry);
544-
}
549+
lower_dentry = ecryptfs_start_removing_dentry(dentry);
550+
if (IS_ERR(lower_dentry))
551+
return PTR_ERR(lower_dentry);
552+
lower_dir = lower_dentry->d_parent->d_inode;
553+
554+
rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry);
545555
if (!rc) {
546556
clear_nlink(d_inode(dentry));
547557
fsstack_copy_attr_times(dir, lower_dir);
548558
set_nlink(dir, lower_dir->i_nlink);
549559
}
550-
dput(lower_dentry);
551-
inode_unlock(lower_dir);
560+
end_removing(lower_dentry);
552561
if (!rc)
553562
d_drop(dentry);
554563
return rc;
@@ -562,10 +571,12 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
562571
struct dentry *lower_dentry;
563572
struct inode *lower_dir;
564573

565-
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
566-
if (!rc)
567-
rc = vfs_mknod(&nop_mnt_idmap, lower_dir,
568-
lower_dentry, mode, dev);
574+
lower_dentry = ecryptfs_start_creating_dentry(dentry);
575+
if (IS_ERR(lower_dentry))
576+
return PTR_ERR(lower_dentry);
577+
lower_dir = lower_dentry->d_parent->d_inode;
578+
579+
rc = vfs_mknod(&nop_mnt_idmap, lower_dir, lower_dentry, mode, dev);
569580
if (rc || d_really_is_negative(lower_dentry))
570581
goto out;
571582
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
@@ -574,7 +585,7 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
574585
fsstack_copy_attr_times(dir, lower_dir);
575586
fsstack_copy_inode_size(dir, lower_dir);
576587
out:
577-
inode_unlock(lower_dir);
588+
end_removing(lower_dentry);
578589
if (d_really_is_negative(dentry))
579590
d_drop(dentry);
580591
return rc;
@@ -590,7 +601,6 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
590601
struct dentry *lower_new_dentry;
591602
struct dentry *lower_old_dir_dentry;
592603
struct dentry *lower_new_dir_dentry;
593-
struct dentry *trap;
594604
struct inode *target_inode;
595605
struct renamedata rd = {};
596606

@@ -605,31 +615,13 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
605615

606616
target_inode = d_inode(new_dentry);
607617

608-
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
609-
if (IS_ERR(trap))
610-
return PTR_ERR(trap);
611-
dget(lower_new_dentry);
612-
rc = -EINVAL;
613-
if (lower_old_dentry->d_parent != lower_old_dir_dentry)
614-
goto out_lock;
615-
if (lower_new_dentry->d_parent != lower_new_dir_dentry)
616-
goto out_lock;
617-
if (d_unhashed(lower_old_dentry) || d_unhashed(lower_new_dentry))
618-
goto out_lock;
619-
/* source should not be ancestor of target */
620-
if (trap == lower_old_dentry)
621-
goto out_lock;
622-
/* target should not be ancestor of source */
623-
if (trap == lower_new_dentry) {
624-
rc = -ENOTEMPTY;
625-
goto out_lock;
626-
}
618+
rd.mnt_idmap = &nop_mnt_idmap;
619+
rd.old_parent = lower_old_dir_dentry;
620+
rd.new_parent = lower_new_dir_dentry;
621+
rc = start_renaming_two_dentries(&rd, lower_old_dentry, lower_new_dentry);
622+
if (rc)
623+
return rc;
627624

628-
rd.mnt_idmap = &nop_mnt_idmap;
629-
rd.old_parent = lower_old_dir_dentry;
630-
rd.old_dentry = lower_old_dentry;
631-
rd.new_parent = lower_new_dir_dentry;
632-
rd.new_dentry = lower_new_dentry;
633625
rc = vfs_rename(&rd);
634626
if (rc)
635627
goto out_lock;
@@ -640,8 +632,7 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
640632
if (new_dir != old_dir)
641633
fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry));
642634
out_lock:
643-
dput(lower_new_dentry);
644-
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
635+
end_renaming(&rd);
645636
return rc;
646637
}
647638

fs/namei.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3397,6 +3397,39 @@ struct dentry *start_removing_noperm(struct dentry *parent,
33973397
}
33983398
EXPORT_SYMBOL(start_removing_noperm);
33993399

3400+
/**
3401+
* start_creating_dentry - prepare to create a given dentry
3402+
* @parent: directory from which dentry should be removed
3403+
* @child: the dentry to be removed
3404+
*
3405+
* A lock is taken to protect the dentry again other dirops and
3406+
* the validity of the dentry is checked: correct parent and still hashed.
3407+
*
3408+
* If the dentry is valid and negative a reference is taken and
3409+
* returned. If not an error is returned.
3410+
*
3411+
* end_creating() should be called when creation is complete, or aborted.
3412+
*
3413+
* Returns: the valid dentry, or an error.
3414+
*/
3415+
struct dentry *start_creating_dentry(struct dentry *parent,
3416+
struct dentry *child)
3417+
{
3418+
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
3419+
if (unlikely(IS_DEADDIR(parent->d_inode) ||
3420+
child->d_parent != parent ||
3421+
d_unhashed(child))) {
3422+
inode_unlock(parent->d_inode);
3423+
return ERR_PTR(-EINVAL);
3424+
}
3425+
if (d_is_positive(child)) {
3426+
inode_unlock(parent->d_inode);
3427+
return ERR_PTR(-EEXIST);
3428+
}
3429+
return dget(child);
3430+
}
3431+
EXPORT_SYMBOL(start_creating_dentry);
3432+
34003433
/**
34013434
* start_removing_dentry - prepare to remove a given dentry
34023435
* @parent: directory from which dentry should be removed

include/linux/namei.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ struct dentry *start_removing_killable(struct mnt_idmap *idmap,
100100
struct qstr *name);
101101
struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *name);
102102
struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *name);
103+
struct dentry *start_creating_dentry(struct dentry *parent,
104+
struct dentry *child);
103105
struct dentry *start_removing_dentry(struct dentry *parent,
104106
struct dentry *child);
105107

0 commit comments

Comments
 (0)