Skip to content

Commit 8d2451f

Browse files
author
Al Viro
committed
cgroup1: switch to option-by-option parsing
[dhowells should be the author - it's carved out of his patch] Signed-off-by: Al Viro <[email protected]>
1 parent f5dfb53 commit 8d2451f

File tree

3 files changed

+117
-98
lines changed

3 files changed

+117
-98
lines changed

kernel/cgroup/cgroup-internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,15 @@ extern const struct proc_ns_operations cgroupns_operations;
257257
*/
258258
extern struct cftype cgroup1_base_files[];
259259
extern struct kernfs_syscall_ops cgroup1_kf_syscall_ops;
260+
extern const struct fs_parameter_description cgroup1_fs_parameters;
260261

261262
int proc_cgroupstats_show(struct seq_file *m, void *v);
262263
bool cgroup1_ssid_disabled(int ssid);
263264
void cgroup1_pidlist_destroy_all(struct cgroup *cgrp);
264265
void cgroup1_release_agent(struct work_struct *work);
265266
void cgroup1_check_for_release(struct cgroup *cgrp);
267+
int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param);
266268
int cgroup1_get_tree(struct fs_context *fc);
267-
int parse_cgroup1_options(char *data, struct cgroup_fs_context *ctx);
268269
int cgroup1_reconfigure(struct fs_context *ctx);
269270

270271
#endif /* __CGROUP_INTERNAL_H */

kernel/cgroup/cgroup-v1.c

Lines changed: 109 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
#include <linux/delayacct.h>
1414
#include <linux/pid_namespace.h>
1515
#include <linux/cgroupstats.h>
16+
#include <linux/fs_parser.h>
1617

1718
#include <trace/events/cgroup.h>
1819

20+
#define cg_invalf(fc, fmt, ...) ({ pr_err(fmt, ## __VA_ARGS__); -EINVAL; })
21+
1922
/*
2023
* pidlists linger the following amount before being destroyed. The goal
2124
* is avoiding frequent destruction in the middle of consecutive read calls
@@ -906,94 +909,117 @@ static int cgroup1_show_options(struct seq_file *seq, struct kernfs_root *kf_roo
906909
return 0;
907910
}
908911

909-
int parse_cgroup1_options(char *data, struct cgroup_fs_context *ctx)
910-
{
911-
char *token, *o = data;
912-
struct cgroup_subsys *ss;
913-
int i;
912+
enum cgroup1_param {
913+
Opt_all,
914+
Opt_clone_children,
915+
Opt_cpuset_v2_mode,
916+
Opt_name,
917+
Opt_none,
918+
Opt_noprefix,
919+
Opt_release_agent,
920+
Opt_xattr,
921+
};
914922

915-
while ((token = strsep(&o, ",")) != NULL) {
916-
if (!*token)
917-
return -EINVAL;
918-
if (!strcmp(token, "none")) {
919-
/* Explicitly have no subsystems */
920-
ctx->none = true;
921-
continue;
922-
}
923-
if (!strcmp(token, "all")) {
924-
ctx->all_ss = true;
925-
continue;
926-
}
927-
if (!strcmp(token, "noprefix")) {
928-
ctx->flags |= CGRP_ROOT_NOPREFIX;
929-
continue;
930-
}
931-
if (!strcmp(token, "clone_children")) {
932-
ctx->cpuset_clone_children = true;
933-
continue;
934-
}
935-
if (!strcmp(token, "cpuset_v2_mode")) {
936-
ctx->flags |= CGRP_ROOT_CPUSET_V2_MODE;
937-
continue;
938-
}
939-
if (!strcmp(token, "xattr")) {
940-
ctx->flags |= CGRP_ROOT_XATTR;
941-
continue;
942-
}
943-
if (!strncmp(token, "release_agent=", 14)) {
944-
/* Specifying two release agents is forbidden */
945-
if (ctx->release_agent)
946-
return -EINVAL;
947-
ctx->release_agent =
948-
kstrndup(token + 14, PATH_MAX - 1, GFP_KERNEL);
949-
if (!ctx->release_agent)
950-
return -ENOMEM;
951-
continue;
952-
}
953-
if (!strncmp(token, "name=", 5)) {
954-
const char *name = token + 5;
955-
956-
/* blocked by boot param? */
957-
if (cgroup_no_v1_named)
958-
return -ENOENT;
959-
/* Can't specify an empty name */
960-
if (!strlen(name))
961-
return -EINVAL;
962-
/* Must match [\w.-]+ */
963-
for (i = 0; i < strlen(name); i++) {
964-
char c = name[i];
965-
if (isalnum(c))
966-
continue;
967-
if ((c == '.') || (c == '-') || (c == '_'))
968-
continue;
969-
return -EINVAL;
970-
}
971-
/* Specifying two names is forbidden */
972-
if (ctx->name)
973-
return -EINVAL;
974-
ctx->name = kstrndup(name,
975-
MAX_CGROUP_ROOT_NAMELEN - 1,
976-
GFP_KERNEL);
977-
if (!ctx->name)
978-
return -ENOMEM;
923+
static const struct fs_parameter_spec cgroup1_param_specs[] = {
924+
fsparam_flag ("all", Opt_all),
925+
fsparam_flag ("clone_children", Opt_clone_children),
926+
fsparam_flag ("cpuset_v2_mode", Opt_cpuset_v2_mode),
927+
fsparam_string("name", Opt_name),
928+
fsparam_flag ("none", Opt_none),
929+
fsparam_flag ("noprefix", Opt_noprefix),
930+
fsparam_string("release_agent", Opt_release_agent),
931+
fsparam_flag ("xattr", Opt_xattr),
932+
{}
933+
};
979934

980-
continue;
981-
}
935+
const struct fs_parameter_description cgroup1_fs_parameters = {
936+
.name = "cgroup1",
937+
.specs = cgroup1_param_specs,
938+
};
982939

940+
int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
941+
{
942+
struct cgroup_fs_context *ctx = cgroup_fc2context(fc);
943+
struct cgroup_subsys *ss;
944+
struct fs_parse_result result;
945+
int opt, i;
946+
947+
opt = fs_parse(fc, &cgroup1_fs_parameters, param, &result);
948+
if (opt == -ENOPARAM) {
949+
if (strcmp(param->key, "source") == 0) {
950+
fc->source = param->string;
951+
param->string = NULL;
952+
return 0;
953+
}
983954
for_each_subsys(ss, i) {
984-
if (strcmp(token, ss->legacy_name))
955+
if (strcmp(param->key, ss->legacy_name))
985956
continue;
986957
ctx->subsys_mask |= (1 << i);
987-
break;
958+
return 0;
988959
}
989-
if (i == CGROUP_SUBSYS_COUNT)
960+
return cg_invalf(fc, "cgroup1: Unknown subsys name '%s'", param->key);
961+
}
962+
if (opt < 0)
963+
return opt;
964+
965+
switch (opt) {
966+
case Opt_none:
967+
/* Explicitly have no subsystems */
968+
ctx->none = true;
969+
break;
970+
case Opt_all:
971+
ctx->all_ss = true;
972+
break;
973+
case Opt_noprefix:
974+
ctx->flags |= CGRP_ROOT_NOPREFIX;
975+
break;
976+
case Opt_clone_children:
977+
ctx->cpuset_clone_children = true;
978+
break;
979+
case Opt_cpuset_v2_mode:
980+
ctx->flags |= CGRP_ROOT_CPUSET_V2_MODE;
981+
break;
982+
case Opt_xattr:
983+
ctx->flags |= CGRP_ROOT_XATTR;
984+
break;
985+
case Opt_release_agent:
986+
/* Specifying two release agents is forbidden */
987+
if (ctx->release_agent)
988+
return cg_invalf(fc, "cgroup1: release_agent respecified");
989+
ctx->release_agent = param->string;
990+
param->string = NULL;
991+
break;
992+
case Opt_name:
993+
/* blocked by boot param? */
994+
if (cgroup_no_v1_named)
990995
return -ENOENT;
996+
/* Can't specify an empty name */
997+
if (!param->size)
998+
return cg_invalf(fc, "cgroup1: Empty name");
999+
if (param->size > MAX_CGROUP_ROOT_NAMELEN - 1)
1000+
return cg_invalf(fc, "cgroup1: Name too long");
1001+
/* Must match [\w.-]+ */
1002+
for (i = 0; i < param->size; i++) {
1003+
char c = param->string[i];
1004+
if (isalnum(c))
1005+
continue;
1006+
if ((c == '.') || (c == '-') || (c == '_'))
1007+
continue;
1008+
return cg_invalf(fc, "cgroup1: Invalid name");
1009+
}
1010+
/* Specifying two names is forbidden */
1011+
if (ctx->name)
1012+
return cg_invalf(fc, "cgroup1: name respecified");
1013+
ctx->name = param->string;
1014+
param->string = NULL;
1015+
break;
9911016
}
9921017
return 0;
9931018
}
9941019

995-
static int check_cgroupfs_options(struct cgroup_fs_context *ctx)
1020+
static int check_cgroupfs_options(struct fs_context *fc)
9961021
{
1022+
struct cgroup_fs_context *ctx = cgroup_fc2context(fc);
9971023
u16 mask = U16_MAX;
9981024
u16 enabled = 0;
9991025
struct cgroup_subsys *ss;
@@ -1018,7 +1044,7 @@ static int check_cgroupfs_options(struct cgroup_fs_context *ctx)
10181044
if (ctx->all_ss) {
10191045
/* Mutually exclusive option 'all' + subsystem name */
10201046
if (ctx->subsys_mask)
1021-
return -EINVAL;
1047+
return cg_invalf(fc, "cgroup1: subsys name conflicts with all");
10221048
/* 'all' => select all the subsystems */
10231049
ctx->subsys_mask = enabled;
10241050
}
@@ -1028,19 +1054,19 @@ static int check_cgroupfs_options(struct cgroup_fs_context *ctx)
10281054
* empty hierarchies must have a name).
10291055
*/
10301056
if (!ctx->subsys_mask && !ctx->name)
1031-
return -EINVAL;
1057+
return cg_invalf(fc, "cgroup1: Need name or subsystem set");
10321058

10331059
/*
10341060
* Option noprefix was introduced just for backward compatibility
10351061
* with the old cpuset, so we allow noprefix only if mounting just
10361062
* the cpuset subsystem.
10371063
*/
10381064
if ((ctx->flags & CGRP_ROOT_NOPREFIX) && (ctx->subsys_mask & mask))
1039-
return -EINVAL;
1065+
return cg_invalf(fc, "cgroup1: noprefix used incorrectly");
10401066

10411067
/* Can't specify "none" and some subsystems */
10421068
if (ctx->subsys_mask && ctx->none)
1043-
return -EINVAL;
1069+
return cg_invalf(fc, "cgroup1: none used incorrectly");
10441070

10451071
return 0;
10461072
}
@@ -1056,7 +1082,7 @@ int cgroup1_reconfigure(struct fs_context *fc)
10561082
cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
10571083

10581084
/* See what subsystems are wanted */
1059-
ret = check_cgroupfs_options(ctx);
1085+
ret = check_cgroupfs_options(fc);
10601086
if (ret)
10611087
goto out_unlock;
10621088

@@ -1070,7 +1096,7 @@ int cgroup1_reconfigure(struct fs_context *fc)
10701096
/* Don't allow flags or name to change at remount */
10711097
if ((ctx->flags ^ root->flags) ||
10721098
(ctx->name && strcmp(ctx->name, root->name))) {
1073-
pr_err("option or name mismatch, new: 0x%x \"%s\", old: 0x%x \"%s\"\n",
1099+
cg_invalf(fc, "option or name mismatch, new: 0x%x \"%s\", old: 0x%x \"%s\"",
10741100
ctx->flags, ctx->name ?: "", root->flags, root->name);
10751101
ret = -EINVAL;
10761102
goto out_unlock;
@@ -1125,7 +1151,7 @@ int cgroup1_get_tree(struct fs_context *fc)
11251151
cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
11261152

11271153
/* First find the desired set of subsystems */
1128-
ret = check_cgroupfs_options(ctx);
1154+
ret = check_cgroupfs_options(fc);
11291155
if (ret)
11301156
goto out_unlock;
11311157

@@ -1192,7 +1218,7 @@ int cgroup1_get_tree(struct fs_context *fc)
11921218
* can't create new one without subsys specification.
11931219
*/
11941220
if (!ctx->subsys_mask && !ctx->none) {
1195-
ret = -EINVAL;
1221+
ret = cg_invalf(fc, "cgroup1: No subsys list or none specified");
11961222
goto out_unlock;
11971223
}
11981224

kernel/cgroup/cgroup.c

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,15 +2083,6 @@ static int cgroup_parse_monolithic(struct fs_context *fc, void *data)
20832083
return parse_cgroup_root_flags(data, &ctx->flags);
20842084
}
20852085

2086-
static int cgroup1_parse_monolithic(struct fs_context *fc, void *data)
2087-
{
2088-
struct cgroup_fs_context *ctx = cgroup_fc2context(fc);
2089-
2090-
if (data)
2091-
security_sb_eat_lsm_opts(data, &fc->security);
2092-
return parse_cgroup1_options(data, ctx);
2093-
}
2094-
20952086
static int cgroup_get_tree(struct fs_context *fc)
20962087
{
20972088
struct cgroup_namespace *ns = current->nsproxy->cgroup_ns;
@@ -2124,7 +2115,7 @@ static const struct fs_context_operations cgroup_fs_context_ops = {
21242115

21252116
static const struct fs_context_operations cgroup1_fs_context_ops = {
21262117
.free = cgroup_fs_context_free,
2127-
.parse_monolithic = cgroup1_parse_monolithic,
2118+
.parse_param = cgroup1_parse_param,
21282119
.get_tree = cgroup1_get_tree,
21292120
.reconfigure = cgroup1_reconfigure,
21302121
};
@@ -2175,10 +2166,11 @@ static void cgroup_kill_sb(struct super_block *sb)
21752166
}
21762167

21772168
struct file_system_type cgroup_fs_type = {
2178-
.name = "cgroup",
2179-
.init_fs_context = cgroup_init_fs_context,
2180-
.kill_sb = cgroup_kill_sb,
2181-
.fs_flags = FS_USERNS_MOUNT,
2169+
.name = "cgroup",
2170+
.init_fs_context = cgroup_init_fs_context,
2171+
.parameters = &cgroup1_fs_parameters,
2172+
.kill_sb = cgroup_kill_sb,
2173+
.fs_flags = FS_USERNS_MOUNT,
21822174
};
21832175

21842176
static struct file_system_type cgroup2_fs_type = {

0 commit comments

Comments
 (0)