eCryptfs: Initialize empty lower files when opening them
authorTyler Hicks <tyhicks@canonical.com>
Thu, 21 Jun 2012 06:50:59 +0000 (23:50 -0700)
committerTyler Hicks <tyhicks@canonical.com>
Sun, 8 Jul 2012 17:51:45 +0000 (12:51 -0500)
Historically, eCryptfs has only initialized lower files in the
ecryptfs_create() path. Lower file initialization is the act of writing
the cryptographic metadata from the inode's crypt_stat to the header of
the file. The ecryptfs_open() path already expects that metadata to be
in the header of the file.

A number of users have reported empty lower files in beneath their
eCryptfs mounts. Most of the causes for those empty files being left
around have been addressed, but the presence of empty files causes
problems due to the lack of proper cryptographic metadata.

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.

The number of nested conditionals in ecryptfs_open() was getting out of
hand, so a helper function was created. To avoid the same nested
conditional problem, the conditional logic was reversed inside of the
helper function.

https://launchpad.net/bugs/911507

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Cc: John Johansen <john.johansen@canonical.com>
Cc: Colin Ian King <colin.king@canonical.com>
fs/ecryptfs/ecryptfs_kernel.h
fs/ecryptfs/file.c
fs/ecryptfs/inode.c

index 0deb4f24957a39814e41257a5dfaf3e798fb823f..9f77ff818173b4a6f8370c9d404bbeb782871788 100644 (file)
@@ -563,6 +563,8 @@ struct ecryptfs_open_req {
 struct inode *ecryptfs_get_inode(struct inode *lower_inode,
                                 struct super_block *sb);
 void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
+int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
+                            struct inode *ecryptfs_inode);
 int ecryptfs_decode_and_decrypt_filename(char **decrypted_name,
                                         size_t *decrypted_name_size,
                                         struct dentry *ecryptfs_dentry,
index 2b17f2f9b121d0ee89e7d00cb1e93d7f9cf9dca4..baf8b0550391ffb12d214421a09ae433318c589d 100644 (file)
@@ -161,6 +161,48 @@ static int ecryptfs_file_mmap(struct file *file, struct vm_area_struct *vma)
 
 struct kmem_cache *ecryptfs_file_info_cache;
 
+static int read_or_initialize_metadata(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+       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_open
  * @inode: inode speciying file to open
@@ -236,32 +278,9 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
                rc = 0;
                goto out;
        }
-       mutex_lock(&crypt_stat->cs_mutex);
-       if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)
-           || !(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
-               rc = ecryptfs_read_metadata(ecryptfs_dentry);
-               if (rc) {
-                       ecryptfs_printk(KERN_DEBUG,
-                                       "Valid headers not found\n");
-                       if (!(mount_crypt_stat->flags
-                             & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
-                               rc = -EIO;
-                               printk(KERN_WARNING "Either the lower file "
-                                      "is not in a valid eCryptfs format, "
-                                      "or the key could not be retrieved. "
-                                      "Plaintext passthrough mode is not "
-                                      "enabled; returning -EIO\n");
-                               mutex_unlock(&crypt_stat->cs_mutex);
-                               goto out_put;
-                       }
-                       rc = 0;
-                       crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
-                                              | ECRYPTFS_ENCRYPTED);
-                       mutex_unlock(&crypt_stat->cs_mutex);
-                       goto out;
-               }
-       }
-       mutex_unlock(&crypt_stat->cs_mutex);
+       rc = read_or_initialize_metadata(ecryptfs_dentry);
+       if (rc)
+               goto out_put;
        ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = "
                        "[0x%.16lx] size: [0x%.16llx]\n", inode, inode->i_ino,
                        (unsigned long long)i_size_read(inode));
index 65efe5fa687c413dd78d1928dac6c56473e43768..2d4143f8f5c969455e636fda9287155300679c42 100644 (file)
@@ -227,8 +227,8 @@ out:
  *
  * Returns zero on success
  */
-static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
-                                   struct inode *ecryptfs_inode)
+int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
+                            struct inode *ecryptfs_inode)
 {
        struct ecryptfs_crypt_stat *crypt_stat =
                &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;