diff mbox

Lucid SRU: eCryptfs: Handle failed metadata read in lookup

Message ID 20110719235324.77460F8AFC@sepang.rtg.net
State New
Headers show

Pull-request

git://kernel.ubuntu.com/rtg/ubuntu-lucid.git ecryptfs-lp509180

Commit Message

Tim Gardner July 19, 2011, 11:53 p.m. UTC
The following changes since commit b9d52a294afd689b987ab0855a86a871d0a48618:
  Greg Kroah-Hartman (1):
        Linux 2.6.32.43

are available in the git repository at:

  git://kernel.ubuntu.com/rtg/ubuntu-lucid.git ecryptfs-lp509180

Tim Gardner (1):
      eCryptfs: Handle failed metadata read in lookup

 fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
 fs/ecryptfs/ecryptfs_kernel.h |    2 ++
 fs/ecryptfs/file.c            |    3 ++-
 fs/ecryptfs/inode.c           |   18 +++---------------
 4 files changed, 28 insertions(+), 16 deletions(-)

From 2afbc1dca9a944e10434ddbe90b0c94521464475 Mon Sep 17 00:00:00 2001
From: Tim Gardner <tim.gardner@canonical.com>
Date: Tue, 19 Jul 2011 17:44:22 -0600
Subject: [PATCH] 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: Tyler Hicks <tyhicks@linux.vnet.ibm.com>

(backported from 3aeb86ea4cd15f728147a3bd5469a205ada8c767)
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
---
 fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
 fs/ecryptfs/ecryptfs_kernel.h |    2 ++
 fs/ecryptfs/file.c            |    3 ++-
 fs/ecryptfs/inode.c           |   18 +++---------------
 4 files changed, 28 insertions(+), 16 deletions(-)

Comments

Stefan Bader July 20, 2011, 8:55 a.m. UTC | #1
On 20.07.2011 01:53, Tim Gardner wrote:
> The following changes since commit b9d52a294afd689b987ab0855a86a871d0a48618:
>   Greg Kroah-Hartman (1):
>         Linux 2.6.32.43
> 
> are available in the git repository at:
> 
>   git://kernel.ubuntu.com/rtg/ubuntu-lucid.git ecryptfs-lp509180
> 
> Tim Gardner (1):
>       eCryptfs: Handle failed metadata read in lookup
> 
>  fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>  fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>  fs/ecryptfs/file.c            |    3 ++-
>  fs/ecryptfs/inode.c           |   18 +++---------------
>  4 files changed, 28 insertions(+), 16 deletions(-)
> 
> From 2afbc1dca9a944e10434ddbe90b0c94521464475 Mon Sep 17 00:00:00 2001
> From: Tim Gardner <tim.gardner@canonical.com>
> Date: Tue, 19 Jul 2011 17:44:22 -0600
> Subject: [PATCH] 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: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
> 
> (backported from 3aeb86ea4cd15f728147a3bd5469a205ada8c767)
> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
> ---
>  fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>  fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>  fs/ecryptfs/file.c            |    3 ++-
>  fs/ecryptfs/inode.c           |   18 +++---------------
>  4 files changed, 28 insertions(+), 16 deletions(-)
> 
> diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
> index 7cb0a59..e915eb1 100644
> --- a/fs/ecryptfs/crypto.c
> +++ b/fs/ecryptfs/crypto.c
> @@ -1455,6 +1455,25 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
>  		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
> @@ -1485,6 +1504,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);
> diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
> index 542f625..9685315 100644
> --- a/fs/ecryptfs/ecryptfs_kernel.h
> +++ b/fs/ecryptfs/ecryptfs_kernel.h
> @@ -270,6 +270,7 @@ struct ecryptfs_crypt_stat {
>  #define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00001000
>  #define ECRYPTFS_ENCFN_USE_FEK        0x00002000
>  #define ECRYPTFS_UNLINK_SIGS	      0x00004000
> +#define ECRYPTFS_I_SIZE_INITIALIZED   0x00008000
>  	u32 flags;
>  	unsigned int file_version;
>  	size_t iv_bytes;
> @@ -619,6 +620,7 @@ struct ecryptfs_open_req {
>  int ecryptfs_interpose(struct dentry *hidden_dentry,
>  		       struct dentry *this_dentry, struct super_block *sb,
>  		       u32 flags);
> +void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
>  int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  					struct dentry *lower_dentry,
>  					struct inode *ecryptfs_dir_inode,
> diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
> index 4e25328..eb744ed 100644
> --- a/fs/ecryptfs/file.c
> +++ b/fs/ecryptfs/file.c
> @@ -237,7 +237,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
>  				goto out_free;
>  			}
>  			rc = 0;
> -			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>  			mutex_unlock(&crypt_stat->cs_mutex);
>  			goto out;
>  		}
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 88ba4d4..5bab1c5 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -256,10 +256,8 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  	struct dentry *lower_dir_dentry;
>  	struct vfsmount *lower_mnt;
>  	struct inode *lower_inode;
> -	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
>  	struct ecryptfs_crypt_stat *crypt_stat;
>  	char *page_virt = NULL;
> -	u64 file_size;
>  	int rc = 0;
>  
>  	lower_dir_dentry = lower_dentry->d_parent;
> @@ -334,18 +332,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  		}
>  		crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
>  	}
> -	mount_crypt_stat = &ecryptfs_superblock_to_private(
> -		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->num_header_bytes_at_front
> -				     + 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);
> +	ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
>  out_free_kmem:
>  	kmem_cache_free(ecryptfs_header_cache_2, page_virt);
>  	goto out;
> @@ -925,7 +912,8 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
>  				goto out;
>  			}
>  			rc = 0;
> -			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>  		}
>  	}
>  	mutex_unlock(&crypt_stat->cs_mutex);

Seems to conform to the upstream change (beside of the flag needing a different
bit). Seems this was actually tagged as stable. So was the required modification
the reason it did not go upstream stable? If so, maybe the backport should get
proposed there?

Acked-by: Stefan Bader <stefan.bader@canonical.com>
Stefan Bader July 20, 2011, 9:03 a.m. UTC | #2
On 20.07.2011 01:53, Tim Gardner wrote:
> The following changes since commit b9d52a294afd689b987ab0855a86a871d0a48618:
>   Greg Kroah-Hartman (1):
>         Linux 2.6.32.43
> 
> are available in the git repository at:
> 
>   git://kernel.ubuntu.com/rtg/ubuntu-lucid.git ecryptfs-lp509180
> 
> Tim Gardner (1):
>       eCryptfs: Handle failed metadata read in lookup
> 
>  fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>  fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>  fs/ecryptfs/file.c            |    3 ++-
>  fs/ecryptfs/inode.c           |   18 +++---------------
>  4 files changed, 28 insertions(+), 16 deletions(-)
> 
> From 2afbc1dca9a944e10434ddbe90b0c94521464475 Mon Sep 17 00:00:00 2001
> From: Tim Gardner <tim.gardner@canonical.com>
> Date: Tue, 19 Jul 2011 17:44:22 -0600
> Subject: [PATCH] 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

Same here, we may want BugLink

> 
> Cc: <stable@kernel.org>
> Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
> 
> (backported from 3aeb86ea4cd15f728147a3bd5469a205ada8c767)
> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
> ---
>  fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>  fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>  fs/ecryptfs/file.c            |    3 ++-
>  fs/ecryptfs/inode.c           |   18 +++---------------
>  4 files changed, 28 insertions(+), 16 deletions(-)
> 
> diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
> index 7cb0a59..e915eb1 100644
> --- a/fs/ecryptfs/crypto.c
> +++ b/fs/ecryptfs/crypto.c
> @@ -1455,6 +1455,25 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
>  		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
> @@ -1485,6 +1504,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);
> diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
> index 542f625..9685315 100644
> --- a/fs/ecryptfs/ecryptfs_kernel.h
> +++ b/fs/ecryptfs/ecryptfs_kernel.h
> @@ -270,6 +270,7 @@ struct ecryptfs_crypt_stat {
>  #define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00001000
>  #define ECRYPTFS_ENCFN_USE_FEK        0x00002000
>  #define ECRYPTFS_UNLINK_SIGS	      0x00004000
> +#define ECRYPTFS_I_SIZE_INITIALIZED   0x00008000
>  	u32 flags;
>  	unsigned int file_version;
>  	size_t iv_bytes;
> @@ -619,6 +620,7 @@ struct ecryptfs_open_req {
>  int ecryptfs_interpose(struct dentry *hidden_dentry,
>  		       struct dentry *this_dentry, struct super_block *sb,
>  		       u32 flags);
> +void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
>  int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  					struct dentry *lower_dentry,
>  					struct inode *ecryptfs_dir_inode,
> diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
> index 4e25328..eb744ed 100644
> --- a/fs/ecryptfs/file.c
> +++ b/fs/ecryptfs/file.c
> @@ -237,7 +237,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
>  				goto out_free;
>  			}
>  			rc = 0;
> -			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>  			mutex_unlock(&crypt_stat->cs_mutex);
>  			goto out;
>  		}
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 88ba4d4..5bab1c5 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -256,10 +256,8 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  	struct dentry *lower_dir_dentry;
>  	struct vfsmount *lower_mnt;
>  	struct inode *lower_inode;
> -	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
>  	struct ecryptfs_crypt_stat *crypt_stat;
>  	char *page_virt = NULL;
> -	u64 file_size;
>  	int rc = 0;
>  
>  	lower_dir_dentry = lower_dentry->d_parent;
> @@ -334,18 +332,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  		}
>  		crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
>  	}
> -	mount_crypt_stat = &ecryptfs_superblock_to_private(
> -		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->num_header_bytes_at_front
> -				     + 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);
> +	ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
>  out_free_kmem:
>  	kmem_cache_free(ecryptfs_header_cache_2, page_virt);
>  	goto out;
> @@ -925,7 +912,8 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
>  				goto out;
>  			}
>  			rc = 0;
> -			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>  		}
>  	}
>  	mutex_unlock(&crypt_stat->cs_mutex);
Leann Ogasawara July 20, 2011, 5:29 p.m. UTC | #3
On Tue, 2011-07-19 at 17:53 -0600, Tim Gardner wrote:
> The following changes since commit b9d52a294afd689b987ab0855a86a871d0a48618:
>   Greg Kroah-Hartman (1):
>         Linux 2.6.32.43
> 
> are available in the git repository at:
> 
>   git://kernel.ubuntu.com/rtg/ubuntu-lucid.git ecryptfs-lp509180
> 
> Tim Gardner (1):
>       eCryptfs: Handle failed metadata read in lookup
> 
>  fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>  fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>  fs/ecryptfs/file.c            |    3 ++-
>  fs/ecryptfs/inode.c           |   18 +++---------------
>  4 files changed, 28 insertions(+), 16 deletions(-)
> 
> From 2afbc1dca9a944e10434ddbe90b0c94521464475 Mon Sep 17 00:00:00 2001
> From: Tim Gardner <tim.gardner@canonical.com>
> Date: Tue, 19 Jul 2011 17:44:22 -0600
> Subject: [PATCH] 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: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
> 
> (backported from 3aeb86ea4cd15f728147a3bd5469a205ada8c767)
> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>

Acked-by: Leann Ogasawara <leann.ogasawara@canonical.com>

> ---
>  fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>  fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>  fs/ecryptfs/file.c            |    3 ++-
>  fs/ecryptfs/inode.c           |   18 +++---------------
>  4 files changed, 28 insertions(+), 16 deletions(-)
> 
> diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
> index 7cb0a59..e915eb1 100644
> --- a/fs/ecryptfs/crypto.c
> +++ b/fs/ecryptfs/crypto.c
> @@ -1455,6 +1455,25 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
>  		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
> @@ -1485,6 +1504,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);
> diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
> index 542f625..9685315 100644
> --- a/fs/ecryptfs/ecryptfs_kernel.h
> +++ b/fs/ecryptfs/ecryptfs_kernel.h
> @@ -270,6 +270,7 @@ struct ecryptfs_crypt_stat {
>  #define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00001000
>  #define ECRYPTFS_ENCFN_USE_FEK        0x00002000
>  #define ECRYPTFS_UNLINK_SIGS	      0x00004000
> +#define ECRYPTFS_I_SIZE_INITIALIZED   0x00008000
>  	u32 flags;
>  	unsigned int file_version;
>  	size_t iv_bytes;
> @@ -619,6 +620,7 @@ struct ecryptfs_open_req {
>  int ecryptfs_interpose(struct dentry *hidden_dentry,
>  		       struct dentry *this_dentry, struct super_block *sb,
>  		       u32 flags);
> +void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
>  int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  					struct dentry *lower_dentry,
>  					struct inode *ecryptfs_dir_inode,
> diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
> index 4e25328..eb744ed 100644
> --- a/fs/ecryptfs/file.c
> +++ b/fs/ecryptfs/file.c
> @@ -237,7 +237,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
>  				goto out_free;
>  			}
>  			rc = 0;
> -			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>  			mutex_unlock(&crypt_stat->cs_mutex);
>  			goto out;
>  		}
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 88ba4d4..5bab1c5 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -256,10 +256,8 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  	struct dentry *lower_dir_dentry;
>  	struct vfsmount *lower_mnt;
>  	struct inode *lower_inode;
> -	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
>  	struct ecryptfs_crypt_stat *crypt_stat;
>  	char *page_virt = NULL;
> -	u64 file_size;
>  	int rc = 0;
>  
>  	lower_dir_dentry = lower_dentry->d_parent;
> @@ -334,18 +332,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>  		}
>  		crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
>  	}
> -	mount_crypt_stat = &ecryptfs_superblock_to_private(
> -		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->num_header_bytes_at_front
> -				     + 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);
> +	ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
>  out_free_kmem:
>  	kmem_cache_free(ecryptfs_header_cache_2, page_virt);
>  	goto out;
> @@ -925,7 +912,8 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
>  				goto out;
>  			}
>  			rc = 0;
> -			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>  		}
>  	}
>  	mutex_unlock(&crypt_stat->cs_mutex);
> -- 
> 1.7.0.4
> 
>
Tim Gardner July 20, 2011, 8:17 p.m. UTC | #4
On 07/19/2011 04:53 PM, Tim Gardner wrote:
> The following changes since commit b9d52a294afd689b987ab0855a86a871d0a48618:
>    Greg Kroah-Hartman (1):
>          Linux 2.6.32.43
>
> are available in the git repository at:
>
>    git://kernel.ubuntu.com/rtg/ubuntu-lucid.git ecryptfs-lp509180
>
> Tim Gardner (1):
>        eCryptfs: Handle failed metadata read in lookup
>
>   fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>   fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>   fs/ecryptfs/file.c            |    3 ++-
>   fs/ecryptfs/inode.c           |   18 +++---------------
>   4 files changed, 28 insertions(+), 16 deletions(-)
>
>  From 2afbc1dca9a944e10434ddbe90b0c94521464475 Mon Sep 17 00:00:00 2001
> From: Tim Gardner<tim.gardner@canonical.com>
> Date: Tue, 19 Jul 2011 17:44:22 -0600
> Subject: [PATCH] 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: Tyler Hicks<tyhicks@linux.vnet.ibm.com>
>
> (backported from 3aeb86ea4cd15f728147a3bd5469a205ada8c767)
> Signed-off-by: Tim Gardner<tim.gardner@canonical.com>
> ---
>   fs/ecryptfs/crypto.c          |   21 +++++++++++++++++++++
>   fs/ecryptfs/ecryptfs_kernel.h |    2 ++
>   fs/ecryptfs/file.c            |    3 ++-
>   fs/ecryptfs/inode.c           |   18 +++---------------
>   4 files changed, 28 insertions(+), 16 deletions(-)
>
> diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
> index 7cb0a59..e915eb1 100644
> --- a/fs/ecryptfs/crypto.c
> +++ b/fs/ecryptfs/crypto.c
> @@ -1455,6 +1455,25 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
>   		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
> @@ -1485,6 +1504,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);
> diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
> index 542f625..9685315 100644
> --- a/fs/ecryptfs/ecryptfs_kernel.h
> +++ b/fs/ecryptfs/ecryptfs_kernel.h
> @@ -270,6 +270,7 @@ struct ecryptfs_crypt_stat {
>   #define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00001000
>   #define ECRYPTFS_ENCFN_USE_FEK        0x00002000
>   #define ECRYPTFS_UNLINK_SIGS	      0x00004000
> +#define ECRYPTFS_I_SIZE_INITIALIZED   0x00008000
>   	u32 flags;
>   	unsigned int file_version;
>   	size_t iv_bytes;
> @@ -619,6 +620,7 @@ struct ecryptfs_open_req {
>   int ecryptfs_interpose(struct dentry *hidden_dentry,
>   		       struct dentry *this_dentry, struct super_block *sb,
>   		       u32 flags);
> +void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
>   int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>   					struct dentry *lower_dentry,
>   					struct inode *ecryptfs_dir_inode,
> diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
> index 4e25328..eb744ed 100644
> --- a/fs/ecryptfs/file.c
> +++ b/fs/ecryptfs/file.c
> @@ -237,7 +237,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
>   				goto out_free;
>   			}
>   			rc = 0;
> -			crypt_stat->flags&= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags&= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>   			mutex_unlock(&crypt_stat->cs_mutex);
>   			goto out;
>   		}
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 88ba4d4..5bab1c5 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -256,10 +256,8 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>   	struct dentry *lower_dir_dentry;
>   	struct vfsmount *lower_mnt;
>   	struct inode *lower_inode;
> -	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
>   	struct ecryptfs_crypt_stat *crypt_stat;
>   	char *page_virt = NULL;
> -	u64 file_size;
>   	int rc = 0;
>
>   	lower_dir_dentry = lower_dentry->d_parent;
> @@ -334,18 +332,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
>   		}
>   		crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
>   	}
> -	mount_crypt_stat =&ecryptfs_superblock_to_private(
> -		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->num_header_bytes_at_front
> -				     + 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);
> +	ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
>   out_free_kmem:
>   	kmem_cache_free(ecryptfs_header_cache_2, page_virt);
>   	goto out;
> @@ -925,7 +912,8 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
>   				goto out;
>   			}
>   			rc = 0;
> -			crypt_stat->flags&= ~(ECRYPTFS_ENCRYPTED);
> +			crypt_stat->flags&= ~(ECRYPTFS_I_SIZE_INITIALIZED
> +						| ECRYPTFS_ENCRYPTED);
>   		}
>   	}
>   	mutex_unlock(&crypt_stat->cs_mutex);
diff mbox

Patch

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 7cb0a59..e915eb1 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1455,6 +1455,25 @@  static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
 		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
@@ -1485,6 +1504,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);
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 542f625..9685315 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -270,6 +270,7 @@  struct ecryptfs_crypt_stat {
 #define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00001000
 #define ECRYPTFS_ENCFN_USE_FEK        0x00002000
 #define ECRYPTFS_UNLINK_SIGS	      0x00004000
+#define ECRYPTFS_I_SIZE_INITIALIZED   0x00008000
 	u32 flags;
 	unsigned int file_version;
 	size_t iv_bytes;
@@ -619,6 +620,7 @@  struct ecryptfs_open_req {
 int ecryptfs_interpose(struct dentry *hidden_dentry,
 		       struct dentry *this_dentry, struct super_block *sb,
 		       u32 flags);
+void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
 int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
 					struct dentry *lower_dentry,
 					struct inode *ecryptfs_dir_inode,
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 4e25328..eb744ed 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -237,7 +237,8 @@  static int ecryptfs_open(struct inode *inode, struct file *file)
 				goto out_free;
 			}
 			rc = 0;
-			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
+			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
+						| ECRYPTFS_ENCRYPTED);
 			mutex_unlock(&crypt_stat->cs_mutex);
 			goto out;
 		}
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 88ba4d4..5bab1c5 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -256,10 +256,8 @@  int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
 	struct dentry *lower_dir_dentry;
 	struct vfsmount *lower_mnt;
 	struct inode *lower_inode;
-	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
 	struct ecryptfs_crypt_stat *crypt_stat;
 	char *page_virt = NULL;
-	u64 file_size;
 	int rc = 0;
 
 	lower_dir_dentry = lower_dentry->d_parent;
@@ -334,18 +332,7 @@  int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
 		}
 		crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
 	}
-	mount_crypt_stat = &ecryptfs_superblock_to_private(
-		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->num_header_bytes_at_front
-				     + 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);
+	ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
 out_free_kmem:
 	kmem_cache_free(ecryptfs_header_cache_2, page_virt);
 	goto out;
@@ -925,7 +912,8 @@  static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
 				goto out;
 			}
 			rc = 0;
-			crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
+			crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
+						| ECRYPTFS_ENCRYPTED);
 		}
 	}
 	mutex_unlock(&crypt_stat->cs_mutex);