cgroups/devices: enable devpath semantics for cgroup2 device controller

parent 1b7b7923
...@@ -2394,6 +2394,31 @@ __cgfsng_ops static int cgfsng_get(struct cgroup_ops *ops, const char *filename, ...@@ -2394,6 +2394,31 @@ __cgfsng_ops static int cgfsng_get(struct cgroup_ops *ops, const char *filename,
return ret; return ret;
} }
static int device_cgroup_parse_access(struct device_item *device, const char *val)
{
for (int count = 0; count < 3; count++, val++) {
switch (*val) {
case 'r':
device->access[count] = *val;
break;
case 'w':
device->access[count] = *val;
break;
case 'm':
device->access[count] = *val;
break;
case '\n':
case '\0':
count = 3;
break;
default:
return ret_errno(EINVAL);
}
}
return 0;
}
static int device_cgroup_rule_parse(struct device_item *device, const char *key, static int device_cgroup_rule_parse(struct device_item *device, const char *key,
const char *val) const char *val)
{ {
...@@ -2476,27 +2501,8 @@ static int device_cgroup_rule_parse(struct device_item *device, const char *key, ...@@ -2476,27 +2501,8 @@ static int device_cgroup_rule_parse(struct device_item *device, const char *key,
} }
if (!isspace(*val)) if (!isspace(*val))
return -1; return -1;
for (val++, count = 0; count < 3; count++, val++) {
switch (*val) {
case 'r':
device->access[count] = *val;
break;
case 'w':
device->access[count] = *val;
break;
case 'm':
device->access[count] = *val;
break;
case '\n':
case '\0':
count = 3;
break;
default:
return -1;
}
}
return 0; return device_cgroup_parse_access(device, ++val);
} }
/* Called externally (i.e. from 'lxc-cgroup') to set new cgroup limits. Here we /* Called externally (i.e. from 'lxc-cgroup') to set new cgroup limits. Here we
...@@ -2559,19 +2565,19 @@ __cgfsng_ops static int cgfsng_set(struct cgroup_ops *ops, ...@@ -2559,19 +2565,19 @@ __cgfsng_ops static int cgfsng_set(struct cgroup_ops *ops,
* line. Return <0 on error. Dest is a preallocated buffer long enough to hold * line. Return <0 on error. Dest is a preallocated buffer long enough to hold
* the output. * the output.
*/ */
static int convert_devpath(const char *invalue, char *dest) static int device_cgroup_rule_parse_devpath(struct device_item *device,
const char *devpath)
{ {
__do_free char *path = NULL; __do_free char *path = NULL;
int n_parts;
char *p, type;
unsigned long minor, major;
struct stat sb;
int ret = -EINVAL;
char *mode = NULL; char *mode = NULL;
int n_parts, ret;
char *p;
struct stat sb;
path = must_copy_string(invalue); path = must_copy_string(devpath);
/* Read path followed by mode. Ignore any trailing text. /*
* Read path followed by mode. Ignore any trailing text.
* A ' # comment' would be legal. Technically other text is not * A ' # comment' would be legal. Technically other text is not
* legal, we could check for that if we cared to. * legal, we could check for that if we cared to.
*/ */
...@@ -2591,43 +2597,59 @@ static int convert_devpath(const char *invalue, char *dest) ...@@ -2591,43 +2597,59 @@ static int convert_devpath(const char *invalue, char *dest)
mode = p; mode = p;
if (*p == '\0') if (*p == '\0')
goto out; return ret_set_errno(-1, EINVAL);
} }
if (device_cgroup_parse_access(device, mode) < 0)
return -1;
if (n_parts == 1) if (n_parts == 1)
goto out; return ret_set_errno(-1, EINVAL);
ret = stat(path, &sb); ret = stat(path, &sb);
if (ret < 0) if (ret < 0)
goto out; return ret_set_errno(-1, errno);
mode_t m = sb.st_mode & S_IFMT; mode_t m = sb.st_mode & S_IFMT;
switch (m) { switch (m) {
case S_IFBLK: case S_IFBLK:
type = 'b'; device->type = 'b';
break; break;
case S_IFCHR: case S_IFCHR:
type = 'c'; device->type = 'c';
break; break;
default: default:
ERROR("Unsupported device type %i for \"%s\"", m, path); return log_error_errno(-1, EINVAL,
ret = -EINVAL; "Unsupported device type %i for \"%s\"",
goto out; m, path);
} }
major = MAJOR(sb.st_rdev); device->major = MAJOR(sb.st_rdev);
minor = MINOR(sb.st_rdev); device->minor = MINOR(sb.st_rdev);
ret = snprintf(dest, 50, "%c %lu:%lu %s", type, major, minor, mode); device->allow = 1;
if (ret < 0 || ret >= 50) { device->global_rule = LXC_BPF_DEVICE_CGROUP_LOCAL_RULE;
ERROR("Error on configuration value \"%c %lu:%lu %s\" (max 50 "
"chars)", type, major, minor, mode);
ret = -ENAMETOOLONG;
goto out;
}
ret = 0;
out: return 0;
return ret; }
static int convert_devpath(const char *invalue, char *dest)
{
struct device_item device = {0};
int ret;
ret = device_cgroup_rule_parse_devpath(&device, invalue);
if (ret < 0)
return -1;
ret = snprintf(dest, 50, "%c %d:%d %s", device.type, device.major,
device.minor, device.access);
if (ret < 0 || ret >= 50)
return log_error_errno(-1,
ENAMETOOLONG, "Error on configuration value \"%c %d:%d %s\" (max 50 chars)",
device.type, device.major, device.minor,
device.access);
return 0;
} }
/* Called from setup_limits - here we have the container's cgroup_data because /* Called from setup_limits - here we have the container's cgroup_data because
...@@ -2737,7 +2759,10 @@ static int bpf_device_cgroup_prepare(struct cgroup_ops *ops, ...@@ -2737,7 +2759,10 @@ static int bpf_device_cgroup_prepare(struct cgroup_ops *ops,
struct device_item device_item = {0}; struct device_item device_item = {0};
int ret; int ret;
ret = device_cgroup_rule_parse(&device_item, key, val); if (strcmp("devices.allow", key) == 0 && *val == '/')
ret = device_cgroup_rule_parse_devpath(&device_item, val);
else
ret = device_cgroup_rule_parse(&device_item, key, val);
if (ret < 0) if (ret < 0)
return log_error_errno(-1, EINVAL, return log_error_errno(-1, EINVAL,
"Failed to parse device string %s=%s", "Failed to parse device string %s=%s",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment