diff --git a/man2/pivot_root.2 b/man2/pivot_root.2 index 9506dc7d1..a11a31a03 100644 --- a/man2/pivot_root.2 +++ b/man2/pivot_root.2 @@ -195,6 +195,140 @@ Some of the more obscure uses of .BR pivot_root () may quickly lead to insanity. +.SH EXAMPLE +.PP +The program below demonstrates the use of +.BR pivot_root () +inside a mount namespace that is created using +.BR clone (2). +After pivoting to the root directory named in the program's +first command-line argument, the child created by +.BR clone (2) +then executes the program named in the remaining command-line arguments. +.PP +We demonstrate the program by creating a directory that will serve as +the new root filesystem and placing a copy of the (statically linked) +.BR busybox (1) +executable in that directory. +.PP +.in +4n +.EX +$ \fBmkdir /tmp/rootfs\fP +$ \fBls \-id /tmp/rootfs\fP # Show inode number of new root directory +319459 /tmp/rootfs +$ \fBcp $(which busybox) /tmp/rootfs\fP +$ \fBPS1='bbsh$ ' sudo ./pivot_root_demo /tmp/rootfs /busybox sh\fP +bbsh$ \fBPATH=/\fP +bbsh$ \fBbusybox ln busybox ln\fP +bbsh$ \fBln busybox echo\fP +bbsh$ \fBln busybox ls\fP +bbsh$ \fBls\fP +busybox echo ln ls +bbsh$ \fBls \-id /\fP # Compare with inode number above +319459 / +bbsh$ \fBecho \(aqhello world\(aq\fP +hello world +.EE +.in +.SS Program source +\& +.PP +.EX +/* pivot_root_demo.c */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \e + } while (0) + +static int +pivot_root(const char *new_root, const char *put_old) +{ + return syscall(SYS_pivot_root, new_root, put_old); +} + +#define STACK_SIZE (1024 * 1024) + +static int /* Startup function for cloned child */ +child(void *arg) +{ + char **args = arg; + char *new_root = args[0]; + const char *put_old = "/oldrootfs"; + char path[PATH_MAX]; + + /* Ensure that \(aqnew_root\(aq and its parent mount don\(aqt have + shared propagation (which would cause pivot_root() to + return an error), and prevent propagation of mount + events to the initial mount namespace */ + + if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) == 1) + errExit("mount\-MS_PRIVATE"); + + /* Ensure that \(aqnew_root\(aq is a mount point */ + + if (mount(new_root, new_root, NULL, MS_BIND, NULL) == \-1) + errExit("mount\-MS_BIND"); + + /* Create directory to which old root will be pivoted */ + + snprintf(path, sizeof(path), "%s/%s", new_root, put_old); + if (mkdir(path, 0777) == \-1) + errExit("mkdir"); + + /* And pivot the root filesystem */ + + if (pivot_root(new_root, path) == \-1) + errExit("pivot_root"); + + /* Switch the current working working directory to "/" */ + + if (chdir("/") == \-1) + errExit("chdir"); + + /* Unmount old root and remove mount point */ + + if (umount2(put_old, MNT_DETACH) == \-1) + perror("umount2"); + if (rmdir(put_old) == \-1) + perror("rmdir"); + + /* Execute the command specified in argv[1]... */ + + execv(args[1], &args[1]); + errExit("execv"); +} + +int +main(int argc, char *argv[]) +{ + /* Create a child process in a new mount namespace */ + + char *stack = malloc(STACK_SIZE); + if (stack == NULL) + errExit("malloc"); + + if (clone(child, stack + STACK_SIZE, + CLONE_NEWNS | SIGCHLD, &argv[1]) == \-1) + errExit("clone"); + + /* Parent falls through to here; wait for child */ + + if (wait(NULL) == \-1) + errExit("wait"); + + exit(EXIT_SUCCESS); +} +.EE .SH SEE ALSO .BR chdir (2), .BR chroot (2),