From a77846506bb6fdf0a678f92f03ebb95dcc8735b5 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 11 Apr 2014 15:06:56 +0000 Subject: [PATCH] Refactor mounts into pkg to make changes easier Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- .../{nsinit/mount.go => mount/init.go} | 153 +++--------------- libcontainer/mount/msmoveroot.go | 19 +++ libcontainer/mount/nodes/nodes.go | 49 ++++++ libcontainer/mount/pivotroot.go | 31 ++++ libcontainer/mount/ptmx.go | 26 +++ libcontainer/mount/readonly.go | 12 ++ libcontainer/mount/remount.go | 31 ++++ libcontainer/nsinit/execin.go | 5 +- libcontainer/nsinit/init.go | 3 +- 9 files changed, 191 insertions(+), 138 deletions(-) rename libcontainer/{nsinit/mount.go => mount/init.go} (51%) create mode 100644 libcontainer/mount/msmoveroot.go create mode 100644 libcontainer/mount/nodes/nodes.go create mode 100644 libcontainer/mount/pivotroot.go create mode 100644 libcontainer/mount/ptmx.go create mode 100644 libcontainer/mount/readonly.go create mode 100644 libcontainer/mount/remount.go diff --git a/libcontainer/nsinit/mount.go b/libcontainer/mount/init.go similarity index 51% rename from libcontainer/nsinit/mount.go rename to libcontainer/mount/init.go index c85058a..2a5e47a 100644 --- a/libcontainer/nsinit/mount.go +++ b/libcontainer/mount/init.go @@ -1,15 +1,14 @@ // +build linux -package nsinit +package mount import ( "fmt" "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" - "github.com/dotcloud/docker/pkg/libcontainer/console" + "github.com/dotcloud/docker/pkg/libcontainer/mount/nodes" "github.com/dotcloud/docker/pkg/libcontainer/security/restrict" "github.com/dotcloud/docker/pkg/system" - "io/ioutil" "os" "path/filepath" "syscall" @@ -26,13 +25,13 @@ type mount struct { data string } -// setupNewMountNamespace is used to initialize a new mount namespace for an new -// container in the rootfs that is specified. -// -// There is no need to unmount the new mounts because as soon as the mount namespace -// is no longer in use, the mounts will be removed automatically -func setupNewMountNamespace(rootfs, console string, container *libcontainer.Container) error { - flag := syscall.MS_PRIVATE +// InitializeMountNamespace setups up the devices, mount points, and filesystems for use inside a +// new mount namepsace +func InitializeMountNamespace(rootfs, console string, container *libcontainer.Container) error { + var ( + err error + flag = syscall.MS_PRIVATE + ) if container.NoPivotRoot { flag = syscall.MS_SLAVE } @@ -48,7 +47,7 @@ func setupNewMountNamespace(rootfs, console string, container *libcontainer.Cont if err := setupBindmounts(rootfs, container.Mounts); err != nil { return fmt.Errorf("bind mounts %s", err) } - if err := copyDevNodes(rootfs); err != nil { + if err := nodes.CopyN(rootfs, nodes.DefaultNodes); err != nil { return fmt.Errorf("copy dev nodes %s", err) } if restrictionPath := container.Context["restriction_path"]; restrictionPath != "" { @@ -56,7 +55,7 @@ func setupNewMountNamespace(rootfs, console string, container *libcontainer.Cont return fmt.Errorf("restrict %s", err) } } - if err := setupPtmx(rootfs, console, container.Context["mount_label"]); err != nil { + if err := SetupPtmx(rootfs, console, container.Context["mount_label"]); err != nil { return err } if err := system.Chdir(rootfs); err != nil { @@ -64,18 +63,17 @@ func setupNewMountNamespace(rootfs, console string, container *libcontainer.Cont } if container.NoPivotRoot { - if err := rootMsMove(rootfs); err != nil { - return err - } + err = MsMoveRoot(rootfs) } else { - if err := rootPivot(rootfs); err != nil { - return err - } + err = PivotRoot(rootfs) + } + if err != nil { + return err } if container.ReadonlyFs { - if err := system.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil { - return fmt.Errorf("mounting %s as readonly %s", rootfs, err) + if err := SetReadonly(); err != nil { + return fmt.Errorf("set readonly %s", err) } } @@ -84,78 +82,6 @@ func setupNewMountNamespace(rootfs, console string, container *libcontainer.Cont return nil } -// use a pivot root to setup the rootfs -func rootPivot(rootfs string) error { - pivotDir, err := ioutil.TempDir(rootfs, ".pivot_root") - if err != nil { - return fmt.Errorf("can't create pivot_root dir %s", pivotDir, err) - } - if err := system.Pivotroot(rootfs, pivotDir); err != nil { - return fmt.Errorf("pivot_root %s", err) - } - if err := system.Chdir("/"); err != nil { - return fmt.Errorf("chdir / %s", err) - } - // path to pivot dir now changed, update - pivotDir = filepath.Join("/", filepath.Base(pivotDir)) - if err := system.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { - return fmt.Errorf("unmount pivot_root dir %s", err) - } - if err := os.Remove(pivotDir); err != nil { - return fmt.Errorf("remove pivot_root dir %s", err) - } - return nil -} - -// use MS_MOVE and chroot to setup the rootfs -func rootMsMove(rootfs string) error { - if err := system.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil { - return fmt.Errorf("mount move %s into / %s", rootfs, err) - } - if err := system.Chroot("."); err != nil { - return fmt.Errorf("chroot . %s", err) - } - if err := system.Chdir("/"); err != nil { - return fmt.Errorf("chdir / %s", err) - } - return nil -} - -// copyDevNodes mknods the hosts devices so the new container has access to them -func copyDevNodes(rootfs string) error { - oldMask := system.Umask(0000) - defer system.Umask(oldMask) - - for _, node := range []string{ - "null", - "zero", - "full", - "random", - "urandom", - "tty", - } { - if err := copyDevNode(rootfs, node); err != nil { - return err - } - } - return nil -} - -func copyDevNode(rootfs, node string) error { - stat, err := os.Stat(filepath.Join("/dev", node)) - if err != nil { - return err - } - var ( - dest = filepath.Join(rootfs, "dev", node) - st = stat.Sys().(*syscall.Stat_t) - ) - if err := system.Mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) { - return fmt.Errorf("copy %s %s", node, err) - } - return nil -} - // mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts // inside the mount namespace func mountSystem(rootfs string, container *libcontainer.Container) error { @@ -170,49 +96,6 @@ func mountSystem(rootfs string, container *libcontainer.Container) error { return nil } -// setupPtmx adds a symlink to pts/ptmx for /dev/ptmx and -// finishes setting up /dev/console -func setupPtmx(rootfs, consolePath, mountLabel string) error { - ptmx := filepath.Join(rootfs, "dev/ptmx") - if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { - return err - } - if err := os.Symlink("pts/ptmx", ptmx); err != nil { - return fmt.Errorf("symlink dev ptmx %s", err) - } - if consolePath != "" { - if err := console.Setup(rootfs, consolePath, mountLabel); err != nil { - return err - } - } - return nil -} - -// remountProc is used to detach and remount the proc filesystem -// commonly needed with running a new process inside an existing container -func remountProc() error { - if err := system.Unmount("/proc", syscall.MNT_DETACH); err != nil { - return err - } - if err := system.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), ""); err != nil { - return err - } - return nil -} - -func remountSys() error { - if err := system.Unmount("/sys", syscall.MNT_DETACH); err != nil { - if err != syscall.EINVAL { - return err - } - } else { - if err := system.Mount("sysfs", "/sys", "sysfs", uintptr(defaultMountFlags), ""); err != nil { - return err - } - } - return nil -} - func setupBindmounts(rootfs string, bindMounts libcontainer.Mounts) error { for _, m := range bindMounts.OfType("bind") { var ( diff --git a/libcontainer/mount/msmoveroot.go b/libcontainer/mount/msmoveroot.go new file mode 100644 index 0000000..b336c86 --- /dev/null +++ b/libcontainer/mount/msmoveroot.go @@ -0,0 +1,19 @@ +// +build linux + +package mount + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/system" + "syscall" +) + +func MsMoveRoot(rootfs string) error { + if err := system.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil { + return fmt.Errorf("mount move %s into / %s", rootfs, err) + } + if err := system.Chroot("."); err != nil { + return fmt.Errorf("chroot . %s", err) + } + return system.Chdir("/") +} diff --git a/libcontainer/mount/nodes/nodes.go b/libcontainer/mount/nodes/nodes.go new file mode 100644 index 0000000..5022f85 --- /dev/null +++ b/libcontainer/mount/nodes/nodes.go @@ -0,0 +1,49 @@ +// +build linux + +package nodes + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/system" + "os" + "path/filepath" + "syscall" +) + +// Default list of device nodes to copy +var DefaultNodes = []string{ + "null", + "zero", + "full", + "random", + "urandom", + "tty", +} + +// CopyN copies the device node from the host into the rootfs +func CopyN(rootfs string, nodesToCopy []string) error { + oldMask := system.Umask(0000) + defer system.Umask(oldMask) + + for _, node := range nodesToCopy { + if err := Copy(rootfs, node); err != nil { + return err + } + } + return nil +} + +func Copy(rootfs, node string) error { + stat, err := os.Stat(filepath.Join("/dev", node)) + if err != nil { + return err + } + var ( + dest = filepath.Join(rootfs, "dev", node) + st = stat.Sys().(*syscall.Stat_t) + ) + if err := system.Mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) { + return fmt.Errorf("copy %s %s", node, err) + } + return nil +} diff --git a/libcontainer/mount/pivotroot.go b/libcontainer/mount/pivotroot.go new file mode 100644 index 0000000..447f590 --- /dev/null +++ b/libcontainer/mount/pivotroot.go @@ -0,0 +1,31 @@ +// +build linux + +package mount + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/system" + "io/ioutil" + "os" + "path/filepath" + "syscall" +) + +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", pivotDir, err) + } + if err := system.Pivotroot(rootfs, pivotDir); err != nil { + return fmt.Errorf("pivot_root %s", err) + } + if err := system.Chdir("/"); err != nil { + return fmt.Errorf("chdir / %s", err) + } + // path to pivot dir now changed, update + pivotDir = filepath.Join("/", filepath.Base(pivotDir)) + if err := system.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { + return fmt.Errorf("unmount pivot_root dir %s", err) + } + return os.Remove(pivotDir) +} diff --git a/libcontainer/mount/ptmx.go b/libcontainer/mount/ptmx.go new file mode 100644 index 0000000..f6ca534 --- /dev/null +++ b/libcontainer/mount/ptmx.go @@ -0,0 +1,26 @@ +// +build linux + +package mount + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer/console" + "os" + "path/filepath" +) + +func SetupPtmx(rootfs, consolePath, mountLabel string) error { + ptmx := filepath.Join(rootfs, "dev/ptmx") + if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { + return err + } + if err := os.Symlink("pts/ptmx", ptmx); err != nil { + return fmt.Errorf("symlink dev ptmx %s", err) + } + if consolePath != "" { + if err := console.Setup(rootfs, consolePath, mountLabel); err != nil { + return err + } + } + return nil +} diff --git a/libcontainer/mount/readonly.go b/libcontainer/mount/readonly.go new file mode 100644 index 0000000..0658358 --- /dev/null +++ b/libcontainer/mount/readonly.go @@ -0,0 +1,12 @@ +// +build linux + +package mount + +import ( + "github.com/dotcloud/docker/pkg/system" + "syscall" +) + +func SetReadonly() error { + return system.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "") +} diff --git a/libcontainer/mount/remount.go b/libcontainer/mount/remount.go new file mode 100644 index 0000000..3e00509 --- /dev/null +++ b/libcontainer/mount/remount.go @@ -0,0 +1,31 @@ +// +build linux + +package mount + +import ( + "github.com/dotcloud/docker/pkg/system" + "syscall" +) + +func RemountProc() error { + if err := system.Unmount("/proc", syscall.MNT_DETACH); err != nil { + return err + } + if err := system.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), ""); err != nil { + return err + } + return nil +} + +func RemountSys() error { + if err := system.Unmount("/sys", syscall.MNT_DETACH); err != nil { + if err != syscall.EINVAL { + return err + } + } else { + if err := system.Mount("sysfs", "/sys", "sysfs", uintptr(defaultMountFlags), ""); err != nil { + return err + } + } + return nil +} diff --git a/libcontainer/nsinit/execin.go b/libcontainer/nsinit/execin.go index 9017af0..b798810 100644 --- a/libcontainer/nsinit/execin.go +++ b/libcontainer/nsinit/execin.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/pkg/libcontainer/mount" "github.com/dotcloud/docker/pkg/system" "os" "path/filepath" @@ -63,10 +64,10 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s if err := system.Unshare(syscall.CLONE_NEWNS); err != nil { return -1, err } - if err := remountProc(); err != nil { + if err := mount.RemountProc(); err != nil { return -1, fmt.Errorf("remount proc %s", err) } - if err := remountSys(); err != nil { + if err := mount.RemountSys(); err != nil { return -1, fmt.Errorf("remount sys %s", err) } goto dropAndExec diff --git a/libcontainer/nsinit/init.go b/libcontainer/nsinit/init.go index fb3a895..6e6b0e5 100644 --- a/libcontainer/nsinit/init.go +++ b/libcontainer/nsinit/init.go @@ -12,6 +12,7 @@ import ( "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/capabilities" + "github.com/dotcloud/docker/pkg/libcontainer/mount" "github.com/dotcloud/docker/pkg/libcontainer/network" "github.com/dotcloud/docker/pkg/libcontainer/security/apparmor" "github.com/dotcloud/docker/pkg/libcontainer/utils" @@ -61,7 +62,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol label.Init() ns.logger.Println("setup mount namespace") - if err := setupNewMountNamespace(rootfs, console, container); err != nil { + if err := mount.InitializeMountNamespace(rootfs, console, container); err != nil { return fmt.Errorf("setup mount namespace %s", err) } if err := system.Sethostname(container.Hostname); err != nil {