Commit 3aeb86ea authored by Tyler Hicks's avatar Tyler Hicks

eCryptfs: Handle failed metadata read in lookup

When failing to read the lower file's crypto metadata during a lookup,
eCryptfs must continue on without throwing an error. For example, there
may be a plaintext file in the lower mount point that the user wants to
delete through the eCryptfs mount.

If an error is encountered while reading the metadata in lookup(), the
eCryptfs inode's size could be incorrect. We must be sure to reread the
plaintext inode size from the metadata when performing an open() or
setattr(). The metadata is already being read in those paths, so this
adds minimal performance overhead.

This patch introduces a flag which will track whether or not the
plaintext inode size has been read so that an incorrect i_size can be
fixed in the open() or setattr() paths.

https://bugs.launchpad.net/bugs/509180

Cc: <stable@kernel.org>
Signed-off-by: default avatarTyler Hicks <tyhicks@linux.vnet.ibm.com>
parent 332ab16f
...@@ -1452,6 +1452,25 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat) ...@@ -1452,6 +1452,25 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
crypt_stat->metadata_size = ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE; crypt_stat->metadata_size = ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE;
} }
void ecryptfs_i_size_init(const char *page_virt, struct inode *inode)
{
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct ecryptfs_crypt_stat *crypt_stat;
u64 file_size;
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
mount_crypt_stat =
&ecryptfs_superblock_to_private(inode->i_sb)->mount_crypt_stat;
if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) {
file_size = i_size_read(ecryptfs_inode_to_lower(inode));
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
file_size += crypt_stat->metadata_size;
} else
file_size = get_unaligned_be64(page_virt);
i_size_write(inode, (loff_t)file_size);
crypt_stat->flags |= ECRYPTFS_I_SIZE_INITIALIZED;
}
/** /**
* ecryptfs_read_headers_virt * ecryptfs_read_headers_virt
* @page_virt: The virtual address into which to read the headers * @page_virt: The virtual address into which to read the headers
...@@ -1482,6 +1501,8 @@ static int ecryptfs_read_headers_virt(char *page_virt, ...@@ -1482,6 +1501,8 @@ static int ecryptfs_read_headers_virt(char *page_virt,
rc = -EINVAL; rc = -EINVAL;
goto out; goto out;
} }
if (!(crypt_stat->flags & ECRYPTFS_I_SIZE_INITIALIZED))
ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset), rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset),
&bytes_read); &bytes_read);
......
...@@ -269,6 +269,7 @@ struct ecryptfs_crypt_stat { ...@@ -269,6 +269,7 @@ struct ecryptfs_crypt_stat {
#define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00000800 #define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00000800
#define ECRYPTFS_ENCFN_USE_FEK 0x00001000 #define ECRYPTFS_ENCFN_USE_FEK 0x00001000
#define ECRYPTFS_UNLINK_SIGS 0x00002000 #define ECRYPTFS_UNLINK_SIGS 0x00002000
#define ECRYPTFS_I_SIZE_INITIALIZED 0x00004000
u32 flags; u32 flags;
unsigned int file_version; unsigned int file_version;
size_t iv_bytes; size_t iv_bytes;
...@@ -628,6 +629,7 @@ struct ecryptfs_open_req { ...@@ -628,6 +629,7 @@ struct ecryptfs_open_req {
int ecryptfs_interpose(struct dentry *hidden_dentry, int ecryptfs_interpose(struct dentry *hidden_dentry,
struct dentry *this_dentry, struct super_block *sb, struct dentry *this_dentry, struct super_block *sb,
u32 flags); u32 flags);
void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
struct dentry *lower_dentry, struct dentry *lower_dentry,
struct inode *ecryptfs_dir_inode); struct inode *ecryptfs_dir_inode);
......
...@@ -235,7 +235,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file) ...@@ -235,7 +235,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
goto out_put; goto out_put;
} }
rc = 0; rc = 0;
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
| ECRYPTFS_ENCRYPTED);
mutex_unlock(&crypt_stat->cs_mutex); mutex_unlock(&crypt_stat->cs_mutex);
goto out; goto out;
} }
......
...@@ -225,10 +225,8 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, ...@@ -225,10 +225,8 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
struct dentry *lower_dir_dentry; struct dentry *lower_dir_dentry;
struct vfsmount *lower_mnt; struct vfsmount *lower_mnt;
struct inode *lower_inode; struct inode *lower_inode;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct ecryptfs_crypt_stat *crypt_stat; struct ecryptfs_crypt_stat *crypt_stat;
char *page_virt = NULL; char *page_virt = NULL;
u64 file_size;
int put_lower = 0, rc = 0; int put_lower = 0, rc = 0;
lower_dir_dentry = lower_dentry->d_parent; lower_dir_dentry = lower_dentry->d_parent;
...@@ -302,18 +300,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, ...@@ -302,18 +300,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
} }
crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
} }
mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
ecryptfs_dentry->d_sb)->mount_crypt_stat;
if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) {
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
file_size = (crypt_stat->metadata_size
+ i_size_read(lower_dentry->d_inode));
else
file_size = i_size_read(lower_dentry->d_inode);
} else {
file_size = get_unaligned_be64(page_virt);
}
i_size_write(ecryptfs_dentry->d_inode, (loff_t)file_size);
out_free_kmem: out_free_kmem:
kmem_cache_free(ecryptfs_header_cache_2, page_virt); kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out; goto out;
...@@ -937,7 +924,8 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) ...@@ -937,7 +924,8 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
goto out; goto out;
} }
rc = 0; rc = 0;
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
| ECRYPTFS_ENCRYPTED);
} }
} }
mutex_unlock(&crypt_stat->cs_mutex); mutex_unlock(&crypt_stat->cs_mutex);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment