eCryptfs: Ensure i_size is set in ecryptfs_getattr()
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>
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index ab35b11..6900c1d 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -299,11 +299,11 @@
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 @@
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(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;