|
@@ -276,7 +276,8 @@ inline int cgroup_is_removed(const struct cgroup *cgrp)
|
|
|
|
|
|
/* bits in struct cgroupfs_root flags field */
|
|
|
enum {
|
|
|
- ROOT_NOPREFIX, /* mounted subsystems have no named prefix */
|
|
|
+ ROOT_NOPREFIX, /* mounted subsystems have no named prefix */
|
|
|
+ ROOT_XATTR, /* supports extended attributes */
|
|
|
};
|
|
|
|
|
|
static int cgroup_is_releasable(const struct cgroup *cgrp)
|
|
@@ -913,15 +914,19 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
|
|
|
*/
|
|
|
BUG_ON(!list_empty(&cgrp->pidlists));
|
|
|
|
|
|
+ simple_xattrs_free(&cgrp->xattrs);
|
|
|
+
|
|
|
kfree_rcu(cgrp, rcu_head);
|
|
|
} else {
|
|
|
struct cfent *cfe = __d_cfe(dentry);
|
|
|
struct cgroup *cgrp = dentry->d_parent->d_fsdata;
|
|
|
+ struct cftype *cft = cfe->type;
|
|
|
|
|
|
WARN_ONCE(!list_empty(&cfe->node) &&
|
|
|
cgrp != &cgrp->root->top_cgroup,
|
|
|
"cfe still linked for %s\n", cfe->type->name);
|
|
|
kfree(cfe);
|
|
|
+ simple_xattrs_free(&cft->xattrs);
|
|
|
}
|
|
|
iput(inode);
|
|
|
}
|
|
@@ -1140,6 +1145,8 @@ static int cgroup_show_options(struct seq_file *seq, struct dentry *dentry)
|
|
|
seq_printf(seq, ",%s", ss->name);
|
|
|
if (test_bit(ROOT_NOPREFIX, &root->flags))
|
|
|
seq_puts(seq, ",noprefix");
|
|
|
+ if (test_bit(ROOT_XATTR, &root->flags))
|
|
|
+ seq_puts(seq, ",xattr");
|
|
|
if (strlen(root->release_agent_path))
|
|
|
seq_printf(seq, ",release_agent=%s", root->release_agent_path);
|
|
|
if (clone_children(&root->top_cgroup))
|
|
@@ -1208,6 +1215,10 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
|
|
opts->clone_children = true;
|
|
|
continue;
|
|
|
}
|
|
|
+ if (!strcmp(token, "xattr")) {
|
|
|
+ set_bit(ROOT_XATTR, &opts->flags);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
if (!strncmp(token, "release_agent=", 14)) {
|
|
|
/* Specifying two release agents is forbidden */
|
|
|
if (opts->release_agent)
|
|
@@ -1425,6 +1436,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
|
|
|
mutex_init(&cgrp->pidlist_mutex);
|
|
|
INIT_LIST_HEAD(&cgrp->event_list);
|
|
|
spin_lock_init(&cgrp->event_list_lock);
|
|
|
+ simple_xattrs_init(&cgrp->xattrs);
|
|
|
}
|
|
|
|
|
|
static void init_cgroup_root(struct cgroupfs_root *root)
|
|
@@ -1769,6 +1781,8 @@ static void cgroup_kill_sb(struct super_block *sb) {
|
|
|
mutex_unlock(&cgroup_root_mutex);
|
|
|
mutex_unlock(&cgroup_mutex);
|
|
|
|
|
|
+ simple_xattrs_free(&cgrp->xattrs);
|
|
|
+
|
|
|
kill_litter_super(sb);
|
|
|
cgroup_drop_root(root);
|
|
|
}
|
|
@@ -2575,6 +2589,64 @@ static int cgroup_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
return simple_rename(old_dir, old_dentry, new_dir, new_dentry);
|
|
|
}
|
|
|
|
|
|
+static struct simple_xattrs *__d_xattrs(struct dentry *dentry)
|
|
|
+{
|
|
|
+ if (S_ISDIR(dentry->d_inode->i_mode))
|
|
|
+ return &__d_cgrp(dentry)->xattrs;
|
|
|
+ else
|
|
|
+ return &__d_cft(dentry)->xattrs;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int xattr_enabled(struct dentry *dentry)
|
|
|
+{
|
|
|
+ struct cgroupfs_root *root = dentry->d_sb->s_fs_info;
|
|
|
+ return test_bit(ROOT_XATTR, &root->flags);
|
|
|
+}
|
|
|
+
|
|
|
+static bool is_valid_xattr(const char *name)
|
|
|
+{
|
|
|
+ if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
|
|
|
+ !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN))
|
|
|
+ return true;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static int cgroup_setxattr(struct dentry *dentry, const char *name,
|
|
|
+ const void *val, size_t size, int flags)
|
|
|
+{
|
|
|
+ if (!xattr_enabled(dentry))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (!is_valid_xattr(name))
|
|
|
+ return -EINVAL;
|
|
|
+ return simple_xattr_set(__d_xattrs(dentry), name, val, size, flags);
|
|
|
+}
|
|
|
+
|
|
|
+static int cgroup_removexattr(struct dentry *dentry, const char *name)
|
|
|
+{
|
|
|
+ if (!xattr_enabled(dentry))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (!is_valid_xattr(name))
|
|
|
+ return -EINVAL;
|
|
|
+ return simple_xattr_remove(__d_xattrs(dentry), name);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t cgroup_getxattr(struct dentry *dentry, const char *name,
|
|
|
+ void *buf, size_t size)
|
|
|
+{
|
|
|
+ if (!xattr_enabled(dentry))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if (!is_valid_xattr(name))
|
|
|
+ return -EINVAL;
|
|
|
+ return simple_xattr_get(__d_xattrs(dentry), name, buf, size);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t cgroup_listxattr(struct dentry *dentry, char *buf, size_t size)
|
|
|
+{
|
|
|
+ if (!xattr_enabled(dentry))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ return simple_xattr_list(__d_xattrs(dentry), buf, size);
|
|
|
+}
|
|
|
+
|
|
|
static const struct file_operations cgroup_file_operations = {
|
|
|
.read = cgroup_file_read,
|
|
|
.write = cgroup_file_write,
|
|
@@ -2583,11 +2655,22 @@ static const struct file_operations cgroup_file_operations = {
|
|
|
.release = cgroup_file_release,
|
|
|
};
|
|
|
|
|
|
+static const struct inode_operations cgroup_file_inode_operations = {
|
|
|
+ .setxattr = cgroup_setxattr,
|
|
|
+ .getxattr = cgroup_getxattr,
|
|
|
+ .listxattr = cgroup_listxattr,
|
|
|
+ .removexattr = cgroup_removexattr,
|
|
|
+};
|
|
|
+
|
|
|
static const struct inode_operations cgroup_dir_inode_operations = {
|
|
|
.lookup = cgroup_lookup,
|
|
|
.mkdir = cgroup_mkdir,
|
|
|
.rmdir = cgroup_rmdir,
|
|
|
.rename = cgroup_rename,
|
|
|
+ .setxattr = cgroup_setxattr,
|
|
|
+ .getxattr = cgroup_getxattr,
|
|
|
+ .listxattr = cgroup_listxattr,
|
|
|
+ .removexattr = cgroup_removexattr,
|
|
|
};
|
|
|
|
|
|
static struct dentry *cgroup_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
|
@@ -2635,6 +2718,7 @@ static int cgroup_create_file(struct dentry *dentry, umode_t mode,
|
|
|
} else if (S_ISREG(mode)) {
|
|
|
inode->i_size = 0;
|
|
|
inode->i_fop = &cgroup_file_operations;
|
|
|
+ inode->i_op = &cgroup_file_inode_operations;
|
|
|
}
|
|
|
d_instantiate(dentry, inode);
|
|
|
dget(dentry); /* Extra count - pin the dentry in core */
|
|
@@ -2695,7 +2779,7 @@ static umode_t cgroup_file_mode(const struct cftype *cft)
|
|
|
}
|
|
|
|
|
|
static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
|
|
- const struct cftype *cft)
|
|
|
+ struct cftype *cft)
|
|
|
{
|
|
|
struct dentry *dir = cgrp->dentry;
|
|
|
struct cgroup *parent = __d_cgrp(dir);
|
|
@@ -2705,6 +2789,8 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
|
|
umode_t mode;
|
|
|
char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
|
|
|
|
|
|
+ simple_xattrs_init(&cft->xattrs);
|
|
|
+
|
|
|
/* does @cft->flags tell us to skip creation on @cgrp? */
|
|
|
if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent)
|
|
|
return 0;
|
|
@@ -2745,9 +2831,9 @@ out:
|
|
|
}
|
|
|
|
|
|
static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
|
|
- const struct cftype cfts[], bool is_add)
|
|
|
+ struct cftype cfts[], bool is_add)
|
|
|
{
|
|
|
- const struct cftype *cft;
|
|
|
+ struct cftype *cft;
|
|
|
int err, ret = 0;
|
|
|
|
|
|
for (cft = cfts; cft->name[0] != '\0'; cft++) {
|
|
@@ -2781,7 +2867,7 @@ static void cgroup_cfts_prepare(void)
|
|
|
}
|
|
|
|
|
|
static void cgroup_cfts_commit(struct cgroup_subsys *ss,
|
|
|
- const struct cftype *cfts, bool is_add)
|
|
|
+ struct cftype *cfts, bool is_add)
|
|
|
__releases(&cgroup_mutex) __releases(&cgroup_cft_mutex)
|
|
|
{
|
|
|
LIST_HEAD(pending);
|
|
@@ -2832,7 +2918,7 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss,
|
|
|
* function currently returns 0 as long as @cfts registration is successful
|
|
|
* even if some file creation attempts on existing cgroups fail.
|
|
|
*/
|
|
|
-int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts)
|
|
|
+int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
|
|
|
{
|
|
|
struct cftype_set *set;
|
|
|
|
|
@@ -2862,7 +2948,7 @@ EXPORT_SYMBOL_GPL(cgroup_add_cftypes);
|
|
|
* Returns 0 on successful unregistration, -ENOENT if @cfts is not
|
|
|
* registered with @ss.
|
|
|
*/
|
|
|
-int cgroup_rm_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts)
|
|
|
+int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
|
|
|
{
|
|
|
struct cftype_set *set;
|
|
|
|