1. Change the root file system to /dev/hda1 from an interactive shell:
    2. mount /dev/hda1 /new-root
    3. cd /new-root
    4. pivot_root . old-root
    5. exec chroot . sh <dev/console >dev/console 2>&1
    6. umount /old-root

    因此执行pivot_root时,内部执行逻辑是:

    1. 第一步,把当前的rootfs mount到一个目录(比如old-root)
    2. 第二部,把进程的rootfs设置为新的

    所以需要在pivot_root之后,umount old-root。

    1. // pivotRoot will call pivot_root such that rootfs becomes the new root
    2. // filesystem, and everything else is cleaned up.
    3. func pivotRoot(rootfs string) error {
    4. // While the documentation may claim otherwise, pivot_root(".", ".") is
    5. // actually valid. What this results in is / being the new root but
    6. // /proc/self/cwd being the old root. Since we can play around with the cwd
    7. // with pivot_root this allows us to pivot without creating directories in
    8. // the rootfs. Shout-outs to the LXC developers for giving us this idea.
    9. oldroot, err := unix.Open("/", unix.O_DIRECTORY|unix.O_RDONLY, 0)
    10. if err != nil {
    11. return err
    12. }
    13. defer unix.Close(oldroot) //nolint: errcheck
    14. newroot, err := unix.Open(rootfs, unix.O_DIRECTORY|unix.O_RDONLY, 0)
    15. if err != nil {
    16. return err
    17. }
    18. defer unix.Close(newroot) //nolint: errcheck
    19. // Change to the new root so that the pivot_root actually acts on it.
    20. if err := unix.Fchdir(newroot); err != nil {
    21. return err
    22. }
    23. // "/" being the new root , /proc/self/cwd being the old root
    24. // https://unix.stackexchange.com/questions/571823/strange-behaviour-of-pivot-root-in-mount-namespace
    25. // You can call pivot_root(".", ".") which avoids the need for a directory to put the old root into.
    26. if err := unix.PivotRoot(".", "."); err != nil {
    27. return fmt.Errorf("pivot_root %s", err)
    28. }
    29. // Currently our "." is oldroot (according to the current kernel code).
    30. // However, purely for safety, we will fchdir(oldroot) since there isn't
    31. // really any guarantee from the kernel what /proc/self/cwd will be after a
    32. // pivot_root(2).
    33. if err := unix.Fchdir(oldroot); err != nil {
    34. return err
    35. }
    36. // Make oldroot rslave to make sure our unmounts don't propagate to the
    37. // host (and thus bork the machine). We don't use rprivate because this is
    38. // known to cause issues due to races where we still have a reference to a
    39. // mount while a process in the host namespace are trying to operate on
    40. // something they think has no mounts (devicemapper in particular).
    41. // 修改oldroot的mount属性
    42. if err := unix.Mount("", ".", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
    43. return err
    44. }
    45. // Preform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd.
    46. if err := unix.Unmount(".", unix.MNT_DETACH); err != nil {
    47. return err
    48. }
    49. // Switch back to our shiny new root.
    50. if err := unix.Chdir("/"); err != nil {
    51. return fmt.Errorf("chdir / %s", err)
    52. }
    53. return nil
    54. }