From: Eric Paris <eparis@redhat.com> Date: Thu, 16 Sep 2010 18:27:12 -0400 Subject: [fs] xattr: refactor vfs_setxattr for SELinux hook use Message-id: <20100916182712.3360.24374.stgit@paris.rdu.redhat.com> Patchwork-id: 28267 O-Subject: [RHEL5 PATCH -v2 1/3] VFS: Factor out part of vfs_setxattr so it can be called from the SELinux hook for inode_setsecctx. Bugzilla: 582374 RH-Acked-by: Alexander Viro <aviro@redhat.com> RHBZ 582374 upstream commit b1ab7e4b2a88d3ac13771463be8f302ce1616cfc version 2: address kabi concerns by not converting char * into const char * like upstream. This factors out the part of the vfs_setxattr function that performs the setting of the xattr and its notification. This is needed so the SELinux implementation of inode_setsecctx can handle the setting of the xattr while maintaining the proper separation of layers. Signed-off-by: David P. Quigley <dpquigl@tycho.nsa.gov> Acked-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: James Morris <jmorris@namei.org> diff --git a/fs/xattr.c b/fs/xattr.c index 0e5447f..7060d78 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -62,22 +62,28 @@ xattr_permission(struct inode *inode, const char *name, int mask) return permission(inode, mask, NULL); } -int -vfs_setxattr(struct dentry *dentry, char *name, void *value, - size_t size, int flags) +/** + * __vfs_setxattr_noperm - perform setxattr operation without performing + * permission checks. + * + * @dentry - object to perform setxattr on + * @name - xattr name to set + * @value - value to set @name to + * @size - size of @value + * @flags - flags to pass into filesystem operations + * + * returns the result of the internal setxattr or setsecurity operations. + * + * This function requires the caller to lock the inode's i_mutex before it + * is executed. It also assumes that the caller will make the appropriate + * permission checks. + */ +int __vfs_setxattr_noperm(struct dentry *dentry, char *name, + void *value, size_t size, int flags) { struct inode *inode = dentry->d_inode; - int error; - - error = xattr_permission(inode, name, MAY_WRITE); - if (error) - return error; + int error = -EOPNOTSUPP; - mutex_lock(&inode->i_mutex); - error = security_inode_setxattr(dentry, name, value, size, flags); - if (error) - goto out; - error = -EOPNOTSUPP; if (inode->i_op->setxattr) { error = inode->i_op->setxattr(dentry, name, value, size, flags); if (!error) { @@ -87,12 +93,35 @@ vfs_setxattr(struct dentry *dentry, char *name, void *value, } } else if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { - const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; + char *suffix = name + XATTR_SECURITY_PREFIX_LEN; error = security_inode_setsecurity(inode, suffix, value, size, flags); if (!error) fsnotify_xattr(dentry); } + + return error; +} + + +int +vfs_setxattr(struct dentry *dentry, char *name, void *value, + size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = xattr_permission(inode, name, MAY_WRITE); + if (error) + return error; + + mutex_lock(&inode->i_mutex); + error = security_inode_setxattr(dentry, name, value, size, flags); + if (error) + goto out; + + error = __vfs_setxattr_noperm(dentry, name, value, size, flags); + out: mutex_unlock(&inode->i_mutex); return error; diff --git a/include/linux/security.h b/include/linux/security.h index ba252dc..a899161 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1776,7 +1776,7 @@ static inline int security_inode_setxattr (struct dentry *dentry, char *name, } static inline void security_inode_post_setxattr (struct dentry *dentry, char *name, - void *value, size_t size, int flags) + void *value, size_t size, int flags) { if (unlikely (IS_PRIVATE (dentry->d_inode))) return; diff --git a/include/linux/xattr.h b/include/linux/xattr.h index cda8a96..d29e9b4 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -41,6 +41,7 @@ struct xattr_handler { }; ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t); +int __vfs_setxattr_noperm(struct dentry *, char *, void *, size_t, int); int vfs_setxattr(struct dentry *, char *, void *, size_t, int); int vfs_removexattr(struct dentry *, char *);