diff --git a/mount/init.go b/mount/init.go index 05ab334c..ea2b7327 100644 --- a/mount/init.go +++ b/mount/init.go @@ -8,7 +8,6 @@ import ( "path/filepath" "syscall" - "github.com/docker/docker/pkg/symlink" "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/mount/nodes" ) @@ -31,24 +30,34 @@ func InitializeMountNamespace(rootfs, console string, sysReadonly bool, mountCon err error flag = syscall.MS_PRIVATE ) + if mountConfig.NoPivotRoot { flag = syscall.MS_SLAVE } + if err := syscall.Mount("", "/", "", uintptr(flag|syscall.MS_REC), ""); err != nil { return fmt.Errorf("mounting / with flags %X %s", (flag | syscall.MS_REC), err) } + if err := syscall.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { return fmt.Errorf("mouting %s as bind %s", rootfs, err) } + if err := mountSystem(rootfs, sysReadonly, mountConfig); err != nil { return fmt.Errorf("mount system %s", err) } - if err := setupBindmounts(rootfs, mountConfig); err != nil { - return fmt.Errorf("bind mounts %s", err) + + // apply any user specified mounts within the new mount namespace + for _, m := range mountConfig.Mounts { + if err := m.Mount(rootfs, mountConfig.MountLabel); err != nil { + return err + } } + if err := nodes.CreateDeviceNodes(rootfs, mountConfig.DeviceNodes); err != nil { return fmt.Errorf("create device nodes %s", err) } + if err := SetupPtmx(rootfs, console, mountConfig.MountLabel); err != nil { return err } @@ -72,6 +81,7 @@ func InitializeMountNamespace(rootfs, console string, sysReadonly bool, mountCon } else { err = PivotRoot(rootfs) } + if err != nil { return err } @@ -90,7 +100,7 @@ func InitializeMountNamespace(rootfs, console string, sysReadonly bool, mountCon // mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts // inside the mount namespace func mountSystem(rootfs string, sysReadonly bool, mountConfig *MountConfig) error { - for _, m := range newSystemMounts(rootfs, mountConfig.MountLabel, sysReadonly, mountConfig.Mounts) { + for _, m := range newSystemMounts(rootfs, mountConfig.MountLabel, sysReadonly) { if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) { return fmt.Errorf("mkdirall %s %s", m.path, err) } @@ -151,56 +161,9 @@ func setupDevSymlinks(rootfs string) error { return nil } -func setupBindmounts(rootfs string, mountConfig *MountConfig) error { - bindMounts := mountConfig.Mounts - for _, m := range bindMounts.OfType("bind") { - var ( - flags = syscall.MS_BIND | syscall.MS_REC - dest = filepath.Join(rootfs, m.Destination) - ) - if !m.Writable { - flags = flags | syscall.MS_RDONLY - } - - stat, err := os.Stat(m.Source) - if err != nil { - return err - } - - dest, err = symlink.FollowSymlinkInScope(dest, rootfs) - if err != nil { - return err - } - - if err := createIfNotExists(dest, stat.IsDir()); err != nil { - return fmt.Errorf("Creating new bind-mount target, %s", err) - } - - if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags), ""); err != nil { - return fmt.Errorf("mounting %s into %s %s", m.Source, dest, err) - } - if !m.Writable { - if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags|syscall.MS_REMOUNT), ""); err != nil { - return fmt.Errorf("remounting %s into %s %s", m.Source, dest, err) - } - } - if m.Relabel != "" { - if err := label.Relabel(m.Source, mountConfig.MountLabel, m.Relabel); err != nil { - return fmt.Errorf("relabeling %s to %s %s", m.Source, mountConfig.MountLabel, err) - } - } - if m.Private { - if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { - return fmt.Errorf("mounting %s private %s", dest, err) - } - } - } - return nil -} - // TODO: this is crappy right now and should be cleaned up with a better way of handling system and // standard bind mounts allowing them to be more dynamic -func newSystemMounts(rootfs, mountLabel string, sysReadonly bool, mounts Mounts) []mount { +func newSystemMounts(rootfs, mountLabel string, sysReadonly bool) []mount { systemMounts := []mount{ {source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags}, {source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: label.FormatMountLabel("mode=755", mountLabel)}, @@ -212,6 +175,7 @@ func newSystemMounts(rootfs, mountLabel string, sysReadonly bool, mounts Mounts) if sysReadonly { sysMountFlags |= syscall.MS_RDONLY } + systemMounts = append(systemMounts, mount{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: sysMountFlags}) return systemMounts diff --git a/mount/mount.go b/mount/mount.go new file mode 100644 index 00000000..6a982824 --- /dev/null +++ b/mount/mount.go @@ -0,0 +1,79 @@ +package mount + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + + "github.com/docker/docker/pkg/symlink" + "github.com/docker/libcontainer/label" +) + +type Mount struct { + Type string `json:"type,omitempty"` + Source string `json:"source,omitempty"` // Source path, in the host namespace + Destination string `json:"destination,omitempty"` // Destination path, in the container + Writable bool `json:"writable,omitempty"` + Relabel string `json:"relabel,omitempty"` // Relabel source if set, "z" indicates shared, "Z" indicates unshared + Private bool `json:"private,omitempty"` +} + +func (m *Mount) Mount(rootfs, mountLabel string) error { + switch m.Type { + case "bind": + return m.bindMount(rootfs, mountLabel) + default: + return fmt.Errorf("unsupported mount type %s for %s", m.Type, m.Destination) + } +} + +func (m *Mount) bindMount(rootfs, mountLabel string) error { + var ( + flags = syscall.MS_BIND | syscall.MS_REC + dest = filepath.Join(rootfs, m.Destination) + ) + + if !m.Writable { + flags = flags | syscall.MS_RDONLY + } + + stat, err := os.Stat(m.Source) + if err != nil { + return err + } + + // FIXME: (crosbymichael) This is not belong here and should be done a layer above + dest, err = symlink.FollowSymlinkInScope(dest, rootfs) + if err != nil { + return err + } + + if err := createIfNotExists(dest, stat.IsDir()); err != nil { + return fmt.Errorf("creating new bind-mount target, %s", err) + } + + if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags), ""); err != nil { + return fmt.Errorf("mounting %s into %s %s", m.Source, dest, err) + } + + if !m.Writable { + if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags|syscall.MS_REMOUNT), ""); err != nil { + return fmt.Errorf("remounting %s into %s %s", m.Source, dest, err) + } + } + + if m.Relabel != "" { + if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil { + return fmt.Errorf("relabeling %s to %s %s", m.Source, mountLabel, err) + } + } + + if m.Private { + if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { + return fmt.Errorf("mounting %s private %s", dest, err) + } + } + + return nil +} diff --git a/mount/types.go b/mount/mount_config.go similarity index 59% rename from mount/types.go rename to mount/mount_config.go index 063bbac1..eef9b8ce 100644 --- a/mount/types.go +++ b/mount/mount_config.go @@ -6,6 +6,8 @@ import ( "github.com/docker/libcontainer/devices" ) +var ErrUnsupported = errors.New("Unsupported method") + type MountConfig struct { // NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs // This is a common option when the container is running in ramdisk @@ -17,33 +19,10 @@ type MountConfig struct { // Mounts specify additional source and destination paths that will be mounted inside the container's // rootfs and mount namespace if specified - Mounts Mounts `json:"mounts,omitempty"` + Mounts []*Mount `json:"mounts,omitempty"` // The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well! DeviceNodes []*devices.Device `json:"device_nodes,omitempty"` MountLabel string `json:"mount_label,omitempty"` } - -type Mount struct { - Type string `json:"type,omitempty"` - Source string `json:"source,omitempty"` // Source path, in the host namespace - Destination string `json:"destination,omitempty"` // Destination path, in the container - Writable bool `json:"writable,omitempty"` - Relabel string `json:"relabel,omitempty"` // Relabel source if set, "z" indicates shared, "Z" indicates unshared - Private bool `json:"private,omitempty"` -} - -type Mounts []Mount - -var ErrUnsupported = errors.New("Unsupported method") - -func (s Mounts) OfType(t string) Mounts { - out := Mounts{} - for _, m := range s { - if m.Type == t { - out = append(out, m) - } - } - return out -}