From 6a1a90ad1b0edb556a7550a6ef8a8756f0304dd5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:00 +0200 Subject: rename generic_setattr Despite its name it's now a generic implementation of ->setattr, but rather a helper to copy attributes from a struct iattr to the inode. Rename it to setattr_copy to reflect this fact. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/attr.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'fs/attr.c') diff --git a/fs/attr.c b/fs/attr.c index b4fa3b0aa596..1f6a895e24e9 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -105,13 +105,13 @@ out_big: EXPORT_SYMBOL(inode_newsize_ok); /** - * generic_setattr - copy simple metadata updates into the generic inode + * setattr_copy - copy simple metadata updates into the generic inode * @inode: the inode to be updated * @attr: the new attributes * - * generic_setattr must be called with i_mutex held. + * setattr_copy must be called with i_mutex held. * - * generic_setattr updates the inode's metadata with that specified + * setattr_copy updates the inode's metadata with that specified * in attr. Noticably missing is inode size update, which is more complex * as it requires pagecache updates. See simple_setsize. * @@ -119,7 +119,7 @@ EXPORT_SYMBOL(inode_newsize_ok); * that for "simple" filesystems, the struct inode is the inode storage. * The caller is free to mark the inode dirty afterwards if needed. */ -void generic_setattr(struct inode *inode, const struct iattr *attr) +void setattr_copy(struct inode *inode, const struct iattr *attr) { unsigned int ia_valid = attr->ia_valid; @@ -144,11 +144,11 @@ void generic_setattr(struct inode *inode, const struct iattr *attr) inode->i_mode = mode; } } -EXPORT_SYMBOL(generic_setattr); +EXPORT_SYMBOL(setattr_copy); /* * note this function is deprecated, the new truncate sequence should be - * used instead -- see eg. simple_setsize, generic_setattr. + * used instead -- see eg. simple_setsize, setattr_copy. */ int inode_setattr(struct inode *inode, const struct iattr *attr) { @@ -163,7 +163,7 @@ int inode_setattr(struct inode *inode, const struct iattr *attr) return error; } - generic_setattr(inode, attr); + setattr_copy(inode, attr); mark_inode_dirty(inode); -- cgit v1.2.2 From eef2380c187890816b73b1a4cb89a09203759469 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:01 +0200 Subject: default to simple_setattr With the new truncate sequence every filesystem that wants to support file size changes on disk needs to implement its own ->setattr. So instead of calling inode_setattr which supports size changes call into a simple method that doesn't support this. simple_setattr is almost what we want except that it does not mark the inode dirty after changes. Given that marking the inode dirty is a no-op for the simple in-memory filesystems that use simple_setattr currently just add the mark_inode_dirty call. Also add a WARN_ON for the presence of a truncate method to simple_setattr to catch new instances of it during the transition period. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/attr.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'fs/attr.c') diff --git a/fs/attr.c b/fs/attr.c index 1f6a895e24e9..aeac826f4774 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -237,13 +237,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr) if (ia_valid & ATTR_SIZE) down_write(&dentry->d_inode->i_alloc_sem); - if (inode->i_op && inode->i_op->setattr) { + if (inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); - } else { - error = inode_change_ok(inode, attr); - if (!error) - error = inode_setattr(inode, attr); - } + else + error = simple_setattr(dentry, attr); if (ia_valid & ATTR_SIZE) up_write(&dentry->d_inode->i_alloc_sem); -- cgit v1.2.2 From 1025774ce411f2bd4b059ad7b53f0003569b74fa Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:02 +0200 Subject: remove inode_setattr Replace inode_setattr with opencoded variants of it in all callers. This moves the remaining call to vmtruncate into the filesystem methods where it can be replaced with the proper truncate sequence. In a few cases it was obvious that we would never end up calling vmtruncate so it was left out in the opencoded variant: spufs: explicitly checks for ATTR_SIZE earlier btrfs,hugetlbfs,logfs,dlmfs: explicitly clears ATTR_SIZE earlier ufs: contains an opencoded simple_seattr + truncate that sets the filesize just above In addition to that ncpfs called inode_setattr with handcrafted iattrs, which allowed to trim down the opencoded variant. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/attr.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'fs/attr.c') diff --git a/fs/attr.c b/fs/attr.c index aeac826f4774..ed44d8ae8bf1 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -146,31 +146,6 @@ void setattr_copy(struct inode *inode, const struct iattr *attr) } EXPORT_SYMBOL(setattr_copy); -/* - * note this function is deprecated, the new truncate sequence should be - * used instead -- see eg. simple_setsize, setattr_copy. - */ -int inode_setattr(struct inode *inode, const struct iattr *attr) -{ - unsigned int ia_valid = attr->ia_valid; - - if (ia_valid & ATTR_SIZE && - attr->ia_size != i_size_read(inode)) { - int error; - - error = vmtruncate(inode, attr->ia_size); - if (error) - return error; - } - - setattr_copy(inode, attr); - - mark_inode_dirty(inode); - - return 0; -} -EXPORT_SYMBOL(inode_setattr); - int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; -- cgit v1.2.2 From 2c27c65ed0696f0b5df2dad2cf6462d72164d547 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:04 +0200 Subject: check ATTR_SIZE contraints in inode_change_ok Make sure we check the truncate constraints early on in ->setattr by adding those checks to inode_change_ok. Also clean up and document inode_change_ok to make this obvious. As a fallout we don't have to call inode_newsize_ok from simple_setsize and simplify it down to a truncate_setsize which doesn't return an error. This simplifies a lot of setattr implementations and means we use truncate_setsize almost everywhere. Get rid of fat_setsize now that it's trivial and mark ext2_setsize static to make the calling convention obvious. Keep the inode_newsize_ok in vmtruncate for now as all callers need an audit for its removal anyway. Note: setattr code in ecryptfs doesn't call inode_change_ok at all and needs a deeper audit, but that is left for later. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/attr.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) (limited to 'fs/attr.c') diff --git a/fs/attr.c b/fs/attr.c index ed44d8ae8bf1..7ca41811afa1 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -14,35 +14,53 @@ #include #include -/* Taken over from the old code... */ - -/* POSIX UID/GID verification for setting inode attributes. */ +/** + * inode_change_ok - check if attribute changes to an inode are allowed + * @inode: inode to check + * @attr: attributes to change + * + * Check if we are allowed to change the attributes contained in @attr + * in the given inode. This includes the normal unix access permission + * checks, as well as checks for rlimits and others. + * + * Should be called as the first thing in ->setattr implementations, + * possibly after taking additional locks. + */ int inode_change_ok(const struct inode *inode, struct iattr *attr) { - int retval = -EPERM; unsigned int ia_valid = attr->ia_valid; + /* + * First check size constraints. These can't be overriden using + * ATTR_FORCE. + */ + if (ia_valid & ATTR_SIZE) { + int error = inode_newsize_ok(inode, attr->ia_size); + if (error) + return error; + } + /* If force is set do it anyway. */ if (ia_valid & ATTR_FORCE) - goto fine; + return 0; /* Make sure a caller can chown. */ if ((ia_valid & ATTR_UID) && (current_fsuid() != inode->i_uid || attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN)) - goto error; + return -EPERM; /* Make sure caller can chgrp. */ if ((ia_valid & ATTR_GID) && (current_fsuid() != inode->i_uid || (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) && !capable(CAP_CHOWN)) - goto error; + return -EPERM; /* Make sure a caller can chmod. */ if (ia_valid & ATTR_MODE) { if (!is_owner_or_cap(inode)) - goto error; + return -EPERM; /* Also check the setgid bit! */ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : inode->i_gid) && !capable(CAP_FSETID)) @@ -52,12 +70,10 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr) /* Check for setting the inode time. */ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { if (!is_owner_or_cap(inode)) - goto error; + return -EPERM; } -fine: - retval = 0; -error: - return retval; + + return 0; } EXPORT_SYMBOL(inode_change_ok); @@ -113,7 +129,7 @@ EXPORT_SYMBOL(inode_newsize_ok); * * setattr_copy updates the inode's metadata with that specified * in attr. Noticably missing is inode size update, which is more complex - * as it requires pagecache updates. See simple_setsize. + * as it requires pagecache updates. * * The inode is not marked as dirty after this operation. The rationale is * that for "simple" filesystems, the struct inode is the inode storage. -- cgit v1.2.2