aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTyler Hicks <tyhicks@canonical.com>2012-01-12 12:24:39 +0100
committerTyler Hicks <tyhicks@canonical.com>2012-03-12 11:39:35 -0500
commit4b08d9cf379be83641b8d7e0eea56e14a8e20cc3 (patch)
tree3ecf254a48eca0136cb6dad09486c4fa8c3ee9c4
parentfde7d9049e55ab85a390be7f415d74c9f62dd0f9 (diff)
downloadecryptfs-lp/842647.tar.gz
eCryptfs: Ensure i_size is set in ecryptfs_getattr()lp/842647
If the eCryptfs inode size was not set in the ecryptfs_lookup() path, we need to force another read in ecryptfs_getattr() so that userspace gets a valid st_size. This could happen if stat was called on an eCryptfs inode that had not yet been initialized. If the read of the eCryptfs metadata during ecryptfs_lookup() was interrupted, it would result in an inode with an i_size equal to that of the lower file (the ECRYPTFS_I_SIZE_INITIALIZED flag would also not be set). ecryptfs_getattr() would then fill the kstat using the bad i_size. If the file was then opened, a full parsing of the metadata occurred and i_size would be properly set. But the amount of readable data would not match what was reported earlier in the flawed stat. The fix is to check for the ECRYPTFS_I_SIZE_INITIALIZED flag and if it is not set, make sure that the lower file is an eCryptfs-encrypted file and then read the decrypted i_size from the metdata. ecryptfs_i_size_read() was changed to return errors if there was a problem reading the decrypted i_size from the metadata. It previously ignored those errors because ecryptfs_lookup_interpose() was the only caller and it needed to ignore the errors so that non-eCryptfs files in the lower filesystem could be looked up. https://launchpad.net/bugs/842647 Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
-rw-r--r--fs/ecryptfs/inode.c34
1 files changed, 23 insertions, 11 deletions
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index ab35b113003b9..6900c1d5e1fce 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -299,11 +299,11 @@ static int ecryptfs_i_size_read(struct dentry *dentry, struct inode *inode)
ecryptfs_put_lower_file(inode);
if (rc) {
rc = ecryptfs_read_and_validate_xattr_region(dentry, inode);
- if (!rc)
- crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
+ if (rc)
+ return -EINVAL;
+ crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
}
- /* Must return 0 to allow non-eCryptfs files to be looked up, too */
return 0;
}
@@ -349,11 +349,8 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry,
return PTR_ERR(inode);
}
if (S_ISREG(inode->i_mode)) {
- rc = ecryptfs_i_size_read(dentry, inode);
- if (rc) {
- make_bad_inode(inode);
- return rc;
- }
+ /* Errors ignored so non-eCryptfs files can be looked up, too */
+ ecryptfs_i_size_read(dentry, inode);
}
if (inode->i_state & I_NEW)
@@ -1057,15 +1054,30 @@ int ecryptfs_getattr_link(struct vfsmount *mnt, struct dentry *dentry,
int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
+ struct inode *inode = dentry->d_inode;
struct kstat lower_stat;
int rc;
+ if (S_ISREG(inode->i_mode)) {
+ struct ecryptfs_crypt_stat *crypt_stat;
+
+ crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
+ mutex_lock(&crypt_stat->cs_mutex);
+ if (!(crypt_stat->flags & ECRYPTFS_I_SIZE_INITIALIZED)) {
+ rc = ecryptfs_i_size_read(dentry, inode);
+ if (rc) {
+ mutex_unlock(&crypt_stat->cs_mutex);
+ return rc;
+ }
+ }
+ mutex_unlock(&crypt_stat->cs_mutex);
+ }
+
rc = vfs_getattr(ecryptfs_dentry_to_lower_mnt(dentry),
ecryptfs_dentry_to_lower(dentry), &lower_stat);
if (!rc) {
- fsstack_copy_attr_all(dentry->d_inode,
- ecryptfs_inode_to_lower(dentry->d_inode));
- generic_fillattr(dentry->d_inode, stat);
+ fsstack_copy_attr_all(inode, ecryptfs_inode_to_lower(inode));
+ generic_fillattr(inode, stat);
stat->blocks = lower_stat.blocks;
}
return rc;