unshare: allow to set a new root

This patch instroduces two new parameters to set the new
root and the new working directory in this new root.

This allows to combine "unshare chroot" in one command,
and doing like this the /proc filesystem is correctly
mounted in the new root with "--mount-proc".

The new parameters are -R, --root and -w, --wd. The names
are the same as for nsenter, except for "-r" that is already
used by "--map-root-user" and replaced by "-R".

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
This commit is contained in:
Laurent Vivier 2018-10-05 13:09:29 +02:00 committed by Karel Zak
parent f8ee3af9fa
commit bf8834d4f4
3 changed files with 38 additions and 6 deletions

View File

@ -33,7 +33,9 @@ _unshare_module()
--propagation
--setgroups
--help
--version"
--version
--root
--wd"
COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
return 0
;;

View File

@ -186,6 +186,12 @@ the GID map becomes writable by unprivileged processes when
.BR \%setgroups (2)
is permanently disabled (with \fBdeny\fR).
.TP
.BR \-R, "\-\-root=\fIdir"
run the command with root directory set to \fIdir\fP.
.TP
.BR \-w, "\-\-wd=\fIdir"
change working directory to \fIdir\fP.
.TP
.BR \-V , " \-\-version"
Display version information and exit.
.TP

View File

@ -269,6 +269,9 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" --propagation slave|shared|private|unchanged\n"
" modify mount propagation in mount namespace\n"), out);
fputs(_(" --setgroups allow|deny control the setgroups syscall in user namespaces\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_(" -R, --root=<dir> run the command with root directory set to <dir>\n"), out);
fputs(_(" -w, --wd=<dir> change working directory to <dir>\n"), out);
fputs(USAGE_SEPARATOR, out);
printf(USAGE_HELP_OPTIONS(27));
@ -283,7 +286,7 @@ int main(int argc, char *argv[])
OPT_MOUNTPROC = CHAR_MAX + 1,
OPT_PROPAGATION,
OPT_SETGROUPS,
OPT_KILLCHILD
OPT_KILLCHILD,
};
static const struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
@ -303,6 +306,8 @@ int main(int argc, char *argv[])
{ "map-root-user", no_argument, NULL, 'r' },
{ "propagation", required_argument, NULL, OPT_PROPAGATION },
{ "setgroups", required_argument, NULL, OPT_SETGROUPS },
{ "root", required_argument, NULL, 'R' },
{ "wd", required_argument, NULL, 'w' },
{ NULL, 0, NULL, 0 }
};
@ -311,6 +316,8 @@ int main(int argc, char *argv[])
int c, forkit = 0, maproot = 0;
int kill_child_signo = 0; /* 0 means --kill-child was not used */
const char *procmnt = NULL;
const char *newroot = NULL;
const char *newdir = NULL;
pid_t pid = 0;
int fds[2];
int status;
@ -323,7 +330,7 @@ int main(int argc, char *argv[])
textdomain(PACKAGE);
atexit(close_stdout);
while ((c = getopt_long(argc, argv, "+fhVmuinpCUr", longopts, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "+fhVmuinpCUrR:w:", longopts, NULL)) != -1) {
switch (c) {
case 'f':
forkit = 1;
@ -392,6 +399,12 @@ int main(int argc, char *argv[])
kill_child_signo = SIGKILL;
}
break;
case 'R':
newroot = optarg;
break;
case 'w':
newdir = optarg;
break;
default:
errtryhelp(EXIT_FAILURE);
}
@ -471,10 +484,21 @@ int main(int argc, char *argv[])
if ((unshare_flags & CLONE_NEWNS) && propagation)
set_propagation(propagation);
if (procmnt &&
(mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 ||
mount("proc", procmnt, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0))
if (newroot) {
if (chroot(newroot) != 0)
err(EXIT_FAILURE,
_("cannot change root directory to '%s'"), newroot);
newdir = newdir ?: "/";
}
if (newdir && chdir(newdir))
err(EXIT_FAILURE, _("cannot chdir to '%s'"), newdir);
if (procmnt) {
if (!newroot && mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0)
err(EXIT_FAILURE, _("umount %s failed"), procmnt);
if (mount("proc", procmnt, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
err(EXIT_FAILURE, _("mount %s failed"), procmnt);
}
if (optind < argc) {
execvp(argv[optind], argv + optind);