|
@@ -18,6 +18,8 @@
|
|
#include <linux/capability.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched.h>
|
|
|
|
+#include <linux/xattr.h>
|
|
|
|
+#include <linux/security.h>
|
|
#include "sysfs.h"
|
|
#include "sysfs.h"
|
|
|
|
|
|
extern struct super_block * sysfs_sb;
|
|
extern struct super_block * sysfs_sb;
|
|
@@ -35,6 +37,7 @@ static struct backing_dev_info sysfs_backing_dev_info = {
|
|
|
|
|
|
static const struct inode_operations sysfs_inode_operations ={
|
|
static const struct inode_operations sysfs_inode_operations ={
|
|
.setattr = sysfs_setattr,
|
|
.setattr = sysfs_setattr,
|
|
|
|
+ .setxattr = sysfs_setxattr,
|
|
};
|
|
};
|
|
|
|
|
|
int __init sysfs_inode_init(void)
|
|
int __init sysfs_inode_init(void)
|
|
@@ -42,18 +45,37 @@ int __init sysfs_inode_init(void)
|
|
return bdi_init(&sysfs_backing_dev_info);
|
|
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)
|
|
int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
|
|
{
|
|
{
|
|
struct inode * inode = dentry->d_inode;
|
|
struct inode * inode = dentry->d_inode;
|
|
struct sysfs_dirent * sd = dentry->d_fsdata;
|
|
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;
|
|
unsigned int ia_valid = iattr->ia_valid;
|
|
int error;
|
|
int error;
|
|
|
|
|
|
if (!sd)
|
|
if (!sd)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- sd_iattr = sd->s_iattr;
|
|
|
|
|
|
+ sd_attrs = sd->s_iattr;
|
|
|
|
|
|
error = inode_change_ok(inode, iattr);
|
|
error = inode_change_ok(inode, iattr);
|
|
if (error)
|
|
if (error)
|
|
@@ -65,42 +87,77 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
|
|
if (error)
|
|
if (error)
|
|
return error;
|
|
return error;
|
|
|
|
|
|
- if (!sd_iattr) {
|
|
|
|
|
|
+ if (!sd_attrs) {
|
|
/* setting attributes for the first time, allocate now */
|
|
/* 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;
|
|
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 */
|
|
|
|
-
|
|
|
|
- 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 (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
|
|
|
|
- mode &= ~S_ISGID;
|
|
|
|
- sd_iattr->ia_mode = sd->s_mode = mode;
|
|
|
|
- }
|
|
|
|
|
|
+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 (!sd)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (!sd->s_iattr)
|
|
|
|
+ sd->s_iattr = sysfs_init_inode_attrs(sd);
|
|
|
|
+ if (!sd->s_iattr)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ 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;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -146,6 +203,7 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd)
|
|
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
|
|
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
|
|
{
|
|
{
|
|
struct bin_attribute *bin_attr;
|
|
struct bin_attribute *bin_attr;
|
|
|
|
+ struct sysfs_inode_attrs *iattrs;
|
|
|
|
|
|
inode->i_private = sysfs_get(sd);
|
|
inode->i_private = sysfs_get(sd);
|
|
inode->i_mapping->a_ops = &sysfs_aops;
|
|
inode->i_mapping->a_ops = &sysfs_aops;
|
|
@@ -154,16 +212,20 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
|
|
inode->i_ino = sd->s_ino;
|
|
inode->i_ino = sd->s_ino;
|
|
lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
|
|
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
|
|
/* sysfs_dirent has non-default attributes
|
|
* get them for the new inode from persistent copy
|
|
* get them for the new inode from persistent copy
|
|
* in sysfs_dirent
|
|
* 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
|
|
} else
|
|
set_default_inode_attr(inode, sd->s_mode);
|
|
set_default_inode_attr(inode, sd->s_mode);
|
|
|
|
|
|
-
|
|
|
|
/* initialize inode according to type */
|
|
/* initialize inode according to type */
|
|
switch (sysfs_type(sd)) {
|
|
switch (sysfs_type(sd)) {
|
|
case SYSFS_DIR:
|
|
case SYSFS_DIR:
|