diff options
Diffstat (limited to 'fs/attr.c')
| -rw-r--r-- | fs/attr.c | 75 | 
1 files changed, 54 insertions, 21 deletions
diff --git a/fs/attr.c b/fs/attr.c index 7ca41811afa..6530ced1969 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -5,7 +5,7 @@   *  changes by Thomas Schoebel-Theuer   */ -#include <linux/module.h> +#include <linux/export.h>  #include <linux/time.h>  #include <linux/mm.h>  #include <linux/string.h> @@ -13,6 +13,8 @@  #include <linux/fsnotify.h>  #include <linux/fcntl.h>  #include <linux/security.h> +#include <linux/evm.h> +#include <linux/ima.h>  /**   * inode_change_ok - check if attribute changes to an inode are allowed @@ -46,30 +48,32 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)  	/* 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)) +	    (!uid_eq(current_fsuid(), inode->i_uid) || +	     !uid_eq(attr->ia_uid, inode->i_uid)) && +	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))  		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)) +	    (!uid_eq(current_fsuid(), inode->i_uid) || +	    (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) && +	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))  		return -EPERM;  	/* Make sure a caller can chmod. */  	if (ia_valid & ATTR_MODE) { -		if (!is_owner_or_cap(inode)) +		if (!inode_owner_or_capable(inode))  			return -EPERM;  		/* Also check the setgid bit! */  		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : -				inode->i_gid) && !capable(CAP_FSETID)) +				inode->i_gid) && +		    !capable_wrt_inode_uidgid(inode, CAP_FSETID))  			attr->ia_mode &= ~S_ISGID;  	}  	/* Check for setting the inode time. */  	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { -		if (!is_owner_or_cap(inode)) +		if (!inode_owner_or_capable(inode))  			return -EPERM;  	} @@ -128,7 +132,7 @@ EXPORT_SYMBOL(inode_newsize_ok);   * setattr_copy must be called with i_mutex held.   *   * setattr_copy updates the inode's metadata with that specified - * in attr. Noticably missing is inode size update, which is more complex + * in attr. Noticeably missing is inode size update, which is more complex   * as it requires pagecache updates.   *   * The inode is not marked as dirty after this operation. The rationale is @@ -155,26 +159,56 @@ void setattr_copy(struct inode *inode, const struct iattr *attr)  	if (ia_valid & ATTR_MODE) {  		umode_t mode = attr->ia_mode; -		if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) +		if (!in_group_p(inode->i_gid) && +		    !capable_wrt_inode_uidgid(inode, CAP_FSETID))  			mode &= ~S_ISGID;  		inode->i_mode = mode;  	}  }  EXPORT_SYMBOL(setattr_copy); -int notify_change(struct dentry * dentry, struct iattr * attr) +/** + * notify_change - modify attributes of a filesytem object + * @dentry:	object affected + * @iattr:	new attributes + * @delegated_inode: returns inode, if the inode is delegated + * + * The caller must hold the i_mutex on the affected object. + * + * If notify_change discovers a delegation in need of breaking, + * it will return -EWOULDBLOCK and return a reference to the inode in + * delegated_inode.  The caller should then break the delegation and + * retry.  Because breaking a delegation may take a long time, the + * caller should drop the i_mutex before doing so. + * + * Alternatively, a caller may pass NULL for delegated_inode.  This may + * be appropriate for callers that expect the underlying filesystem not + * to be NFS exported.  Also, passing NULL is fine for callers holding + * the file open for write, as there can be no conflicting delegation in + * that case. + */ +int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)  {  	struct inode *inode = dentry->d_inode; -	mode_t mode = inode->i_mode; +	umode_t mode = inode->i_mode;  	int error;  	struct timespec now;  	unsigned int ia_valid = attr->ia_valid; +	WARN_ON_ONCE(!mutex_is_locked(&inode->i_mutex)); +  	if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {  		if (IS_IMMUTABLE(inode) || IS_APPEND(inode))  			return -EPERM;  	} +	if ((ia_valid & ATTR_MODE)) { +		umode_t amode = attr->ia_mode; +		/* Flag setting protected by i_mutex */ +		if (is_sxid(amode)) +			inode->i_flags &= ~S_NOSEC; +	} +  	now = current_fs_time(inode->i_sb);  	attr->ia_ctime = now; @@ -224,22 +258,21 @@ int notify_change(struct dentry * dentry, struct iattr * attr)  	error = security_inode_setattr(dentry, attr);  	if (error)  		return error; - -	if (ia_valid & ATTR_SIZE) -		down_write(&dentry->d_inode->i_alloc_sem); +	error = try_break_deleg(inode, delegated_inode); +	if (error) +		return error;  	if (inode->i_op->setattr)  		error = inode->i_op->setattr(dentry, attr);  	else  		error = simple_setattr(dentry, attr); -	if (ia_valid & ATTR_SIZE) -		up_write(&dentry->d_inode->i_alloc_sem); - -	if (!error) +	if (!error) {  		fsnotify_change(dentry, ia_valid); +		ima_inode_post_setattr(dentry); +		evm_inode_post_setattr(dentry, ia_valid); +	}  	return error;  } -  EXPORT_SYMBOL(notify_change);  | 
