123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- package chrootarchive
- import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "syscall"
- "github.com/docker/docker/pkg/mount"
- rsystem "github.com/opencontainers/runc/libcontainer/system"
- )
- // 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) {
- // if the engine is running in a user namespace we need to use actual chroot
- if rsystem.RunningInUserNS() {
- return realChroot(path)
- }
- if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil {
- return fmt.Errorf("Error creating mount namespace before pivot: %v", err)
- }
- // make everything in new ns private
- if err := mount.MakeRPrivate("/"); err != nil {
- return err
- }
- if mounted, _ := mount.Mounted(path); !mounted {
- if err := mount.Mount(path, path, "bind", "rbind,rw"); err != nil {
- return realChroot(path)
- }
- }
- // 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
- // because 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
- 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
- }
- func realChroot(path string) error {
- if err := syscall.Chroot(path); err != nil {
- return fmt.Errorf("Error after fallback to chroot: %v", err)
- }
- if err := syscall.Chdir("/"); err != nil {
- return fmt.Errorf("Error changing to new root after chroot: %v", err)
- }
- return nil
- }
|