diff options
Diffstat (limited to 'fs/ecryptfs')
| -rw-r--r-- | fs/ecryptfs/ecryptfs_kernel.h | 5 | ||||
| -rw-r--r-- | fs/ecryptfs/file.c | 22 | ||||
| -rw-r--r-- | fs/ecryptfs/inode.c | 30 | ||||
| -rw-r--r-- | fs/ecryptfs/kthread.c | 6 | ||||
| -rw-r--r-- | fs/ecryptfs/main.c | 72 | ||||
| -rw-r--r-- | fs/ecryptfs/super.c | 16 | 
6 files changed, 92 insertions, 59 deletions
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index bd3cafd0949..380bee1094c 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -295,6 +295,8 @@ struct ecryptfs_crypt_stat {  struct ecryptfs_inode_info {  	struct inode vfs_inode;  	struct inode *wii_inode; +	struct mutex lower_file_mutex; +	atomic_t lower_file_count;  	struct file *lower_file;  	struct ecryptfs_crypt_stat crypt_stat;  }; @@ -757,7 +759,8 @@ int ecryptfs_privileged_open(struct file **lower_file,  			     struct dentry *lower_dentry,  			     struct vfsmount *lower_mnt,  			     const struct cred *cred); -int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry); +int ecryptfs_get_lower_file(struct dentry *ecryptfs_dentry); +void ecryptfs_put_lower_file(struct inode *inode);  int  ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,  			     size_t *packet_size, diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index cedc913d11b..146c4edff70 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -191,10 +191,10 @@ static int ecryptfs_open(struct inode *inode, struct file *file)  				      | ECRYPTFS_ENCRYPTED);  	}  	mutex_unlock(&crypt_stat->cs_mutex); -	rc = ecryptfs_init_persistent_file(ecryptfs_dentry); +	rc = ecryptfs_get_lower_file(ecryptfs_dentry);  	if (rc) {  		printk(KERN_ERR "%s: Error attempting to initialize " -			"the persistent file for the dentry with name " +			"the lower file for the dentry with name "  			"[%s]; rc = [%d]\n", __func__,  			ecryptfs_dentry->d_name.name, rc);  		goto out_free; @@ -202,9 +202,9 @@ static int ecryptfs_open(struct inode *inode, struct file *file)  	if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_ACCMODE)  	    == O_RDONLY && (file->f_flags & O_ACCMODE) != O_RDONLY) {  		rc = -EPERM; -		printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs " +		printk(KERN_WARNING "%s: Lower file is RO; eCryptfs "  		       "file must hence be opened RO\n", __func__); -		goto out_free; +		goto out_put;  	}  	ecryptfs_set_file_lower(  		file, ecryptfs_inode_to_private(inode)->lower_file); @@ -232,7 +232,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file)  				       "Plaintext passthrough mode is not "  				       "enabled; returning -EIO\n");  				mutex_unlock(&crypt_stat->cs_mutex); -				goto out_free; +				goto out_put;  			}  			rc = 0;  			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); @@ -245,6 +245,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)  			"[0x%.16lx] size: [0x%.16llx]\n", inode, inode->i_ino,  			(unsigned long long)i_size_read(inode));  	goto out; +out_put: +	ecryptfs_put_lower_file(inode);  out_free:  	kmem_cache_free(ecryptfs_file_info_cache,  			ecryptfs_file_to_private(file)); @@ -254,17 +256,13 @@ out:  static int ecryptfs_flush(struct file *file, fl_owner_t td)  { -	int rc = 0; -	struct file *lower_file = NULL; - -	lower_file = ecryptfs_file_to_lower(file); -	if (lower_file->f_op && lower_file->f_op->flush) -		rc = lower_file->f_op->flush(lower_file, td); -	return rc; +	return file->f_mode & FMODE_WRITE +	       ? filemap_write_and_wait(file->f_mapping) : 0;  }  static int ecryptfs_release(struct inode *inode, struct file *file)  { +	ecryptfs_put_lower_file(inode);  	kmem_cache_free(ecryptfs_file_info_cache,  			ecryptfs_file_to_private(file));  	return 0; diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 72d35764959..f6b388638c3 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -168,19 +168,18 @@ static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry)  				"context; rc = [%d]\n", rc);  		goto out;  	} -	rc = ecryptfs_init_persistent_file(ecryptfs_dentry); +	rc = ecryptfs_get_lower_file(ecryptfs_dentry);  	if (rc) {  		printk(KERN_ERR "%s: Error attempting to initialize " -			"the persistent file for the dentry with name " +			"the lower file for the dentry with name "  			"[%s]; rc = [%d]\n", __func__,  			ecryptfs_dentry->d_name.name, rc);  		goto out;  	}  	rc = ecryptfs_write_metadata(ecryptfs_dentry); -	if (rc) { +	if (rc)  		printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc); -		goto out; -	} +	ecryptfs_put_lower_file(ecryptfs_dentry->d_inode);  out:  	return rc;  } @@ -230,7 +229,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,  	struct ecryptfs_crypt_stat *crypt_stat;  	char *page_virt = NULL;  	u64 file_size; -	int rc = 0; +	int put_lower = 0, rc = 0;  	lower_dir_dentry = lower_dentry->d_parent;  	lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( @@ -277,14 +276,15 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,  		rc = -ENOMEM;  		goto out;  	} -	rc = ecryptfs_init_persistent_file(ecryptfs_dentry); +	rc = ecryptfs_get_lower_file(ecryptfs_dentry);  	if (rc) {  		printk(KERN_ERR "%s: Error attempting to initialize " -			"the persistent file for the dentry with name " +			"the lower file for the dentry with name "  			"[%s]; rc = [%d]\n", __func__,  			ecryptfs_dentry->d_name.name, rc);  		goto out_free_kmem;  	} +	put_lower = 1;  	crypt_stat = &ecryptfs_inode_to_private(  					ecryptfs_dentry->d_inode)->crypt_stat;  	/* TODO: lock for crypt_stat comparison */ @@ -322,6 +322,8 @@ out_put:  	mntput(lower_mnt);  	d_drop(ecryptfs_dentry);  out: +	if (put_lower) +		ecryptfs_put_lower_file(ecryptfs_dentry->d_inode);  	return rc;  } @@ -757,8 +759,11 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,  	if (unlikely((ia->ia_size == i_size))) {  		lower_ia->ia_valid &= ~ATTR_SIZE; -		goto out; +		return 0;  	} +	rc = ecryptfs_get_lower_file(dentry); +	if (rc) +		return rc;  	crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;  	/* Switch on growing or shrinking file */  	if (ia->ia_size > i_size) { @@ -836,6 +841,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,  			lower_ia->ia_valid &= ~ATTR_SIZE;  	}  out: +	ecryptfs_put_lower_file(inode);  	return rc;  } @@ -911,7 +917,13 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)  		mount_crypt_stat = &ecryptfs_superblock_to_private(  			dentry->d_sb)->mount_crypt_stat; +		rc = ecryptfs_get_lower_file(dentry); +		if (rc) { +			mutex_unlock(&crypt_stat->cs_mutex); +			goto out; +		}  		rc = ecryptfs_read_metadata(dentry); +		ecryptfs_put_lower_file(inode);  		if (rc) {  			if (!(mount_crypt_stat->flags  			      & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c index 0851ab6980f..69f994a7d52 100644 --- a/fs/ecryptfs/kthread.c +++ b/fs/ecryptfs/kthread.c @@ -44,7 +44,7 @@ static struct task_struct *ecryptfs_kthread;   * @ignored: ignored   *   * The eCryptfs kernel thread that has the responsibility of getting - * the lower persistent file with RW permissions. + * the lower file with RW permissions.   *   * Returns zero on success; non-zero otherwise   */ @@ -141,8 +141,8 @@ int ecryptfs_privileged_open(struct file **lower_file,  	int rc = 0;  	/* Corresponding dput() and mntput() are done when the -	 * persistent file is fput() when the eCryptfs inode is -	 * destroyed. */ +	 * lower file is fput() when all eCryptfs files for the inode are +	 * released. */  	dget(lower_dentry);  	mntget(lower_mnt);  	flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR; diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index fdb2eb0ad09..89b93389af8 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,42 +104,70 @@ 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; +	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); +	struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); +	int rc; -	if (!inode_info->lower_file) { -		struct dentry *lower_dentry; -		struct vfsmount *lower_mnt = -			ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); +	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; +} -		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; -		} +int ecryptfs_get_lower_file(struct dentry *dentry) +{ +	struct ecryptfs_inode_info *inode_info = +		ecryptfs_inode_to_private(dentry->d_inode); +	int count, rc = 0; + +	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);  	} +	mutex_unlock(&inode_info->lower_file_mutex);  	return rc;  } +void ecryptfs_put_lower_file(struct inode *inode) +{ +	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); +	} +} +  static struct inode *ecryptfs_get_inode(struct inode *lower_inode,  		       struct super_block *sb)  { diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c index bacc882e1ae..245b517bf1b 100644 --- a/fs/ecryptfs/super.c +++ b/fs/ecryptfs/super.c @@ -55,6 +55,8 @@ static struct inode *ecryptfs_alloc_inode(struct super_block *sb)  	if (unlikely(!inode_info))  		goto out;  	ecryptfs_init_crypt_stat(&inode_info->crypt_stat); +	mutex_init(&inode_info->lower_file_mutex); +	atomic_set(&inode_info->lower_file_count, 0);  	inode_info->lower_file = NULL;  	inode = &inode_info->vfs_inode;  out: @@ -77,8 +79,7 @@ static void ecryptfs_i_callback(struct rcu_head *head)   *   * This is used during the final destruction of the inode.  All   * allocation of memory related to the inode, including allocated - * memory in the crypt_stat struct, will be released here. This - * function also fput()'s the persistent file for the lower inode. + * memory in the crypt_stat struct, will be released here.   * There should be no chance that this deallocation will be missed.   */  static void ecryptfs_destroy_inode(struct inode *inode) @@ -86,16 +87,7 @@ static void ecryptfs_destroy_inode(struct inode *inode)  	struct ecryptfs_inode_info *inode_info;  	inode_info = ecryptfs_inode_to_private(inode); -	if (inode_info->lower_file) { -		struct dentry *lower_dentry = -			inode_info->lower_file->f_dentry; - -		BUG_ON(!lower_dentry); -		if (lower_dentry->d_inode) { -			fput(inode_info->lower_file); -			inode_info->lower_file = NULL; -		} -	} +	BUG_ON(inode_info->lower_file);  	ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);  	call_rcu(&inode->i_rcu, ecryptfs_i_callback);  }  |