Depending on the synchronization state of the lower filesystem during a power cut it can happen that a lower file is empty after that power cut. An inode_operations::setattr operation fails with -EIO on such files, because ecryptfs_read_metadata() fails. In e3ccaa976120 ("eCryptfs: Initialize empty lower files when opening them") a similar problem was solved in the open() path: | To transparently solve this problem, this patch initializes empty lower | files in the ecryptfs_open() error path. If the metadata is unreadable | due to the lower inode size being 0, plaintext passthrough support is | not in use, and the metadata is stored in the header of the file (as | opposed to the user.ecryptfs extended attribute), the lower file will be | initialized. Do the same in inode_operations::setattr to allow setattr on empty lower files. Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> --- fs/ecryptfs/crypto.c | 42 +++++++++++++++++++++++++++++++++ fs/ecryptfs/ecryptfs_kernel.h | 1 + fs/ecryptfs/file.c | 44 +---------------------------------- fs/ecryptfs/inode.c | 2 +- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 0681540c48d9..a48116aae02c 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1448,6 +1448,48 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) return rc; } +int ecryptfs_read_or_initialize_metadata(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + struct ecryptfs_mount_crypt_stat *mount_crypt_stat; + struct ecryptfs_crypt_stat *crypt_stat; + int rc; + + crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; + mount_crypt_stat = &ecryptfs_superblock_to_private( + inode->i_sb)->mount_crypt_stat; + mutex_lock(&crypt_stat->cs_mutex); + + if (crypt_stat->flags & ECRYPTFS_POLICY_APPLIED && + crypt_stat->flags & ECRYPTFS_KEY_VALID) { + rc = 0; + goto out; + } + + rc = ecryptfs_read_metadata(dentry); + if (!rc) + goto out; + + if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) { + crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED + | ECRYPTFS_ENCRYPTED); + rc = 0; + goto out; + } + + if (!(mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) && + !i_size_read(ecryptfs_inode_to_lower(inode))) { + rc = ecryptfs_initialize_file(dentry, inode); + if (!rc) + goto out; + } + + rc = -EIO; +out: + mutex_unlock(&crypt_stat->cs_mutex); + return rc; +} + /** * ecryptfs_encrypt_filename - encrypt filename * diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index e6ac78c62ca4..3815b3cc4c33 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -589,6 +589,7 @@ int ecryptfs_decrypt_page(struct page *page); int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry, struct inode *ecryptfs_inode); int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry); +int ecryptfs_read_or_initialize_metadata(struct dentry *dentry); int ecryptfs_new_file_context(struct inode *ecryptfs_inode); void ecryptfs_write_crypt_stat_flags(char *page_virt, struct ecryptfs_crypt_stat *crypt_stat, diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 5fb45d865ce5..14157b312f06 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -124,48 +124,6 @@ static int ecryptfs_readdir(struct file *file, struct dir_context *ctx) struct kmem_cache *ecryptfs_file_info_cache; -static int read_or_initialize_metadata(struct dentry *dentry) -{ - struct inode *inode = d_inode(dentry); - struct ecryptfs_mount_crypt_stat *mount_crypt_stat; - struct ecryptfs_crypt_stat *crypt_stat; - int rc; - - crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; - mount_crypt_stat = &ecryptfs_superblock_to_private( - inode->i_sb)->mount_crypt_stat; - mutex_lock(&crypt_stat->cs_mutex); - - if (crypt_stat->flags & ECRYPTFS_POLICY_APPLIED && - crypt_stat->flags & ECRYPTFS_KEY_VALID) { - rc = 0; - goto out; - } - - rc = ecryptfs_read_metadata(dentry); - if (!rc) - goto out; - - if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) { - crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED - | ECRYPTFS_ENCRYPTED); - rc = 0; - goto out; - } - - if (!(mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) && - !i_size_read(ecryptfs_inode_to_lower(inode))) { - rc = ecryptfs_initialize_file(dentry, inode); - if (!rc) - goto out; - } - - rc = -EIO; -out: - mutex_unlock(&crypt_stat->cs_mutex); - return rc; -} - static int ecryptfs_mmap(struct file *file, struct vm_area_struct *vma) { struct file *lower_file = ecryptfs_file_to_lower(file); @@ -232,7 +190,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file) } ecryptfs_set_file_lower( file, ecryptfs_inode_to_private(inode)->lower_file); - rc = read_or_initialize_metadata(ecryptfs_dentry); + rc = ecryptfs_read_or_initialize_metadata(ecryptfs_dentry); if (rc) goto out_put; ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = " diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 58d0f7187997..230fee828059 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -912,7 +912,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) mutex_unlock(&crypt_stat->cs_mutex); goto out; } - rc = ecryptfs_read_metadata(dentry); + rc = ecryptfs_read_or_initialize_metadata(dentry); ecryptfs_put_lower_file(inode); if (rc) { if (!(mount_crypt_stat->flags -- 2.29.2