diff options
author | Tyler Hicks <tyhicks@canonical.com> | 2012-01-12 12:24:39 +0100 |
---|---|---|
committer | Tyler Hicks <tyhicks@canonical.com> | 2012-03-12 11:39:35 -0500 |
commit | 4b08d9cf379be83641b8d7e0eea56e14a8e20cc3 (patch) | |
tree | 3ecf254a48eca0136cb6dad09486c4fa8c3ee9c4 | |
parent | fde7d9049e55ab85a390be7f415d74c9f62dd0f9 (diff) | |
download | ecryptfs-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.c | 34 |
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; |