From 31f73bee3e170b7cabb35db9e2f4bf7919b9d036 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 29 Jul 2010 13:01:36 +0200 Subject: ecryptfs: release reference to lower mount if interpose fails In ecryptfs_lookup_and_interpose_lower() the lower mount is not decremented if allocation of a dentry info struct failed. As a result the lower filesystem cant be unmounted any more (since it is considered busy). This patch corrects the reference counting. Signed-off-by: Lino Sanfilippo Cc: stable@kernel.org Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 31ef5252f0fe..8cd617b66baa 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -264,7 +264,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); - goto out_dput; + goto out_put; } ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt); @@ -339,8 +339,9 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, out_free_kmem: kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; -out_dput: +out_put: dput(lower_dentry); + mntput(lower_mnt); d_drop(ecryptfs_dentry); out: return rc; -- cgit v1.2.3-55-g7522 From 21edad32205e97dc7ccb81a85234c77e760364c8 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 15 Jul 2010 15:11:55 +0200 Subject: ecryptfs: dont call lookup_one_len to avoid NULL nameidata I have encountered the same problem that Eric Sandeen described in this post http://lkml.org/lkml/fancy/2010/4/23/467 while experimenting with stackable filesystems. The reason seems to be that ecryptfs calls lookup_one_len() to get the lower dentry, which in turn calls the lower parent dirs d_revalidate() with a NULL nameidata object. If ecryptfs is the underlaying filesystem, the NULL pointer dereference occurs, since ecryptfs is not prepared to handle a NULL nameidata. I know that this cant happen any more, since it is no longer allowed to mount ecryptfs upon itself. But maybe this patch it useful nevertheless, since the problem would still apply for an underlaying filesystem that implements d_revalidate() and is not prepared to handle a NULL nameidata (I dont know if there actually is such a fs). With this patch (against 2.6.35-rc5) ecryptfs uses the vfs_lookup_path() function instead of lookup_one_len() which ensures that the nameidata passed to the lower filesystems d_revalidate(). Signed-off-by: Lino Sanfilippo Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 89 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 12 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 8cd617b66baa..a8acfc5d3476 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -347,6 +347,76 @@ out: return rc; } +/** + * ecryptfs_new_lower_dentry + * @ename: The name of the new dentry. + * @lower_dir_dentry: Parent directory of the new dentry. + * @nd: nameidata from last lookup. + * + * Create a new dentry or get it from lower parent dir. + */ +static struct dentry * +ecryptfs_new_lower_dentry(struct qstr *name, struct dentry *lower_dir_dentry, + struct nameidata *nd) +{ + struct dentry *new_dentry; + struct dentry *tmp; + struct inode *lower_dir_inode; + + lower_dir_inode = lower_dir_dentry->d_inode; + + tmp = d_alloc(lower_dir_dentry, name); + if (!tmp) + return ERR_PTR(-ENOMEM); + + mutex_lock(&lower_dir_inode->i_mutex); + new_dentry = lower_dir_inode->i_op->lookup(lower_dir_inode, tmp, nd); + mutex_unlock(&lower_dir_inode->i_mutex); + + if (!new_dentry) + new_dentry = tmp; + else + dput(tmp); + + return new_dentry; +} + + +/** + * ecryptfs_lookup_one_lower + * @ecryptfs_dentry: The eCryptfs dentry that we are looking up + * @lower_dir_dentry: lower parent directory + * + * Get the lower dentry from vfs. If lower dentry does not exist yet, + * create it. + */ +static struct dentry * +ecryptfs_lookup_one_lower(struct dentry *ecryptfs_dentry, + struct dentry *lower_dir_dentry) +{ + struct nameidata nd; + struct vfsmount *lower_mnt; + struct qstr *name; + int err; + + name = &ecryptfs_dentry->d_name; + lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( + ecryptfs_dentry->d_parent)); + err = vfs_path_lookup(lower_dir_dentry, lower_mnt, name->name , 0, &nd); + mntput(lower_mnt); + + if (!err) { + /* we dont need the mount */ + mntput(nd.path.mnt); + return nd.path.dentry; + } + if (err != -ENOENT) + return ERR_PTR(err); + + /* create a new lower dentry */ + return ecryptfs_new_lower_dentry(name, lower_dir_dentry, &nd); +} + /** * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode @@ -374,14 +444,12 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, goto out_d_drop; } lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent); - mutex_lock(&lower_dir_dentry->d_inode->i_mutex); - lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name, - lower_dir_dentry, - ecryptfs_dentry->d_name.len); - mutex_unlock(&lower_dir_dentry->d_inode->i_mutex); + + lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry, + lower_dir_dentry); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); - ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " + ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned " "[%d] on lower_dentry = [%s]\n", __func__, rc, encrypted_and_encoded_name); goto out_d_drop; @@ -403,14 +471,11 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, "filename; rc = [%d]\n", __func__, rc); goto out_d_drop; } - mutex_lock(&lower_dir_dentry->d_inode->i_mutex); - lower_dentry = lookup_one_len(encrypted_and_encoded_name, - lower_dir_dentry, - encrypted_and_encoded_name_size - 1); - mutex_unlock(&lower_dir_dentry->d_inode->i_mutex); + lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry, + lower_dir_dentry); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); - ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " + ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned " "[%d] on lower_dentry = [%s]\n", __func__, rc, encrypted_and_encoded_name); goto out_d_drop; -- cgit v1.2.3-55-g7522