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> --- Changes since v1 (https://www.spinics.net/lists/ecryptfs/msg01397.html): - In ecryptfs_setattr() ecryptfs_read_or_initialize_metadata() can't be called directly as &crypt_stat->cs_mutex would be locked, but in ecryptfs_settattr() that mutex is already locked. Create a locked/unlocked version of ecryptfs_read_or_initialize_metadata() and use the latter one in ecryptfs_setattr(). fs/ecryptfs/crypto.c | 61 +++++++++++++++++++++++++++++++++-- fs/ecryptfs/ecryptfs_kernel.h | 3 +- fs/ecryptfs/file.c | 44 +------------------------ fs/ecryptfs/inode.c | 2 +- 4 files changed, 63 insertions(+), 47 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index e3f5d7f3c8a0a..a14d3ef40259f 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1377,7 +1377,7 @@ int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry, * * Returns zero if valid headers found and parsed; non-zero otherwise */ -int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) +static int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) { int rc; char *page_virt; @@ -1443,7 +1443,64 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) return rc; } -/* +int ecryptfs_read_or_initialize_metadata_locked(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; + + 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: + return rc; +} + +int ecryptfs_read_or_initialize_metadata(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + struct ecryptfs_crypt_stat *crypt_stat; + int rc; + + crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; + + mutex_lock(&crypt_stat->cs_mutex); + + rc = ecryptfs_read_or_initialize_metadata_locked(dentry); + + mutex_unlock(&crypt_stat->cs_mutex); + + return rc; +} + +/** * ecryptfs_encrypt_filename - encrypt filename * * CBC-encrypts the filename. We do not want to encrypt the same diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 5f2b49e13731a..af71d6f7da91d 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -573,7 +573,8 @@ int ecryptfs_encrypt_page(struct page *page); 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_read_or_initialize_metadata_locked(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 18d5b91cb573e..4721aba376784 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 16d50dface59a..bdeeb89d1dc95 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -916,7 +916,7 @@ static int ecryptfs_setattr(struct user_namespace *mnt_userns, mutex_unlock(&crypt_stat->cs_mutex); goto out; } - rc = ecryptfs_read_metadata(dentry); + rc = ecryptfs_read_or_initialize_metadata_locked(dentry); ecryptfs_put_lower_file(inode); if (rc) { if (!(mount_crypt_stat->flags -- 2.30.2