110 lines
2.9 KiB
Go
110 lines
2.9 KiB
Go
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"`
|
|
Slave bool `json:"slave,omitempty"`
|
|
}
|
|
|
|
func (m *Mount) Mount(rootfs, mountLabel string) error {
|
|
switch m.Type {
|
|
case "bind":
|
|
return m.bindMount(rootfs, mountLabel)
|
|
case "tmpfs":
|
|
return m.tmpfsMount(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
|
|
}
|
|
|
|
if m.Slave {
|
|
flags = flags | syscall.MS_SLAVE
|
|
}
|
|
|
|
stat, err := os.Stat(m.Source)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// FIXME: (crosbymichael) This does 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
|
|
}
|
|
|
|
func (m *Mount) tmpfsMount(rootfs, mountLabel string) error {
|
|
var (
|
|
err error
|
|
l = label.FormatMountLabel("", mountLabel)
|
|
dest = filepath.Join(rootfs, m.Destination)
|
|
)
|
|
|
|
// FIXME: (crosbymichael) This does not belong here and should be done a layer above
|
|
if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := createIfNotExists(dest, true); err != nil {
|
|
return fmt.Errorf("creating new tmpfs mount target %s", err)
|
|
}
|
|
|
|
if err := syscall.Mount("tmpfs", dest, "tmpfs", uintptr(defaultMountFlags), l); err != nil {
|
|
return fmt.Errorf("%s mounting %s in tmpfs", err, dest)
|
|
}
|
|
|
|
return nil
|
|
}
|