github.com/DaoCloud/dao@v0.0.0-20161212064103-c3dbfd13ee36/pkg/chrootarchive/chroot_linux.go (about) 1 package chrootarchive 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "syscall" 9 10 "github.com/docker/docker/pkg/mount" 11 ) 12 13 // chroot on linux uses pivot_root instead of chroot 14 // pivot_root takes a new root and an old root. 15 // Old root must be a sub-dir of new root, it is where the current rootfs will reside after the call to pivot_root. 16 // New root is where the new rootfs is set to. 17 // Old root is removed after the call to pivot_root so it is no longer available under the new root. 18 // This is similar to how libcontainer sets up a container's rootfs 19 func chroot(path string) (err error) { 20 if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil { 21 return fmt.Errorf("Error creating mount namespace before pivot: %v", err) 22 } 23 24 // make everything in new ns private 25 if err := mount.MakeRPrivate("/"); err != nil { 26 return err 27 } 28 // ensure path is a mountpoint 29 if err := mount.MakePrivate(path); err != nil { 30 return err 31 } 32 33 // setup oldRoot for pivot_root 34 pivotDir, err := ioutil.TempDir(path, ".pivot_root") 35 if err != nil { 36 return fmt.Errorf("Error setting up pivot dir: %v", err) 37 } 38 39 var mounted bool 40 defer func() { 41 if mounted { 42 // make sure pivotDir is not mounted before we try to remove it 43 if errCleanup := syscall.Unmount(pivotDir, syscall.MNT_DETACH); errCleanup != nil { 44 if err == nil { 45 err = errCleanup 46 } 47 return 48 } 49 } 50 51 errCleanup := os.Remove(pivotDir) 52 // pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful 53 // because we already cleaned it up on failed pivot_root 54 if errCleanup != nil && !os.IsNotExist(errCleanup) { 55 errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup) 56 if err == nil { 57 err = errCleanup 58 } 59 } 60 }() 61 62 if err := syscall.PivotRoot(path, pivotDir); err != nil { 63 // If pivot fails, fall back to the normal chroot after cleaning up temp dir 64 if err := os.Remove(pivotDir); err != nil { 65 return fmt.Errorf("Error cleaning up after failed pivot: %v", err) 66 } 67 return realChroot(path) 68 } 69 mounted = true 70 71 // This is the new path for where the old root (prior to the pivot) has been moved to 72 // This dir contains the rootfs of the caller, which we need to remove so it is not visible during extraction 73 pivotDir = filepath.Join("/", filepath.Base(pivotDir)) 74 75 if err := syscall.Chdir("/"); err != nil { 76 return fmt.Errorf("Error changing to new root: %v", err) 77 } 78 79 // Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host 80 if err := syscall.Mount("", pivotDir, "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil { 81 return fmt.Errorf("Error making old root private after pivot: %v", err) 82 } 83 84 // Now unmount the old root so it's no longer visible from the new root 85 if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { 86 return fmt.Errorf("Error while unmounting old root after pivot: %v", err) 87 } 88 mounted = false 89 90 return nil 91 } 92 93 func realChroot(path string) error { 94 if err := syscall.Chroot(path); err != nil { 95 return fmt.Errorf("Error after fallback to chroot: %v", err) 96 } 97 if err := syscall.Chdir("/"); err != nil { 98 return fmt.Errorf("Error changing to new root after chroot: %v", err) 99 } 100 return nil 101 }