aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ecryptfs
diff options
context:
space:
mode:
authorTyler Hicks <tyhicks@canonical.com>2012-01-19 21:33:44 -0500
committerTyler Hicks <tyhicks@canonical.com>2012-01-25 15:43:41 -0500
commita261a03904849c3df50bd0300efb7fb3f865137d (patch)
tree59785a3a6894e3e29e547813ff1d010bc08e5893 /fs/ecryptfs
parent5e6f0d769017cc49207ef56996e42363ec26c1f0 (diff)
eCryptfs: Check inode changes in setattr
Most filesystems call inode_change_ok() very early in ->setattr(), but eCryptfs didn't call it at all. It allowed the lower filesystem to make the call in its ->setattr() function. Then, eCryptfs would copy the appropriate inode attributes from the lower inode to the eCryptfs inode. This patch changes that and actually calls inode_change_ok() on the eCryptfs inode, fairly early in ecryptfs_setattr(). Ideally, the call would happen earlier in ecryptfs_setattr(), but there are some possible inode initialization steps that must happen first. Since the call was already being made on the lower inode, the change in functionality should be minimal, except for the case of a file extending truncate call. In that case, inode_newsize_ok() was never being called on the eCryptfs inode. Rather than inode_newsize_ok() catching maximum file size errors early on, eCryptfs would encrypt zeroed pages and write them to the lower filesystem until the lower filesystem's write path caught the error in generic_write_checks(). This patch introduces a new function, called ecryptfs_inode_newsize_ok(), which checks if the new lower file size is within the appropriate limits when the truncate operation will be growing the lower file. In summary this change prevents eCryptfs truncate operations (and the resulting page encryptions), which would exceed the lower filesystem limits or FSIZE rlimits, from ever starting. Signed-off-by: Tyler Hicks <tyhicks@canonical.com> Reviewed-by: Li Wang <liwang@nudt.edu.cn> Cc: <stable@vger.kernel.org>
Diffstat (limited to 'fs/ecryptfs')
-rw-r--r--fs/ecryptfs/inode.c48
1 files changed, 36 insertions, 12 deletions
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 19a8ca4ab1dd..19892d7d2ed1 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -822,18 +822,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
822 size_t num_zeros = (PAGE_CACHE_SIZE 822 size_t num_zeros = (PAGE_CACHE_SIZE
823 - (ia->ia_size & ~PAGE_CACHE_MASK)); 823 - (ia->ia_size & ~PAGE_CACHE_MASK));
824 824
825
826 /*
827 * XXX(truncate) this should really happen at the begginning
828 * of ->setattr. But the code is too messy to that as part
829 * of a larger patch. ecryptfs is also totally missing out
830 * on the inode_change_ok check at the beginning of
831 * ->setattr while would include this.
832 */
833 rc = inode_newsize_ok(inode, ia->ia_size);
834 if (rc)
835 goto out;
836
837 if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { 825 if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
838 truncate_setsize(inode, ia->ia_size); 826 truncate_setsize(inode, ia->ia_size);
839 lower_ia->ia_size = ia->ia_size; 827 lower_ia->ia_size = ia->ia_size;
@@ -883,6 +871,28 @@ out:
883 return rc; 871 return rc;
884} 872}
885 873
874static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset)
875{
876 struct ecryptfs_crypt_stat *crypt_stat;
877 loff_t lower_oldsize, lower_newsize;
878
879 crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
880 lower_oldsize = upper_size_to_lower_size(crypt_stat,
881 i_size_read(inode));
882 lower_newsize = upper_size_to_lower_size(crypt_stat, offset);
883 if (lower_newsize > lower_oldsize) {
884 /*
885 * The eCryptfs inode and the new *lower* size are mixed here
886 * because we may not have the lower i_mutex held and/or it may
887 * not be appropriate to call inode_newsize_ok() with inodes
888 * from other filesystems.
889 */
890 return inode_newsize_ok(inode, lower_newsize);
891 }
892
893 return 0;
894}
895
886/** 896/**
887 * ecryptfs_truncate 897 * ecryptfs_truncate
888 * @dentry: The ecryptfs layer dentry 898 * @dentry: The ecryptfs layer dentry
@@ -899,6 +909,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
899 struct iattr lower_ia = { .ia_valid = 0 }; 909 struct iattr lower_ia = { .ia_valid = 0 };
900 int rc; 910 int rc;
901 911
912 rc = ecryptfs_inode_newsize_ok(dentry->d_inode, new_length);
913 if (rc)
914 return rc;
915
902 rc = truncate_upper(dentry, &ia, &lower_ia); 916 rc = truncate_upper(dentry, &ia, &lower_ia);
903 if (!rc && lower_ia.ia_valid & ATTR_SIZE) { 917 if (!rc && lower_ia.ia_valid & ATTR_SIZE) {
904 struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); 918 struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
@@ -978,6 +992,16 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
978 } 992 }
979 } 993 }
980 mutex_unlock(&crypt_stat->cs_mutex); 994 mutex_unlock(&crypt_stat->cs_mutex);
995
996 rc = inode_change_ok(inode, ia);
997 if (rc)
998 goto out;
999 if (ia->ia_valid & ATTR_SIZE) {
1000 rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size);
1001 if (rc)
1002 goto out;
1003 }
1004
981 if (S_ISREG(inode->i_mode)) { 1005 if (S_ISREG(inode->i_mode)) {
982 rc = filemap_write_and_wait(inode->i_mapping); 1006 rc = filemap_write_and_wait(inode->i_mapping);
983 if (rc) 1007 if (rc)