/* * chroot_ns.c * Author: Serge Hallyn * Date: Jan 25, 2005 * * This version acts as "chroot" using namespaces. * * Usage: * chroot_ns -u /mnt/d6 mnt * This will create a new filesystem namespace, make /mnt/d6 the root * of the filesystem, place the old root under /mnt and immediately * unmount it, then run /bin/sh in the new filesystem. * * Note that pivot_root requires the new root to be under a different * vfsmount. If you get the following error: * pivot_root: Device or resource busy * then try the following command first: * * mount --bind * * Now you should be able to call chroot_ns . * * Copyright (C) 2004 International Business Machines * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #ifndef CLONE_NEWNS #define CLONE_NEWNS 0x00020000 #endif #ifndef MNT_DETACH #define MNT_DETACH 0x00000002 #endif #define MAX_PATH 256 static inline _syscall2(int, clone, int, flags, int, foo) static _syscall2(int,pivot_root,const char *,new_root,const char *,put_old) void usage(char *cmd) { printf("Usage: %s [-u] [] []\n", cmd); printf(" Perform under a new namespace with \n"); printf(" as the root of the filesystem.\n"); printf(" If -u is specified, the old root will be unmounted before" " is executed.\n"); printf(" is relative to the old root."); printf(" If unspecified, is '/mnt'.\n"); printf(" If unspecified, is '/bin/sh'.\n"); exit(-EINVAL); } #define OLD_ROOT "mnt" #define CMD "/bin/sh" int main(int argc, char *argv[]) { int pid = clone(CLONE_NEWNS | SIGCHLD,0); int ret; char *new_root, *old_root, *cmd, *argv0; char full_oldroot[MAX_PATH]; int do_umount; if (pid == -1) { fprintf(stderr, "Permission denied on clone.\n"); fprintf(stderr, "You must have CAP_SYS_ADMIN to clone a" " fs namespace.\n"); exit(-1); } if (pid != 0) { waitpid(pid, &ret, 0); exit(-1); } argv0 = argv[0]; if (argc > 1 && strcmp(argv[1], "-u") == 0) { do_umount = 1; argv++; argc--; } else do_umount = 0; if (argc < 2 || strcmp(argv[1], "-h") == 0) usage(argv0); new_root = argv[1]; if (argc > 2) old_root = argv[2]; else old_root = OLD_ROOT; if (argc > 3) cmd = argv[3]; else cmd = CMD; if (strlen(old_root) + strlen(new_root) >= MAX_PATH-1) { printf("paths too long.\n"); return -1; } snprintf(full_oldroot, MAX_PATH, "%s/%s", new_root, old_root); /* jump into the new root directory */ printf("going into %s\n", new_root); ret = chdir(new_root); if (ret) { perror("chdir"); exit(2); } /* pivot root */ printf("switching %s and %s\n", new_root, full_oldroot); ret = pivot_root(new_root, full_oldroot); if (ret) { perror("pivot_root"); printf("Try \"mount --bind %s %s\"\n", new_root, new_root); exit(ret); } /* unmount if requested */ if (do_umount) { ret = umount2(old_root, MNT_DETACH); if (ret) { perror("umount"); exit(2); } } /* Execute the command */ execl(cmd, cmd, NULL); perror("execl"); fprintf(stderr, "Cannot exec %s.\n", cmd); exit(-1); }