func pivotRoot(rootfs, pivotBaseDir string) error { if pivotBaseDir == "" { pivotBaseDir = "/" } tmpDir := filepath.Join(rootfs, pivotBaseDir) if err := os.MkdirAll(tmpDir, 0755); err != nil { return fmt.Errorf("can't create tmp dir %s, error %v", tmpDir, err) } pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root") if err != nil { return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err) } if err := syscall.PivotRoot(rootfs, pivotDir); err != nil { return fmt.Errorf("pivot_root %s", err) } if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %s", err) } // path to pivot dir now changed, update pivotDir = filepath.Join(pivotBaseDir, filepath.Base(pivotDir)) if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("unmount pivot_root dir %s", err) } return os.Remove(pivotDir) }
func pivotRoot(rootfs, pivotBaseDir string) error { if pivotBaseDir == "" { pivotBaseDir = "/" } tmpDir := filepath.Join(rootfs, pivotBaseDir) if err := os.MkdirAll(tmpDir, 0755); err != nil { return fmt.Errorf("can't create tmp dir %s, error %v", tmpDir, err) } pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root") if err != nil { return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err) } if err := syscall.PivotRoot(rootfs, pivotDir); err != nil { return fmt.Errorf("pivot_root %s", err) } if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %s", err) } // path to pivot dir now changed, update pivotDir = filepath.Join(pivotBaseDir, filepath.Base(pivotDir)) // Make pivotDir rprivate to make sure any of the unmounts don't // propagate to parent. if err := syscall.Mount("", pivotDir, "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil { return err } if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("unmount pivot_root dir %s", err) } return os.Remove(pivotDir) }
func (r *RootFS) Enter() error { if err := checkDirectory(r.Root); err != nil { return err } if err := syscall.Mount(r.Root, r.Root, "", uintptr(syscall.MS_BIND|syscall.MS_REC), ""); err != nil { return fmt.Errorf("system: failed to bind mount the root filesystem onto itself: %s", err) } if err := os.Chdir(r.Root); err != nil { return fmt.Errorf("system: failed to change directory into the bind mounted rootfs dir: %s", err) } if err := os.MkdirAll("tmp/garden-host", 0700); err != nil { return fmt.Errorf("system: mkdir: %s", err) } if err := syscall.PivotRoot(".", "tmp/garden-host"); err != nil { return fmt.Errorf("system: failed to pivot root: %s", err) } if err := os.Chdir("/"); err != nil { return fmt.Errorf("system: failed to chdir to new root: %s", err) } return nil }
func pivotRoot(root string) error { // we need this to satisfy restriction: // "new_root and put_old must not be on the same filesystem as the current root" if err := syscall.Mount(root, root, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { return fmt.Errorf("Mount rootfs to itself error: %v", err) } // create rootfs/.pivot_root as path for old_root pivotDir := filepath.Join(root, ".pivot_root") if err := os.Mkdir(pivotDir, 0777); err != nil { return err } logrus.Debugf("Pivot root dir: %s", pivotDir) logrus.Debugf("Pivot root to %s", root) // pivot_root to rootfs, now old_root is mounted in rootfs/.pivot_root // mounts from it still can be seen in `mount` if err := syscall.PivotRoot(root, pivotDir); err != nil { return fmt.Errorf("pivot_root %v", err) } // change working directory to / // it is recommendation from man-page if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %v", err) } // path to pivot root now changed, update pivotDir = filepath.Join("/", ".pivot_root") // umount rootfs/.pivot_root(which is now /.pivot_root) with all submounts // now we have only mounts that we mounted ourself in `mount` if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("unmount pivot_root dir %v", err) } // remove temporary directory return os.Remove(pivotDir) }
func main() { flag.Usage = usage flag.Parse() if flag.NArg() < 2 { usage() } ck(syscall.PivotRoot(flag.Arg(0), flag.Arg(1))) }
// pivotRoot will call pivot_root such that rootfs becomes the new root // filesystem, and everything else is cleaned up. func pivotRoot(rootfs string) error { // While the documentation may claim otherwise, pivot_root(".", ".") is // actually valid. What this results in is / being the new root but // /proc/self/cwd being the old root. Since we can play around with the cwd // with pivot_root this allows us to pivot without creating directories in // the rootfs. Shout-outs to the LXC developers for giving us this idea. oldroot, err := syscall.Open("/", syscall.O_DIRECTORY|syscall.O_RDONLY, 0) if err != nil { return err } defer syscall.Close(oldroot) newroot, err := syscall.Open(rootfs, syscall.O_DIRECTORY|syscall.O_RDONLY, 0) if err != nil { return err } defer syscall.Close(newroot) // Change to the new root so that the pivot_root actually acts on it. if err := syscall.Fchdir(newroot); err != nil { return err } if err := syscall.PivotRoot(".", "."); err != nil { return fmt.Errorf("pivot_root %s", err) } // Currently our "." is oldroot (according to the current kernel code). // However, purely for safety, we will fchdir(oldroot) since there isn't // really any guarantee from the kernel what /proc/self/cwd will be after a // pivot_root(2). if err := syscall.Fchdir(oldroot); err != nil { return err } // Make oldroot rprivate to make sure our unmounts don't propagate to the // host (and thus bork the machine). if err := syscall.Mount("", ".", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil { return err } // Preform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd. if err := syscall.Unmount(".", syscall.MNT_DETACH); err != nil { return err } // Switch back to our shiny new root. if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %s", err) } return nil }
/* PivotRoot() */ func PivotRoot(rootfs string) error { pivotRoot := filepath.Join(rootfs, ".pivot_root") if e := BindMount("/dev", filepath.Join(rootfs, "dev")); e != nil { return fmt.Errorf("Failed to bind mount dev: %v", e) } if e := BindMount("/proc", filepath.Join(rootfs, "proc")); e != nil { return fmt.Errorf("Failed to bind mount dev: %v", e) } if e := BindMount("/sys", filepath.Join(rootfs, "sys")); e != nil { return fmt.Errorf("Failed to mount sysfs: %v", e) } if e := BindMount(rootfs, rootfs); e != nil { return fmt.Errorf("Failed to bind mount rootfs: %v", e) } if _, e := os.Stat(pivotRoot); os.IsNotExist(e) { if e := os.Mkdir(pivotRoot, 755); e != nil { return fmt.Errorf("Failed to make .pivot_root dir: %v", e) } } if e := syscall.PivotRoot(rootfs, pivotRoot); e != nil { return fmt.Errorf("Failed to pivot_root: %v", e) } if e := syscall.Chdir("/"); e != nil { return fmt.Errorf("Failed to cd to /: %v", e) } if e := syscall.Unmount("/.pivot_root", syscall.MNT_DETACH); e != nil { return fmt.Errorf("Failed to unmount pivot_root dir: %v", e) } if e := os.Remove("/.pivot_root"); e != nil { return fmt.Errorf("Failed to rmdir /.pivot_root: %v", e) } return nil }
func child() { // Create temp file for NS path := "/proc/self/ns/net" must(syscall.Mount("fs/rootfs", "fs/rootfs", "", syscall.MS_BIND, "")) must(os.MkdirAll("fs/rootfs/oldrootfs", 0700)) must(syscall.PivotRoot("fs/rootfs", "fs/rootfs/oldrootfs")) must(os.Chdir("/")) must(syscall.Unmount("/oldrootfs", syscall.MNT_DETACH)) must(os.Remove("/oldrootfs")) must(syscall.Mount("proc", "/proc", "proc", 0, "")) // Some devices syscall.Mknod("/dev/null", 0666, Mkdev(int64(1), int64(3))) syscall.Mknod("/dev/zero", 0666, Mkdev(int64(1), int64(5))) syscall.Mknod("/dev/random", 0666, Mkdev(int64(1), int64(8))) syscall.Mknod("/dev/urandom", 0666, Mkdev(int64(1), int64(9))) fmt.Println("Pid:", os.Getpid()) ns, err := netns.GetFromPath(path) if err != nil { fmt.Println("cant find ns") } must(netns.Set(ns)) routingUp() cmd := exec.Command(os.Args[2], os.Args[3:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Println("ERROR", err) os.Exit(1) } routingDown() }
func PivotRoot(rootfs string) error { pivotDir, err := ioutil.TempDir(rootfs, ".pivot_root") if err != nil { return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err) } if err := syscall.PivotRoot(rootfs, pivotDir); err != nil { return fmt.Errorf("pivot_root %s", err) } if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %s", err) } // path to pivot dir now changed, update pivotDir = filepath.Join("/", filepath.Base(pivotDir)) if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("unmount pivot_root dir %s", err) } return os.Remove(pivotDir) }
func Pivotroot(newroot, putold string) error { return syscall.PivotRoot(newroot, putold) }
// chroot on linux uses pivot_root instead of chroot // pivot_root takes a new root and an old root. // Old root must be a sub-dir of new root, it is where the current rootfs will reside after the call to pivot_root. // New root is where the new rootfs is set to. // Old root is removed after the call to pivot_root so it is no longer available under the new root. // This is similar to how libcontainer sets up a container's rootfs func chroot(path string) (err error) { // Create new mount namespace so mounts don't leak if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil { return fmt.Errorf("Error creating mount namespace before pivot: %v", err) } // path must be a different fs for pivot_root, so bind-mount to itself to ensure this if err := syscall.Mount(path, path, "", syscall.MS_BIND, ""); err != nil { return fmt.Errorf("Error mounting pivot dir before pivot: %v", err) } // setup oldRoot for pivot_root pivotDir, err := ioutil.TempDir(path, ".pivot_root") if err != nil { return fmt.Errorf("Error setting up pivot dir: %v", err) } var mounted bool defer func() { if mounted { // make sure pivotDir is not mounted before we try to remove it if errCleanup := syscall.Unmount(pivotDir, syscall.MNT_DETACH); errCleanup != nil { if err == nil { err = errCleanup } return } } errCleanup := os.Remove(pivotDir) // pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful // but we already cleaned it up on failed pivot_root if errCleanup != nil && !os.IsNotExist(errCleanup) { errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup) if err == nil { err = errCleanup } } }() if err := syscall.PivotRoot(path, pivotDir); err != nil { // If pivot fails, fall back to the normal chroot after cleaning up temp dir for pivot_root if err := os.Remove(pivotDir); err != nil { return fmt.Errorf("Error cleaning up after failed pivot: %v", err) } return realChroot(path) } mounted = true // This is the new path for where the old root (prior to the pivot) has been moved to // This dir contains the rootfs of the caller, which we need to remove so it is not visible during extraction pivotDir = filepath.Join("/", filepath.Base(pivotDir)) if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("Error changing to new root: %v", err) } // Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host if err := syscall.Mount("", pivotDir, "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil { return fmt.Errorf("Error making old root private after pivot: %v", err) } // Now unmount the old root so it's no longer visible from the new root if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("Error while unmounting old root after pivot: %v", err) } mounted = false return nil }