From 3aeb86ea4cd15f728147a3bd5469a205ada8c767 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 15 Mar 2011 14:54:00 -0500 Subject: 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: Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'fs/ecryptfs/crypto.c') diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index d2a70a4561f9..b8d5c8091024 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -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; } +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 * @page_virt: The virtual address into which to read the headers @@ -1482,6 +1501,8 @@ static int ecryptfs_read_headers_virt(char *page_virt, rc = -EINVAL; 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; rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset), &bytes_read); -- cgit v1.2.3 From 3b06b3ebf44170c90c893c6c80916db6e922b9f2 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 May 2011 03:49:02 -0500 Subject: eCryptfs: Fix new inode race condition Only unlock and d_add() new inodes after the plaintext inode size has been read from the lower filesystem. This fixes a race condition that was sometimes seen during a multi-job kernel build in an eCryptfs mount. https://bugzilla.kernel.org/show_bug.cgi?id=36002 Signed-off-by: Tyler Hicks Reported-by: David Tested-by: David --- fs/ecryptfs/crypto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/ecryptfs/crypto.c') diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index b8d5c8091024..f48c4987a15c 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1568,11 +1568,11 @@ out: } int ecryptfs_read_and_validate_xattr_region(char *page_virt, - struct dentry *ecryptfs_dentry) + struct inode *inode) { int rc; - rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_dentry->d_inode); + rc = ecryptfs_read_xattr_region(page_virt, inode); if (rc) goto out; if (!contains_ecryptfs_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES)) { -- cgit v1.2.3 From 7a86617e553f47761b10f57de472d7262562b7de Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Mon, 2 May 2011 00:39:54 -0500 Subject: eCryptfs: Return useful code from contains_ecryptfs_marker Instead of having the calling functions translate the true/false return code to either 0 or -EINVAL, have contains_ecryptfs_marker() return 0 or -EINVAL so that the calling functions can just reuse the return code. Also, rename the function to ecryptfs_validate_marker() to avoid callers mistakenly thinking that it returns true/false codes. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'fs/ecryptfs/crypto.c') diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index f48c4987a15c..162f9baf9eb5 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1024,25 +1024,25 @@ out: } /** - * contains_ecryptfs_marker - check for the ecryptfs marker + * ecryptfs_validate_marker - check for the ecryptfs marker * @data: The data block in which to check * - * Returns one if marker found; zero if not found + * Returns zero if marker found; -EINVAL if not found */ -static int contains_ecryptfs_marker(char *data) +static int ecryptfs_validate_marker(char *data) { u32 m_1, m_2; m_1 = get_unaligned_be32(data); m_2 = get_unaligned_be32(data + 4); if ((m_1 ^ MAGIC_ECRYPTFS_MARKER) == m_2) - return 1; + return 0; ecryptfs_printk(KERN_DEBUG, "m_1 = [0x%.8x]; m_2 = [0x%.8x]; " "MAGIC_ECRYPTFS_MARKER = [0x%.8x]\n", m_1, m_2, MAGIC_ECRYPTFS_MARKER); ecryptfs_printk(KERN_DEBUG, "(m_1 ^ MAGIC_ECRYPTFS_MARKER) = " "[0x%.8x]\n", (m_1 ^ MAGIC_ECRYPTFS_MARKER)); - return 0; + return -EINVAL; } struct ecryptfs_flag_map_elem { @@ -1217,10 +1217,7 @@ int ecryptfs_read_and_validate_header_region(char *data, __func__, rc); goto out; } - if (!contains_ecryptfs_marker(data + ECRYPTFS_FILE_SIZE_BYTES)) { - rc = -EINVAL; - } else - rc = 0; + rc = ecryptfs_validate_marker(data + ECRYPTFS_FILE_SIZE_BYTES); out: return rc; } @@ -1496,11 +1493,9 @@ static int ecryptfs_read_headers_virt(char *page_virt, crypt_stat->mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; offset = ECRYPTFS_FILE_SIZE_BYTES; - rc = contains_ecryptfs_marker(page_virt + offset); - if (rc == 0) { - rc = -EINVAL; + rc = ecryptfs_validate_marker(page_virt + offset); + if (rc) 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; @@ -1575,11 +1570,10 @@ int ecryptfs_read_and_validate_xattr_region(char *page_virt, rc = ecryptfs_read_xattr_region(page_virt, inode); if (rc) goto out; - if (!contains_ecryptfs_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES)) { + rc = ecryptfs_validate_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES); + if (rc) printk(KERN_WARNING "Valid data found in [%s] xattr, but " "the marker is invalid\n", ECRYPTFS_XATTR_NAME); - rc = -EINVAL; - } out: return rc; } -- cgit v1.2.3 From 778aeb42a708d2a57e491d2cbb5a1e74f61270b9 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 May 2011 04:56:23 -0500 Subject: eCryptfs: Cleanup and optimize ecryptfs_lookup_interpose() ecryptfs_lookup_interpose() has turned into spaghetti code over the years. This is an effort to clean it up. - Shorten overly descriptive variable names such as ecryptfs_dentry - Simplify gotos and error paths - Create helper function for reading plaintext i_size from metadata It also includes an optimization when reading i_size from the metadata. A complete page-sized kmem_cache_alloc() was being done to read in 16 bytes of metadata. The buffer for that is now statically declared. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'fs/ecryptfs/crypto.c') diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 162f9baf9eb5..66d8e6748a46 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1201,24 +1201,19 @@ int ecryptfs_cipher_code_to_string(char *str, u8 cipher_code) return rc; } -int ecryptfs_read_and_validate_header_region(char *data, - struct inode *ecryptfs_inode) +int ecryptfs_read_and_validate_header_region(struct inode *inode) { - struct ecryptfs_crypt_stat *crypt_stat = - &(ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat); + u8 file_size[ECRYPTFS_SIZE_AND_MARKER_BYTES]; + u8 *marker = file_size + ECRYPTFS_FILE_SIZE_BYTES; int rc; - if (crypt_stat->extent_size == 0) - crypt_stat->extent_size = ECRYPTFS_DEFAULT_EXTENT_SIZE; - rc = ecryptfs_read_lower(data, 0, crypt_stat->extent_size, - ecryptfs_inode); - if (rc < 0) { - printk(KERN_ERR "%s: Error reading header region; rc = [%d]\n", - __func__, rc); - goto out; - } - rc = ecryptfs_validate_marker(data + ECRYPTFS_FILE_SIZE_BYTES); -out: + rc = ecryptfs_read_lower(file_size, 0, ECRYPTFS_SIZE_AND_MARKER_BYTES, + inode); + if (rc < ECRYPTFS_SIZE_AND_MARKER_BYTES) + return rc >= 0 ? -EINVAL : rc; + rc = ecryptfs_validate_marker(marker); + if (!rc) + ecryptfs_i_size_init(file_size, inode); return rc; } @@ -1562,19 +1557,21 @@ out: return rc; } -int ecryptfs_read_and_validate_xattr_region(char *page_virt, +int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry, struct inode *inode) { + u8 file_size[ECRYPTFS_SIZE_AND_MARKER_BYTES]; + u8 *marker = file_size + ECRYPTFS_FILE_SIZE_BYTES; int rc; - rc = ecryptfs_read_xattr_region(page_virt, inode); - if (rc) - goto out; - rc = ecryptfs_validate_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES); - if (rc) - printk(KERN_WARNING "Valid data found in [%s] xattr, but " - "the marker is invalid\n", ECRYPTFS_XATTR_NAME); -out: + rc = ecryptfs_getxattr_lower(ecryptfs_dentry_to_lower(dentry), + ECRYPTFS_XATTR_NAME, file_size, + ECRYPTFS_SIZE_AND_MARKER_BYTES); + if (rc < ECRYPTFS_SIZE_AND_MARKER_BYTES) + return rc >= 0 ? -EINVAL : rc; + rc = ecryptfs_validate_marker(marker); + if (!rc) + ecryptfs_i_size_init(file_size, inode); return rc; } -- cgit v1.2.3 From 3063287053bca5207e121c567b95b2b6f0bdc2c8 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 May 2011 05:11:12 -0500 Subject: eCryptfs: Remove ecryptfs_header_cache_2 Now that ecryptfs_lookup_interpose() is no longer using ecryptfs_header_cache_2 to read in metadata, the kmem_cache can be removed and the ecryptfs_header_cache_1 kmem_cache can be renamed to ecryptfs_header_cache. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs/ecryptfs/crypto.c') diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 66d8e6748a46..58609bde3b9f 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1234,8 +1234,7 @@ ecryptfs_write_header_metadata(char *virt, (*written) = 6; } -struct kmem_cache *ecryptfs_header_cache_1; -struct kmem_cache *ecryptfs_header_cache_2; +struct kmem_cache *ecryptfs_header_cache; /** * ecryptfs_write_headers_virt @@ -1601,7 +1600,7 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat, mount_crypt_stat); /* Read the first page from the underlying file */ - page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, GFP_USER); + page_virt = kmem_cache_alloc(ecryptfs_header_cache, GFP_USER); if (!page_virt) { rc = -ENOMEM; printk(KERN_ERR "%s: Unable to allocate page_virt\n", @@ -1646,7 +1645,7 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) out: if (page_virt) { memset(page_virt, 0, PAGE_CACHE_SIZE); - kmem_cache_free(ecryptfs_header_cache_1, page_virt); + kmem_cache_free(ecryptfs_header_cache, page_virt); } return rc; } -- cgit v1.2.3