diff options
author | Alban Crequy <alban@endocode.com> | 2015-03-31 17:14:48 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-03-31 17:21:03 +0200 |
commit | 81f5049b7c35752182e855cdb06d707db92d7ac8 (patch) | |
tree | 8043f5c94ac2053b0f6296b1ec61329fb9409932 | |
parent | 4f923a1984476de3441922ee5bf7102ebdd250ef (diff) |
nspawn: fallback on bind mount when mknod fails
Some systems abusively restrict mknod, even when the device node already
exists in /dev. This is unfortunate because it prevents systemd-nspawn
from creating the basic devices in /dev in the container.
This patch implements a workaround: when mknod fails, fallback on bind
mounts.
Additionally, /dev/console was created with a mknod with the same
major/minor as /dev/null before bind mounting a pts on it. This patch
removes the mknod and creates an empty regular file instead.
In order to test this patch, I used the following configuration, which I
think should replicate the system with the abusive restriction on mknod:
# grep devices /proc/self/cgroup
4:devices:/user.slice/restrict
# cat /sys/fs/cgroup/devices/user.slice/restrict/devices.list
c 1:9 r
c 5:2 rw
c 136:* rw
# systemd-nspawn --register=false -D .
v2:
- remove "bind", it is not needed since there is already MS_BIND
v3:
- fix error management when calling touch()
- fix lowercase in error message
-rw-r--r-- | src/nspawn/nspawn.c | 29 |
1 files changed, 16 insertions, 13 deletions
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index b8301410cd..7e56cf2056 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1449,8 +1449,18 @@ static int copy_devnodes(const char *dest) { return -r; } - if (mknod(to, st.st_mode, st.st_rdev) < 0) - return log_error_errno(errno, "mknod(%s) failed: %m", to); + if (mknod(to, st.st_mode, st.st_rdev) < 0) { + if (errno != EPERM) + return log_error_errno(errno, "mknod(%s) failed: %m", to); + + /* Some systems abusively restrict mknod but + * allow bind mounts. */ + r = touch(to); + if (r < 0) + return log_error_errno(r, "touch (%s) failed: %m", to); + if (mount(from, to, NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Both mknod and bind mount (%s) failed: %m", to); + } if (arg_userns && arg_uid_shift != UID_INVALID) if (lchown(to, arg_uid_shift, arg_uid_shift) < 0) @@ -1481,7 +1491,6 @@ static int setup_ptmx(const char *dest) { static int setup_dev_console(const char *dest, const char *console) { _cleanup_umask_ mode_t u; const char *to; - struct stat st; int r; assert(dest); @@ -1489,24 +1498,18 @@ static int setup_dev_console(const char *dest, const char *console) { u = umask(0000); - if (stat("/dev/null", &st) < 0) - return log_error_errno(errno, "Failed to stat /dev/null: %m"); - r = chmod_and_chown(console, 0600, 0, 0); if (r < 0) return log_error_errno(r, "Failed to correct access mode for TTY: %m"); /* We need to bind mount the right tty to /dev/console since * ptys can only exist on pts file systems. To have something - * to bind mount things on we create a device node first, and - * use /dev/null for that since we the cgroups device policy - * allows us to create that freely, while we cannot create - * /dev/console. (Note that the major minor doesn't actually - * matter here, since we mount it over anyway). */ + * to bind mount things on we create a empty regular file. */ to = strjoina(dest, "/dev/console"); - if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) - return log_error_errno(errno, "mknod() for /dev/console failed: %m"); + r = touch(to); + if (r < 0) + return log_error_errno(r, "touch() for /dev/console failed: %m"); if (mount(console, to, NULL, MS_BIND, NULL) < 0) return log_error_errno(errno, "Bind mount for /dev/console failed: %m"); |