chroot_linux.go 3.4 KB

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