diff options
Diffstat (limited to 'fs/attr.c')
| -rw-r--r-- | fs/attr.c | 88 | 
1 files changed, 38 insertions, 50 deletions
diff --git a/fs/attr.c b/fs/attr.c index b4fa3b0aa59..7ca41811afa 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -14,35 +14,53 @@  #include <linux/fcntl.h>  #include <linux/security.h> -/* 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); @@ -105,21 +121,21 @@ 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. + * 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.   * 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,32 +160,7 @@ void generic_setattr(struct inode *inode, const struct iattr *attr)  		inode->i_mode = mode;  	}  } -EXPORT_SYMBOL(generic_setattr); - -/* - * note this function is deprecated, the new truncate sequence should be - * used instead -- see eg. simple_setsize, generic_setattr. - */ -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; -	} - -	generic_setattr(inode, attr); - -	mark_inode_dirty(inode); - -	return 0; -} -EXPORT_SYMBOL(inode_setattr); +EXPORT_SYMBOL(setattr_copy);  int notify_change(struct dentry * dentry, struct iattr * attr)  { @@ -237,13 +228,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);  |