diff options
Diffstat (limited to 'fs/sysfs/inode.c')
| -rw-r--r-- | fs/sysfs/inode.c | 131 | 
1 files changed, 97 insertions, 34 deletions
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 555f0ff988d..e28cecf179f 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -18,6 +18,8 @@  #include <linux/capability.h>  #include <linux/errno.h>  #include <linux/sched.h> +#include <linux/xattr.h> +#include <linux/security.h>  #include "sysfs.h"  extern struct super_block * sysfs_sb; @@ -29,12 +31,14 @@ static const struct address_space_operations sysfs_aops = {  };  static struct backing_dev_info sysfs_backing_dev_info = { +	.name		= "sysfs",  	.ra_pages	= 0,	/* No readahead */  	.capabilities	= BDI_CAP_NO_ACCT_AND_WRITEBACK,  };  static const struct inode_operations sysfs_inode_operations ={  	.setattr	= sysfs_setattr, +	.setxattr	= sysfs_setxattr,  };  int __init sysfs_inode_init(void) @@ -42,18 +46,37 @@ int __init sysfs_inode_init(void)  	return bdi_init(&sysfs_backing_dev_info);  } +struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) +{ +	struct sysfs_inode_attrs *attrs; +	struct iattr *iattrs; + +	attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); +	if (!attrs) +		return NULL; +	iattrs = &attrs->ia_iattr; + +	/* assign default attributes */ +	iattrs->ia_mode = sd->s_mode; +	iattrs->ia_uid = 0; +	iattrs->ia_gid = 0; +	iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; + +	return attrs; +}  int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)  {  	struct inode * inode = dentry->d_inode;  	struct sysfs_dirent * sd = dentry->d_fsdata; -	struct iattr * sd_iattr; +	struct sysfs_inode_attrs *sd_attrs; +	struct iattr *iattrs;  	unsigned int ia_valid = iattr->ia_valid;  	int error;  	if (!sd)  		return -EINVAL; -	sd_iattr = sd->s_iattr; +	sd_attrs = sd->s_iattr;  	error = inode_change_ok(inode, iattr);  	if (error) @@ -65,42 +88,77 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)  	if (error)  		return error; -	if (!sd_iattr) { +	if (!sd_attrs) {  		/* setting attributes for the first time, allocate now */ -		sd_iattr = kzalloc(sizeof(struct iattr), GFP_KERNEL); -		if (!sd_iattr) +		sd_attrs = sysfs_init_inode_attrs(sd); +		if (!sd_attrs)  			return -ENOMEM; -		/* assign default attributes */ -		sd_iattr->ia_mode = sd->s_mode; -		sd_iattr->ia_uid = 0; -		sd_iattr->ia_gid = 0; -		sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME; -		sd->s_iattr = sd_iattr; +		sd->s_iattr = sd_attrs; +	} else { +		/* attributes were changed at least once in past */ +		iattrs = &sd_attrs->ia_iattr; + +		if (ia_valid & ATTR_UID) +			iattrs->ia_uid = iattr->ia_uid; +		if (ia_valid & ATTR_GID) +			iattrs->ia_gid = iattr->ia_gid; +		if (ia_valid & ATTR_ATIME) +			iattrs->ia_atime = timespec_trunc(iattr->ia_atime, +					inode->i_sb->s_time_gran); +		if (ia_valid & ATTR_MTIME) +			iattrs->ia_mtime = timespec_trunc(iattr->ia_mtime, +					inode->i_sb->s_time_gran); +		if (ia_valid & ATTR_CTIME) +			iattrs->ia_ctime = timespec_trunc(iattr->ia_ctime, +					inode->i_sb->s_time_gran); +		if (ia_valid & ATTR_MODE) { +			umode_t mode = iattr->ia_mode; + +			if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) +				mode &= ~S_ISGID; +			iattrs->ia_mode = sd->s_mode = mode; +		}  	} +	return error; +} -	/* attributes were changed atleast once in past */ +int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, +		size_t size, int flags) +{ +	struct sysfs_dirent *sd = dentry->d_fsdata; +	struct sysfs_inode_attrs *iattrs; +	void *secdata; +	int error; +	u32 secdata_len = 0; -	if (ia_valid & ATTR_UID) -		sd_iattr->ia_uid = iattr->ia_uid; -	if (ia_valid & ATTR_GID) -		sd_iattr->ia_gid = iattr->ia_gid; -	if (ia_valid & ATTR_ATIME) -		sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime, -						inode->i_sb->s_time_gran); -	if (ia_valid & ATTR_MTIME) -		sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime, -						inode->i_sb->s_time_gran); -	if (ia_valid & ATTR_CTIME) -		sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime, -						inode->i_sb->s_time_gran); -	if (ia_valid & ATTR_MODE) { -		umode_t mode = iattr->ia_mode; +	if (!sd) +		return -EINVAL; +	if (!sd->s_iattr) +		sd->s_iattr = sysfs_init_inode_attrs(sd); +	if (!sd->s_iattr) +		return -ENOMEM; -		if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) -			mode &= ~S_ISGID; -		sd_iattr->ia_mode = sd->s_mode = mode; -	} +	iattrs = sd->s_iattr; +	if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { +		const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; +		error = security_inode_setsecurity(dentry->d_inode, suffix, +						value, size, flags); +		if (error) +			goto out; +		error = security_inode_getsecctx(dentry->d_inode, +						&secdata, &secdata_len); +		if (error) +			goto out; +		if (iattrs->ia_secdata) +			security_release_secctx(iattrs->ia_secdata, +						iattrs->ia_secdata_len); +		iattrs->ia_secdata = secdata; +		iattrs->ia_secdata_len = secdata_len; + +	} else +		return -EINVAL; +out:  	return error;  } @@ -146,6 +204,7 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd)  static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)  {  	struct bin_attribute *bin_attr; +	struct sysfs_inode_attrs *iattrs;  	inode->i_private = sysfs_get(sd);  	inode->i_mapping->a_ops = &sysfs_aops; @@ -154,16 +213,20 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)  	inode->i_ino = sd->s_ino;  	lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); -	if (sd->s_iattr) { +	iattrs = sd->s_iattr; +	if (iattrs) {  		/* sysfs_dirent has non-default attributes  		 * get them for the new inode from persistent copy  		 * in sysfs_dirent  		 */ -		set_inode_attr(inode, sd->s_iattr); +		set_inode_attr(inode, &iattrs->ia_iattr); +		if (iattrs->ia_secdata) +			security_inode_notifysecctx(inode, +						iattrs->ia_secdata, +						iattrs->ia_secdata_len);  	} else  		set_default_inode_attr(inode, sd->s_mode); -  	/* initialize inode according to type */  	switch (sysfs_type(sd)) {  	case SYSFS_DIR:  |