pivot_root(2) System Calls Manual pivot_root(2) pivot_root - (libc -lc) #include /* SYS_* */ #include int syscall(SYS_pivot_root, const char *new_root, const char *put_old); : glibc pivot_root() syscall(2). pivot_root() . put_old new_root . CAP_SYS_ADMIN . pivot_root() new_root . ( .) pivot_root() ( ) chdir("/"). : o new_root put_old . o new_root put_old . o put_old new_root "/.." put_old new_root. o new_root "/". . o new_root MS_SHARED put_old MS_SHARED. pivot_root() . o . . -1 errno . pivot_root() stat(2). : EBUSY new_root put_old . ( new_root "/".) EINVAL new_root . EINVAL put_old new_root. EINVAL ( chroot(2) ). EINVAL rootfs (ramfs ) . EINVAL new_root MS_SHARED. EINVAL put_old MS_SHARED. ENOTDIR new_root put_old . EPERM CAP_SYS_ADMIN. . 2.3.41. pivot_root(8) . pivot_root() new_root . ( .) pivot_root() ( initrd(4)) . . pivot_root() . pivot_root() rootfs (ramfs ). rootfs rootfs stdin/stdout/stderr /dev/console init(1) . switch_root(8). pivot_root(".", ".") new_root put_old . pivot-root : chdir(new_root); pivot_root(".", "."); umount2(".", MNT_DETACH); pivot_root() /. (new_root). umount() "." new_root / . : pivot_root() . pivot_root() . new_root pivot_root(). . . pivot_root() clone(2). clone(2) . busybox(1) ( ) . $ mkdir /tmp/rootfs; $ ls -id /tmp/rootfs; # Show inode number of new root directory 319459 /tmp/rootfs $ cp $(which busybox) /tmp/rootfs; $ PS1='bbsh$ ' sudo ./pivot_root_demo /tmp/rootfs /busybox sh; bbsh$ PATH=/; bbsh$ busybox ln busybox ln; bbsh$ ln busybox echo; bbsh$ ln busybox ls; bbsh$ ls; busybox echo ln ls bbsh$ ls -id /; # Compare with inode number above 319459 / bbsh$ echo 'hello world'; hello world /* pivot_root_demo.c */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include 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 path[PATH_MAX]; char **args = arg; char *new_root = args[0]; const char *put_old = "/oldrootfs"; /* Ensure that 'new_root' and its parent mount don't 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) err(EXIT_FAILURE, "mount-MS_PRIVATE"); /* Ensure that 'new_root' is a mount point. */ if (mount(new_root, new_root, NULL, MS_BIND, NULL) == -1) err(EXIT_FAILURE, "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) err(EXIT_FAILURE, "mkdir"); /* And pivot the root filesystem. */ if (pivot_root(new_root, path) == -1) err(EXIT_FAILURE, "pivot_root"); /* Switch the current working directory to "/". */ if (chdir("/") == -1) err(EXIT_FAILURE, "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]); err(EXIT_FAILURE, "execv"); } int main(int argc, char *argv[]) { char *stack; /* Create a child process in a new mount namespace. */ stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); if (stack == MAP_FAILED) err(EXIT_FAILURE, "mmap"); if (clone(child, stack + STACK_SIZE, CLONE_NEWNS | SIGCHLD, &argv[1]) == -1) err(EXIT_FAILURE, "clone"); /* Parent falls through to here; wait for child. */ if (wait(NULL) == -1) err(EXIT_FAILURE, "wait"); exit(EXIT_SUCCESS); } chdir(2), chroot(2), mount(2), stat(2), initrd(4), mount_namespaces(7), pivot_root(8), switch_root(8) 3 . . : . 6.18 8 2026 pivot_root(2)