Skip to content

Commit bcf0d9d

Browse files
author
Al Viro
committed
ecryptfs: fix unlink and rmdir in face of underlying fs modifications
A problem similar to the one caught in commit 74dd7c9 ("ecryptfs_rename(): verify that lower dentries are still OK after lock_rename()") exists for unlink/rmdir as well. Instead of playing with dget_parent() of underlying dentry of victim and hoping it's the same as underlying dentry of our directory, do the following: * find the underlying dentry of victim * find the underlying directory of victim's parent (stable since the victim is ecryptfs dentry and inode of its parent is held exclusive by the caller). * lock the inode of dentry underlying the victim's parent * check that underlying dentry of victim is still hashed and has the right parent - it can be moved, but it can't be moved to/from the directory we are holding exclusive. So while ->d_parent itself might not be stable, the result of comparison is. If the check passes, everything is fine - underlying directory is locked, underlying victim is still a child of that directory and we can go ahead and feed them to vfs_unlink(). As in the current mainline we need to pin the underlying dentry of victim, so that it wouldn't go negative under us, but that's the only temporary reference that needs to be grabbed there. Underlying dentry of parent won't go away (it's pinned by the parent, which is held by caller), so there's no need to grab it. The same problem (with the same solution) exists for rmdir. Moreover, rename gets simpler and more robust with the same "don't bother with dget_parent()" approach. Fixes: 74dd7c9 "ecryptfs_rename(): verify that lower dentries are still OK after lock_rename()" Signed-off-by: Al Viro <[email protected]>
1 parent 69924b8 commit bcf0d9d

File tree

1 file changed

+40
-25
lines changed

1 file changed

+40
-25
lines changed

fs/ecryptfs/inode.c

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -128,24 +128,32 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
128128
struct inode *inode)
129129
{
130130
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
131-
struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
132131
struct dentry *lower_dir_dentry;
132+
struct inode *lower_dir_inode;
133133
int rc;
134134

135-
dget(lower_dentry);
136-
lower_dir_dentry = lock_parent(lower_dentry);
137-
rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
135+
lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
136+
lower_dir_inode = d_inode(lower_dir_dentry);
137+
inode_lock_nested(lower_dir_inode, I_MUTEX_PARENT);
138+
dget(lower_dentry); // don't even try to make the lower negative
139+
if (lower_dentry->d_parent != lower_dir_dentry)
140+
rc = -EINVAL;
141+
else if (d_unhashed(lower_dentry))
142+
rc = -EINVAL;
143+
else
144+
rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
138145
if (rc) {
139146
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
140147
goto out_unlock;
141148
}
142149
fsstack_copy_attr_times(dir, lower_dir_inode);
143150
set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
144151
inode->i_ctime = dir->i_ctime;
145-
d_drop(dentry);
146152
out_unlock:
147-
unlock_dir(lower_dir_dentry);
148153
dput(lower_dentry);
154+
inode_unlock(lower_dir_inode);
155+
if (!rc)
156+
d_drop(dentry);
149157
return rc;
150158
}
151159

@@ -512,22 +520,30 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
512520
{
513521
struct dentry *lower_dentry;
514522
struct dentry *lower_dir_dentry;
523+
struct inode *lower_dir_inode;
515524
int rc;
516525

517526
lower_dentry = ecryptfs_dentry_to_lower(dentry);
518-
dget(dentry);
519-
lower_dir_dentry = lock_parent(lower_dentry);
520-
dget(lower_dentry);
521-
rc = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
522-
dput(lower_dentry);
523-
if (!rc && d_really_is_positive(dentry))
527+
lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
528+
lower_dir_inode = d_inode(lower_dir_dentry);
529+
530+
inode_lock_nested(lower_dir_inode, I_MUTEX_PARENT);
531+
dget(lower_dentry); // don't even try to make the lower negative
532+
if (lower_dentry->d_parent != lower_dir_dentry)
533+
rc = -EINVAL;
534+
else if (d_unhashed(lower_dentry))
535+
rc = -EINVAL;
536+
else
537+
rc = vfs_rmdir(lower_dir_inode, lower_dentry);
538+
if (!rc) {
524539
clear_nlink(d_inode(dentry));
525-
fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry));
526-
set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink);
527-
unlock_dir(lower_dir_dentry);
540+
fsstack_copy_attr_times(dir, lower_dir_inode);
541+
set_nlink(dir, lower_dir_inode->i_nlink);
542+
}
543+
dput(lower_dentry);
544+
inode_unlock(lower_dir_inode);
528545
if (!rc)
529546
d_drop(dentry);
530-
dput(dentry);
531547
return rc;
532548
}
533549

@@ -565,20 +581,22 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
565581
struct dentry *lower_new_dentry;
566582
struct dentry *lower_old_dir_dentry;
567583
struct dentry *lower_new_dir_dentry;
568-
struct dentry *trap = NULL;
584+
struct dentry *trap;
569585
struct inode *target_inode;
570586

571587
if (flags)
572588
return -EINVAL;
573589

590+
lower_old_dir_dentry = ecryptfs_dentry_to_lower(old_dentry->d_parent);
591+
lower_new_dir_dentry = ecryptfs_dentry_to_lower(new_dentry->d_parent);
592+
574593
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
575594
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
576-
dget(lower_old_dentry);
577-
dget(lower_new_dentry);
578-
lower_old_dir_dentry = dget_parent(lower_old_dentry);
579-
lower_new_dir_dentry = dget_parent(lower_new_dentry);
595+
580596
target_inode = d_inode(new_dentry);
597+
581598
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
599+
dget(lower_new_dentry);
582600
rc = -EINVAL;
583601
if (lower_old_dentry->d_parent != lower_old_dir_dentry)
584602
goto out_lock;
@@ -606,11 +624,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
606624
if (new_dir != old_dir)
607625
fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry));
608626
out_lock:
609-
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
610-
dput(lower_new_dir_dentry);
611-
dput(lower_old_dir_dentry);
612627
dput(lower_new_dentry);
613-
dput(lower_old_dentry);
628+
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
614629
return rc;
615630
}
616631

0 commit comments

Comments
 (0)