Skip to content

Commit

Permalink
eCryptfs: Use notify_change for truncating lower inodes
Browse files Browse the repository at this point in the history
When truncating inodes in the lower filesystem, eCryptfs directly
invoked vmtruncate(). As Christoph Hellwig pointed out, vmtruncate() is
a filesystem helper function, but filesystems may need to do more than
just a call to vmtruncate().

This patch moves the lower inode truncation out of ecryptfs_truncate()
and renames the function to truncate_upper().  truncate_upper() updates
an iattr for the lower inode to indicate if the lower inode needs to be
truncated upon return.  ecryptfs_setattr() then calls notify_change(),
using the updated iattr for the lower inode, to complete the truncation.

For eCryptfs functions needing to truncate, ecryptfs_truncate() is
reintroduced as a simple way to truncate the upper inode to a specified
size and then truncate the lower inode accordingly.

https://bugs.launchpad.net/bugs/451368

Reported-by: Christoph Hellwig <hch@lst.de>
Acked-by: Dustin Kirkland <kirkland@canonical.com>
Cc: ecryptfs-devel@lists.launchpad.net
Cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
  • Loading branch information
Tyler Hicks committed Jan 20, 2010
1 parent 24bc734 commit 5f3ef64
Showing 1 changed file with 67 additions and 32 deletions.
99 changes: 67 additions & 32 deletions fs/ecryptfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -772,18 +772,23 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat,
}

/**
* ecryptfs_truncate
* truncate_upper
* @dentry: The ecryptfs layer dentry
* @new_length: The length to expand the file to
* @ia: Address of the ecryptfs inode's attributes
* @lower_ia: Address of the lower inode's attributes
*
* Function to handle truncations modifying the size of the file. Note
* that the file sizes are interpolated. When expanding, we are simply
* writing strings of 0's out. When truncating, we need to modify the
* underlying file size according to the page index interpolations.
* writing strings of 0's out. When truncating, we truncate the upper
* inode and update the lower_ia according to the page index
* interpolations. If ATTR_SIZE is set in lower_ia->ia_valid upon return,
* the caller must use lower_ia in a call to notify_change() to perform
* the truncation of the lower inode.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
static int truncate_upper(struct dentry *dentry, struct iattr *ia,
struct iattr *lower_ia)
{
int rc = 0;
struct inode *inode = dentry->d_inode;
Expand All @@ -794,8 +799,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
loff_t lower_size_before_truncate;
loff_t lower_size_after_truncate;

if (unlikely((new_length == i_size)))
if (unlikely((ia->ia_size == i_size))) {
lower_ia->ia_valid &= ~ATTR_SIZE;
goto out;
}
crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
/* Set up a fake ecryptfs file, this is used to interface with
* the file in the underlying filesystem so that the
Expand All @@ -815,28 +822,30 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
&fake_ecryptfs_file,
ecryptfs_inode_to_private(dentry->d_inode)->lower_file);
/* Switch on growing or shrinking file */
if (new_length > i_size) {
if (ia->ia_size > i_size) {
char zero[] = { 0x00 };

lower_ia->ia_valid &= ~ATTR_SIZE;
/* Write a single 0 at the last position of the file;
* this triggers code that will fill in 0's throughout
* the intermediate portion of the previous end of the
* file and the new and of the file */
rc = ecryptfs_write(&fake_ecryptfs_file, zero,
(new_length - 1), 1);
} else { /* new_length < i_size_read(inode) */
/* We're chopping off all the pages down do the page
* in which new_length is located. Fill in the end of
* that page from (new_length & ~PAGE_CACHE_MASK) to
(ia->ia_size - 1), 1);
} else { /* ia->ia_size < i_size_read(inode) */
/* We're chopping off all the pages down to the page
* in which ia->ia_size is located. Fill in the end of
* that page from (ia->ia_size & ~PAGE_CACHE_MASK) to
* PAGE_CACHE_SIZE with zeros. */
size_t num_zeros = (PAGE_CACHE_SIZE
- (new_length & ~PAGE_CACHE_MASK));
- (ia->ia_size & ~PAGE_CACHE_MASK));

if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
rc = vmtruncate(inode, new_length);
rc = vmtruncate(inode, ia->ia_size);
if (rc)
goto out_free;
rc = vmtruncate(lower_dentry->d_inode, new_length);
lower_ia->ia_size = ia->ia_size;
lower_ia->ia_valid |= ATTR_SIZE;
goto out_free;
}
if (num_zeros) {
Expand All @@ -848,7 +857,7 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
goto out_free;
}
rc = ecryptfs_write(&fake_ecryptfs_file, zeros_virt,
new_length, num_zeros);
ia->ia_size, num_zeros);
kfree(zeros_virt);
if (rc) {
printk(KERN_ERR "Error attempting to zero out "
Expand All @@ -857,7 +866,7 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
goto out_free;
}
}
vmtruncate(inode, new_length);
vmtruncate(inode, ia->ia_size);
rc = ecryptfs_write_inode_size_to_metadata(inode);
if (rc) {
printk(KERN_ERR "Problem with "
Expand All @@ -870,10 +879,12 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
lower_size_before_truncate =
upper_size_to_lower_size(crypt_stat, i_size);
lower_size_after_truncate =
upper_size_to_lower_size(crypt_stat, new_length);
if (lower_size_after_truncate < lower_size_before_truncate)
vmtruncate(lower_dentry->d_inode,
lower_size_after_truncate);
upper_size_to_lower_size(crypt_stat, ia->ia_size);
if (lower_size_after_truncate < lower_size_before_truncate) {
lower_ia->ia_size = lower_size_after_truncate;
lower_ia->ia_valid |= ATTR_SIZE;
} else
lower_ia->ia_valid &= ~ATTR_SIZE;
}
out_free:
if (ecryptfs_file_to_private(&fake_ecryptfs_file))
Expand All @@ -883,6 +894,33 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
return rc;
}

/**
* ecryptfs_truncate
* @dentry: The ecryptfs layer dentry
* @new_length: The length to expand the file to
*
* Simple function that handles the truncation of an eCryptfs inode and
* its corresponding lower inode.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
{
struct iattr ia = { .ia_valid = ATTR_SIZE, .ia_size = new_length };
struct iattr lower_ia = { .ia_valid = 0 };
int rc;

rc = truncate_upper(dentry, &ia, &lower_ia);
if (!rc && lower_ia.ia_valid & ATTR_SIZE) {
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);

mutex_lock(&lower_dentry->d_inode->i_mutex);
rc = notify_change(lower_dentry, &lower_ia);
mutex_unlock(&lower_dentry->d_inode->i_mutex);
}
return rc;
}

static int
ecryptfs_permission(struct inode *inode, int mask)
{
Expand All @@ -905,6 +943,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
{
int rc = 0;
struct dentry *lower_dentry;
struct iattr lower_ia;
struct inode *inode;
struct inode *lower_inode;
struct ecryptfs_crypt_stat *crypt_stat;
Expand Down Expand Up @@ -943,15 +982,11 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
}
}
mutex_unlock(&crypt_stat->cs_mutex);
memcpy(&lower_ia, ia, sizeof(lower_ia));
if (ia->ia_valid & ATTR_FILE)
lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file);
if (ia->ia_valid & ATTR_SIZE) {
ecryptfs_printk(KERN_DEBUG,
"ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n",
ia->ia_valid, ATTR_SIZE);
rc = ecryptfs_truncate(dentry, ia->ia_size);
/* ecryptfs_truncate handles resizing of the lower file */
ia->ia_valid &= ~ATTR_SIZE;
ecryptfs_printk(KERN_DEBUG, "ia->ia_valid = [%x]\n",
ia->ia_valid);
rc = truncate_upper(dentry, ia, &lower_ia);
if (rc < 0)
goto out;
}
Expand All @@ -960,11 +995,11 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
* mode change is for clearing setuid/setgid bits. Allow lower fs
* to interpret this in its own way.
*/
if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
ia->ia_valid &= ~ATTR_MODE;
if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
lower_ia.ia_valid &= ~ATTR_MODE;

mutex_lock(&lower_dentry->d_inode->i_mutex);
rc = notify_change(lower_dentry, ia);
rc = notify_change(lower_dentry, &lower_ia);
mutex_unlock(&lower_dentry->d_inode->i_mutex);
out:
fsstack_copy_attr_all(inode, lower_inode);
Expand Down

0 comments on commit 5f3ef64

Please sign in to comment.