diff options
Diffstat (limited to 'fs/ecryptfs/main.c')
| -rw-r--r-- | fs/ecryptfs/main.c | 136 | 
1 files changed, 45 insertions, 91 deletions
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index fdb2eb0ad09e..9f1bb747d77d 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -96,7 +96,7 @@ void __ecryptfs_printk(const char *fmt, ...)  }  /** - * ecryptfs_init_persistent_file + * ecryptfs_init_lower_file   * @ecryptfs_dentry: Fully initialized eCryptfs dentry object, with   *                   the lower dentry and the lower mount set   * @@ -104,109 +104,68 @@ void __ecryptfs_printk(const char *fmt, ...)   * inode. All I/O operations to the lower inode occur through that   * file. When the first eCryptfs dentry that interposes with the first   * lower dentry for that inode is created, this function creates the - * persistent file struct and associates it with the eCryptfs - * inode. When the eCryptfs inode is destroyed, the file is closed. + * lower file struct and associates it with the eCryptfs + * inode. When all eCryptfs files associated with the inode are released, the + * file is closed.   * - * The persistent file will be opened with read/write permissions, if + * The lower file will be opened with read/write permissions, if   * possible. Otherwise, it is opened read-only.   * - * This function does nothing if a lower persistent file is already + * This function does nothing if a lower file is already   * associated with the eCryptfs inode.   *   * Returns zero on success; non-zero otherwise   */ -int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry) +static int ecryptfs_init_lower_file(struct dentry *dentry, +				    struct file **lower_file)  {  	const struct cred *cred = current_cred(); -	struct ecryptfs_inode_info *inode_info = -		ecryptfs_inode_to_private(ecryptfs_dentry->d_inode); -	int rc = 0; - -	if (!inode_info->lower_file) { -		struct dentry *lower_dentry; -		struct vfsmount *lower_mnt = -			ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); +	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); +	struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); +	int rc; -		lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); -		rc = ecryptfs_privileged_open(&inode_info->lower_file, -					      lower_dentry, lower_mnt, cred); -		if (rc) { -			printk(KERN_ERR "Error opening lower persistent file " -			       "for lower_dentry [0x%p] and lower_mnt [0x%p]; " -			       "rc = [%d]\n", lower_dentry, lower_mnt, rc); -			inode_info->lower_file = NULL; -		} +	rc = ecryptfs_privileged_open(lower_file, lower_dentry, lower_mnt, +				      cred); +	if (rc) { +		printk(KERN_ERR "Error opening lower file " +		       "for lower_dentry [0x%p] and lower_mnt [0x%p]; " +		       "rc = [%d]\n", lower_dentry, lower_mnt, rc); +		(*lower_file) = NULL;  	}  	return rc;  } -static struct inode *ecryptfs_get_inode(struct inode *lower_inode, -		       struct super_block *sb) +int ecryptfs_get_lower_file(struct dentry *dentry, struct inode *inode)  { -	struct inode *inode; -	int rc = 0; +	struct ecryptfs_inode_info *inode_info; +	int count, rc = 0; -	if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) { -		rc = -EXDEV; -		goto out; -	} -	if (!igrab(lower_inode)) { -		rc = -ESTALE; -		goto out; -	} -	inode = iget5_locked(sb, (unsigned long)lower_inode, -			     ecryptfs_inode_test, ecryptfs_inode_set, -			     lower_inode); -	if (!inode) { -		rc = -EACCES; -		iput(lower_inode); -		goto out; +	inode_info = ecryptfs_inode_to_private(inode); +	mutex_lock(&inode_info->lower_file_mutex); +	count = atomic_inc_return(&inode_info->lower_file_count); +	if (WARN_ON_ONCE(count < 1)) +		rc = -EINVAL; +	else if (count == 1) { +		rc = ecryptfs_init_lower_file(dentry, +					      &inode_info->lower_file); +		if (rc) +			atomic_set(&inode_info->lower_file_count, 0);  	} -	if (inode->i_state & I_NEW) -		unlock_new_inode(inode); -	else -		iput(lower_inode); -	if (S_ISLNK(lower_inode->i_mode)) -		inode->i_op = &ecryptfs_symlink_iops; -	else if (S_ISDIR(lower_inode->i_mode)) -		inode->i_op = &ecryptfs_dir_iops; -	if (S_ISDIR(lower_inode->i_mode)) -		inode->i_fop = &ecryptfs_dir_fops; -	if (special_file(lower_inode->i_mode)) -		init_special_inode(inode, lower_inode->i_mode, -				   lower_inode->i_rdev); -	fsstack_copy_attr_all(inode, lower_inode); -	/* This size will be overwritten for real files w/ headers and -	 * other metadata */ -	fsstack_copy_inode_size(inode, lower_inode); -	return inode; -out: -	return ERR_PTR(rc); +	mutex_unlock(&inode_info->lower_file_mutex); +	return rc;  } -/** - * ecryptfs_interpose - * @lower_dentry: Existing dentry in the lower filesystem - * @dentry: ecryptfs' dentry - * @sb: ecryptfs's super_block - * @flags: flags to govern behavior of interpose procedure - * - * Interposes upper and lower dentries. - * - * Returns zero on success; non-zero otherwise - */ -int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry, -		       struct super_block *sb, u32 flags) +void ecryptfs_put_lower_file(struct inode *inode)  { -	struct inode *lower_inode = lower_dentry->d_inode; -	struct inode *inode = ecryptfs_get_inode(lower_inode, sb); -	if (IS_ERR(inode)) -		return PTR_ERR(inode); -	if (flags & ECRYPTFS_INTERPOSE_FLAG_D_ADD) -		d_add(dentry, inode); -	else -		d_instantiate(dentry, inode); -	return 0; +	struct ecryptfs_inode_info *inode_info; + +	inode_info = ecryptfs_inode_to_private(inode); +	if (atomic_dec_and_mutex_lock(&inode_info->lower_file_count, +				      &inode_info->lower_file_mutex)) { +		fput(inode_info->lower_file); +		inode_info->lower_file = NULL; +		mutex_unlock(&inode_info->lower_file_mutex); +	}  }  enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, @@ -676,13 +635,8 @@ static struct ecryptfs_cache_info {  		.size = sizeof(struct ecryptfs_sb_info),  	},  	{ -		.cache = &ecryptfs_header_cache_1, -		.name = "ecryptfs_headers_1", -		.size = PAGE_CACHE_SIZE, -	}, -	{ -		.cache = &ecryptfs_header_cache_2, -		.name = "ecryptfs_headers_2", +		.cache = &ecryptfs_header_cache, +		.name = "ecryptfs_headers",  		.size = PAGE_CACHE_SIZE,  	},  	{  | 
