diff --git a/libcontainer/configs/mount.go b/libcontainer/configs/mount.go index 50668f04fc9..f982da14de4 100644 --- a/libcontainer/configs/mount.go +++ b/libcontainer/configs/mount.go @@ -1,5 +1,13 @@ package configs +import ( + "path/filepath" + "strings" + "syscall" + + "github.com/opencontainers/runc/libcontainer/label" +) + type Mount struct { // Source path for the mount. Source string `json:"source"` @@ -13,6 +21,9 @@ type Mount struct { // Mount flags. Flags int `json:"flags"` + // Propagation Flags + PropagationFlags []int `json:"propagation_flags"` + // Mount data applied to the mount. Data string `json:"data"` @@ -25,3 +36,40 @@ type Mount struct { // Optional Command to be run after Source is mounted. PostmountCmds []Command `json:"postmount_cmds"` } + +func (m *Mount) Remount(rootfs string) error { + var ( + dest = m.Destination + ) + if !strings.HasPrefix(dest, rootfs) { + dest = filepath.Join(rootfs, dest) + } + + if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { + return err + } + return nil +} + +// Do the mount operation followed by additional mounts required to take care +// of propagation flags. +func (m *Mount) MountPropagate(rootfs string, mountLabel string) error { + var ( + dest = m.Destination + data = label.FormatMountLabel(m.Data, mountLabel) + ) + if !strings.HasPrefix(dest, rootfs) { + dest = filepath.Join(rootfs, dest) + } + + if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { + return err + } + + for _, pflag := range m.PropagationFlags { + if err := syscall.Mount("", dest, "", uintptr(pflag), ""); err != nil { + return err + } + } + return nil +} diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index a5602d1bb70..51f961dfaf4 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -96,7 +96,6 @@ func mountCmd(cmd configs.Command) error { func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { var ( dest = m.Destination - data = label.FormatMountLabel(m.Data, mountLabel) ) if !strings.HasPrefix(dest, rootfs) { dest = filepath.Join(rootfs, dest) @@ -107,12 +106,12 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { if err := os.MkdirAll(dest, 0755); err != nil { return err } - return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "") + return m.MountPropagate(rootfs, mountLabel) case "mqueue": if err := os.MkdirAll(dest, 0755); err != nil { return err } - if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), ""); err != nil { + if err := m.MountPropagate(rootfs, mountLabel); err != nil { return err } return label.SetFileLabel(dest, mountLabel) @@ -123,7 +122,7 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { return err } } - if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { + if err := m.MountPropagate(rootfs, mountLabel); err != nil { return err } if stat != nil { @@ -136,12 +135,12 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { if err := os.MkdirAll(dest, 0755); err != nil { return err } - return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data) + return m.MountPropagate(rootfs, mountLabel) case "securityfs": if err := os.MkdirAll(dest, 0755); err != nil { return err } - return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data) + return m.MountPropagate(rootfs, mountLabel) case "bind": stat, err := os.Stat(m.Source) if err != nil { @@ -162,11 +161,11 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } - if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { + if err := m.MountPropagate(rootfs, mountLabel); err != nil { return err } // bind mount won't change mount options, we need remount to make mount options effective. - if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { + if err := m.Remount(rootfs); err != nil { return err } if m.Relabel != "" { @@ -178,11 +177,6 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { return err } } - if m.Flags&syscall.MS_PRIVATE != 0 { - if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { - return err - } - } case "cgroup": binds, err := getCgroupMounts(m) if err != nil { @@ -196,11 +190,12 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { } } tmpfs := &configs.Mount{ - Source: "tmpfs", - Device: "tmpfs", - Destination: m.Destination, - Flags: defaultMountFlags, - Data: "mode=755", + Source: "tmpfs", + Device: "tmpfs", + Destination: m.Destination, + Flags: defaultMountFlags, + Data: "mode=755", + PropagationFlags: m.PropagationFlags, } if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil { return err @@ -235,8 +230,11 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { } if m.Flags&syscall.MS_RDONLY != 0 { // remount cgroup root as readonly - rootfsCgroup := filepath.Join(rootfs, m.Destination) - if err := syscall.Mount("", rootfsCgroup, "", defaultMountFlags|syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil { + mcgrouproot := &configs.Mount{ + Destination: m.Destination, + Flags: defaultMountFlags | syscall.MS_RDONLY, + } + if err := mcgrouproot.Remount(rootfs); err != nil { return err } } @@ -269,10 +267,11 @@ func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) { return nil, err } binds = append(binds, &configs.Mount{ - Device: "bind", - Source: filepath.Join(mm.Mountpoint, relDir), - Destination: filepath.Join(m.Destination, strings.Join(mm.Subsystems, ",")), - Flags: syscall.MS_BIND | syscall.MS_REC | m.Flags, + Device: "bind", + Source: filepath.Join(mm.Mountpoint, relDir), + Destination: filepath.Join(m.Destination, strings.Join(mm.Subsystems, ",")), + Flags: syscall.MS_BIND | syscall.MS_REC | m.Flags, + PropagationFlags: m.PropagationFlags, }) } diff --git a/spec.go b/spec.go index 892dc938215..ff9e5c941b6 100644 --- a/spec.go +++ b/spec.go @@ -387,7 +387,7 @@ func createLibcontainerConfig(cgroupName string, spec *specs.LinuxSpec, rspec *s } func createLibcontainerMount(cwd, dest string, m specs.Mount) *configs.Mount { - flags, data := parseMountOptions(m.Options) + flags, pgflags, data := parseMountOptions(m.Options) source := m.Source if m.Type == "bind" { if !filepath.IsAbs(source) { @@ -395,11 +395,12 @@ func createLibcontainerMount(cwd, dest string, m specs.Mount) *configs.Mount { } } return &configs.Mount{ - Device: m.Type, - Source: source, - Destination: dest, - Data: data, - Flags: flags, + Device: m.Type, + Source: source, + Destination: dest, + Data: data, + Flags: flags, + PropagationFlags: pgflags, } } @@ -519,12 +520,13 @@ func createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) { }, nil } -// parseMountOptions parses the string and returns the flags and any mount data that -// it contains. -func parseMountOptions(options []string) (int, string) { +// parseMountOptions parses the string and returns the flags, propagation +// flags and any mount data that it contains. +func parseMountOptions(options []string) (int, []int, string) { var ( - flag int - data []string + flag int + pgflag []int + data []string ) flags := map[string]struct { clear bool @@ -547,22 +549,27 @@ func parseMountOptions(options []string) (int, string) { "norelatime": {true, syscall.MS_RELATIME}, "nostrictatime": {true, syscall.MS_STRICTATIME}, "nosuid": {false, syscall.MS_NOSUID}, - "private": {false, syscall.MS_PRIVATE}, "rbind": {false, syscall.MS_BIND | syscall.MS_REC}, "relatime": {false, syscall.MS_RELATIME}, "remount": {false, syscall.MS_REMOUNT}, "ro": {false, syscall.MS_RDONLY}, - "rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC}, - "rshared": {false, syscall.MS_SHARED | syscall.MS_REC}, - "rslave": {false, syscall.MS_SLAVE | syscall.MS_REC}, - "runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC}, "rw": {true, syscall.MS_RDONLY}, - "shared": {false, syscall.MS_SHARED}, - "slave": {false, syscall.MS_SLAVE}, "strictatime": {false, syscall.MS_STRICTATIME}, "suid": {true, syscall.MS_NOSUID}, "sync": {false, syscall.MS_SYNCHRONOUS}, - "unbindable": {false, syscall.MS_UNBINDABLE}, + } + propagationFlags := map[string]struct { + clear bool + flag int + }{ + "private": {false, syscall.MS_PRIVATE}, + "shared": {false, syscall.MS_SHARED}, + "slave": {false, syscall.MS_SLAVE}, + "unbindable": {false, syscall.MS_UNBINDABLE}, + "rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC}, + "rshared": {false, syscall.MS_SHARED | syscall.MS_REC}, + "rslave": {false, syscall.MS_SLAVE | syscall.MS_REC}, + "runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC}, } for _, o := range options { // If the option does not exist in the flags table or the flag @@ -574,11 +581,13 @@ func parseMountOptions(options []string) (int, string) { } else { flag |= f.flag } + } else if f, exists := propagationFlags[o]; exists && f.flag != 0 { + pgflag = append(pgflag, f.flag) } else { data = append(data, o) } } - return flag, strings.Join(data, ",") + return flag, pgflag, strings.Join(data, ",") } func setupSeccomp(config *specs.Seccomp) (*configs.Seccomp, error) {