diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 916070b3f2562..eb0a26ef35bf4 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -959,6 +959,71 @@ static int setup_volatile_yes( return r; } +static int setup_volatile_overlay( + const char *directory, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + _cleanup_free_ char *buf = NULL, *escaped_directory = NULL, *escaped_upper = NULL, *escaped_work = NULL; + char template[] = "/tmp/nspawn-volatile-XXXXXX"; + const char *upper, *work, *options; + bool tmpfs_mounted = false; + int r; + + assert(directory); + + /* --volatile=overlay means we mount an overlayfs to the root dir. */ + + if (!mkdtemp(template)) + return log_error_errno(errno, "Failed to create temporary directory: %m"); + + options = "mode=755"; + r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf); + if (r < 0) + goto finish; + if (r > 0) + options = buf; + + r = mount_verbose(LOG_ERR, "tmpfs", template, "tmpfs", MS_STRICTATIME, options); + if (r < 0) + goto finish; + + tmpfs_mounted = true; + + upper = strjoina(template, "/upper"); + work = strjoina(template, "/work"); + + if (mkdir(upper, 0755) < 0) { + r = log_error_errno(errno, "Failed to create %s: %m", upper); + goto finish; + } + if (mkdir(work, 0755) < 0) { + r = log_error_errno(errno, "Failed to create %s: %m", work); + goto finish; + } + + /* And now, let's overmount the root dir with an overlayfs that uses the root dir as lower dir. It's kinda nice + * that the kernel allows us to do that without going through some mount point rearrangements. */ + + escaped_directory = shell_escape(directory, ",:"); + escaped_upper = shell_escape(upper, ",:"); + escaped_work = shell_escape(work, ",:"); + if (!escaped_directory || !escaped_upper || !escaped_work) { + r = -ENOMEM; + goto finish; + } + + options = strjoina("lowerdir=", escaped_directory, ",upperdir=", escaped_upper, ",workdir=", escaped_work); + r = mount_verbose(LOG_ERR, "overlay", directory, "overlay", 0, options); + +finish: + if (tmpfs_mounted) + (void) umount_verbose(template); + + (void) rmdir(template); + return r; +} + int setup_volatile_mode( const char *directory, VolatileMode mode, @@ -973,6 +1038,9 @@ int setup_volatile_mode( case VOLATILE_STATE: return setup_volatile_state(directory, userns, uid_shift, uid_range, selinux_apifs_context); + case VOLATILE_OVERLAY: + return setup_volatile_overlay(directory, userns, uid_shift, uid_range, selinux_apifs_context); + default: return 0; } diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index bb070e1b35989..0bdfc7677a449 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1431,9 +1431,9 @@ static int setup_timezone(const char *dest) { if (IN_SET(arg_timezone, TIMEZONE_AUTO, TIMEZONE_SYMLINK)) { r = readlink_malloc("/etc/localtime", &p); if (r == -ENOENT && arg_timezone == TIMEZONE_AUTO) - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? TIMEZONE_OFF : TIMEZONE_DELETE; + m = arg_read_only && IN_SET(arg_volatile_mode, VOLATILE_NO, VOLATILE_STATE) ? TIMEZONE_OFF : TIMEZONE_DELETE; else if (r == -EINVAL && arg_timezone == TIMEZONE_AUTO) /* regular file? */ - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? TIMEZONE_BIND : TIMEZONE_COPY; + m = arg_read_only && IN_SET(arg_volatile_mode, VOLATILE_NO, VOLATILE_STATE) ? TIMEZONE_BIND : TIMEZONE_COPY; else if (r < 0) { log_warning_errno(r, "Failed to read host's /etc/localtime symlink, not updating container timezone: %m"); /* To handle warning, delete /etc/localtime and replace it with a symbolic link to a time zone data @@ -1444,7 +1444,7 @@ static int setup_timezone(const char *dest) { */ return 0; } else if (arg_timezone == TIMEZONE_AUTO) - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? TIMEZONE_BIND : TIMEZONE_SYMLINK; + m = arg_read_only && IN_SET(arg_volatile_mode, VOLATILE_NO, VOLATILE_STATE) ? TIMEZONE_BIND : TIMEZONE_SYMLINK; else m = arg_timezone; } else @@ -1606,11 +1606,11 @@ static int setup_resolv_conf(const char *dest) { if (arg_private_network) m = RESOLV_CONF_OFF; else if (have_resolv_conf(STATIC_RESOLV_CONF) > 0 && resolved_listening() > 0) - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? RESOLV_CONF_BIND_STATIC : RESOLV_CONF_COPY_STATIC; + m = arg_read_only && IN_SET(arg_volatile_mode, VOLATILE_NO, VOLATILE_STATE) ? RESOLV_CONF_BIND_STATIC : RESOLV_CONF_COPY_STATIC; else if (have_resolv_conf("/etc/resolv.conf") > 0) - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? RESOLV_CONF_BIND_HOST : RESOLV_CONF_COPY_HOST; + m = arg_read_only && IN_SET(arg_volatile_mode, VOLATILE_NO, VOLATILE_STATE) ? RESOLV_CONF_BIND_HOST : RESOLV_CONF_COPY_HOST; else - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? RESOLV_CONF_OFF : RESOLV_CONF_DELETE; + m = arg_read_only && IN_SET(arg_volatile_mode, VOLATILE_NO, VOLATILE_STATE) ? RESOLV_CONF_OFF : RESOLV_CONF_DELETE; } else m = arg_resolv_conf; diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c index 4d75bc0e96e2e..917ebfa4e4384 100644 --- a/src/shared/volatile-util.c +++ b/src/shared/volatile-util.c @@ -39,6 +39,7 @@ static const char* const volatile_mode_table[_VOLATILE_MODE_MAX] = { [VOLATILE_NO] = "no", [VOLATILE_YES] = "yes", [VOLATILE_STATE] = "state", + [VOLATILE_OVERLAY] = "overlay", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(volatile_mode, VolatileMode, VOLATILE_YES); diff --git a/src/shared/volatile-util.h b/src/shared/volatile-util.h index 8761c44ab8553..2d31bb11749d3 100644 --- a/src/shared/volatile-util.h +++ b/src/shared/volatile-util.h @@ -5,6 +5,7 @@ typedef enum VolatileMode { VOLATILE_NO, VOLATILE_YES, VOLATILE_STATE, + VOLATILE_OVERLAY, _VOLATILE_MODE_MAX, _VOLATILE_MODE_INVALID = -1 } VolatileMode;