diff options
Diffstat (limited to 'fs/attr.c')
| -rw-r--r-- | fs/attr.c | 38 | 
1 files changed, 28 insertions, 10 deletions
diff --git a/fs/attr.c b/fs/attr.c index 1449adb14ef..6530ced1969 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -50,14 +50,14 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)  	if ((ia_valid & ATTR_UID) &&  	    (!uid_eq(current_fsuid(), inode->i_uid) ||  	     !uid_eq(attr->ia_uid, inode->i_uid)) && -	    !inode_capable(inode, CAP_CHOWN)) +	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))  		return -EPERM;  	/* Make sure caller can chgrp. */  	if ((ia_valid & ATTR_GID) &&  	    (!uid_eq(current_fsuid(), inode->i_uid) ||  	    (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) && -	    !inode_capable(inode, CAP_CHOWN)) +	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))  		return -EPERM;  	/* Make sure a caller can chmod. */ @@ -67,7 +67,7 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)  		/* Also check the setgid bit! */  		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :  				inode->i_gid) && -		    !inode_capable(inode, CAP_FSETID)) +		    !capable_wrt_inode_uidgid(inode, CAP_FSETID))  			attr->ia_mode &= ~S_ISGID;  	} @@ -160,14 +160,34 @@ void setattr_copy(struct inode *inode, const struct iattr *attr)  		umode_t mode = attr->ia_mode;  		if (!in_group_p(inode->i_gid) && -		    !inode_capable(inode, CAP_FSETID)) +		    !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;  	umode_t mode = inode->i_mode; @@ -182,11 +202,6 @@ int notify_change(struct dentry * dentry, struct iattr * attr)  			return -EPERM;  	} -	if ((ia_valid & ATTR_SIZE) && IS_I_VERSION(inode)) { -		if (attr->ia_size != inode->i_size) -			inode_inc_iversion(inode); -	} -  	if ((ia_valid & ATTR_MODE)) {  		umode_t amode = attr->ia_mode;  		/* Flag setting protected by i_mutex */ @@ -243,6 +258,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr)  	error = security_inode_setattr(dentry, attr);  	if (error)  		return error; +	error = try_break_deleg(inode, delegated_inode); +	if (error) +		return error;  	if (inode->i_op->setattr)  		error = inode->i_op->setattr(dentry, attr);  | 
