|
@@ -12,6 +12,7 @@ import (
|
|
|
derr "github.com/docker/docker/errors"
|
|
|
"github.com/docker/docker/pkg/mount"
|
|
|
|
|
|
+ "github.com/docker/docker/volume"
|
|
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
|
"github.com/opencontainers/runc/libcontainer/devices"
|
|
@@ -278,6 +279,116 @@ func (d *Driver) setupRlimits(container *configs.Config, c *execdriver.Command)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// If rootfs mount propagation is RPRIVATE, that means all the volumes are
|
|
|
+// going to be private anyway. There is no need to apply per volume
|
|
|
+// propagation on top. This is just an optimzation so that cost of per volume
|
|
|
+// propagation is paid only if user decides to make some volume non-private
|
|
|
+// which will force rootfs mount propagation to be non RPRIVATE.
|
|
|
+func checkResetVolumePropagation(container *configs.Config) {
|
|
|
+ if container.RootPropagation != mount.RPRIVATE {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, m := range container.Mounts {
|
|
|
+ m.PropagationFlags = nil
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info {
|
|
|
+ for _, m := range mountinfo {
|
|
|
+ if m.Mountpoint == dir {
|
|
|
+ return m
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Get the source mount point of directory passed in as argument. Also return
|
|
|
+// optional fields.
|
|
|
+func getSourceMount(source string) (string, string, error) {
|
|
|
+ // Ensure any symlinks are resolved.
|
|
|
+ sourcePath, err := filepath.EvalSymlinks(source)
|
|
|
+ if err != nil {
|
|
|
+ return "", "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ mountinfos, err := mount.GetMounts()
|
|
|
+ if err != nil {
|
|
|
+ return "", "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ mountinfo := getMountInfo(mountinfos, sourcePath)
|
|
|
+ if mountinfo != nil {
|
|
|
+ return sourcePath, mountinfo.Optional, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ path := sourcePath
|
|
|
+ for {
|
|
|
+ path = filepath.Dir(path)
|
|
|
+
|
|
|
+ mountinfo = getMountInfo(mountinfos, path)
|
|
|
+ if mountinfo != nil {
|
|
|
+ return path, mountinfo.Optional, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ if path == "/" {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we are here, we did not find parent mount. Something is wrong.
|
|
|
+ return "", "", fmt.Errorf("Could not find source mount of %s", source)
|
|
|
+}
|
|
|
+
|
|
|
+// Ensure mount point on which path is mouted, is shared.
|
|
|
+func ensureShared(path string) error {
|
|
|
+ sharedMount := false
|
|
|
+
|
|
|
+ sourceMount, optionalOpts, err := getSourceMount(path)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // Make sure source mount point is shared.
|
|
|
+ optsSplit := strings.Split(optionalOpts, " ")
|
|
|
+ for _, opt := range optsSplit {
|
|
|
+ if strings.HasPrefix(opt, "shared:") {
|
|
|
+ sharedMount = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if !sharedMount {
|
|
|
+ return fmt.Errorf("Path %s is mounted on %s but it is not a shared mount.", path, sourceMount)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Ensure mount point on which path is mounted, is either shared or slave.
|
|
|
+func ensureSharedOrSlave(path string) error {
|
|
|
+ sharedMount := false
|
|
|
+ slaveMount := false
|
|
|
+
|
|
|
+ sourceMount, optionalOpts, err := getSourceMount(path)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // Make sure source mount point is shared.
|
|
|
+ optsSplit := strings.Split(optionalOpts, " ")
|
|
|
+ for _, opt := range optsSplit {
|
|
|
+ if strings.HasPrefix(opt, "shared:") {
|
|
|
+ sharedMount = true
|
|
|
+ break
|
|
|
+ } else if strings.HasPrefix(opt, "master:") {
|
|
|
+ slaveMount = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if !sharedMount && !slaveMount {
|
|
|
+ return fmt.Errorf("Path %s is mounted on %s but it is not a shared or slave mount.", path, sourceMount)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) error {
|
|
|
userMounts := make(map[string]struct{})
|
|
|
for _, m := range c.Mounts {
|
|
@@ -298,6 +409,15 @@ func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) e
|
|
|
}
|
|
|
container.Mounts = defaultMounts
|
|
|
|
|
|
+ mountPropagationMap := map[string]int{
|
|
|
+ "private": mount.PRIVATE,
|
|
|
+ "rprivate": mount.RPRIVATE,
|
|
|
+ "shared": mount.SHARED,
|
|
|
+ "rshared": mount.RSHARED,
|
|
|
+ "slave": mount.SLAVE,
|
|
|
+ "rslave": mount.RSLAVE,
|
|
|
+ }
|
|
|
+
|
|
|
for _, m := range c.Mounts {
|
|
|
for _, cm := range container.Mounts {
|
|
|
if cm.Destination == m.Destination {
|
|
@@ -319,31 +439,65 @@ func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) e
|
|
|
}
|
|
|
}
|
|
|
container.Mounts = append(container.Mounts, &configs.Mount{
|
|
|
- Source: m.Source,
|
|
|
- Destination: m.Destination,
|
|
|
- Data: data,
|
|
|
- Device: "tmpfs",
|
|
|
- Flags: flags,
|
|
|
- PremountCmds: genTmpfsPremountCmd(c.TmpDir, fulldest, m.Destination),
|
|
|
- PostmountCmds: genTmpfsPostmountCmd(c.TmpDir, fulldest, m.Destination),
|
|
|
+ Source: m.Source,
|
|
|
+ Destination: m.Destination,
|
|
|
+ Data: data,
|
|
|
+ Device: "tmpfs",
|
|
|
+ Flags: flags,
|
|
|
+ PremountCmds: genTmpfsPremountCmd(c.TmpDir, fulldest, m.Destination),
|
|
|
+ PostmountCmds: genTmpfsPostmountCmd(c.TmpDir, fulldest, m.Destination),
|
|
|
+ PropagationFlags: []int{mountPropagationMap[volume.DefaultPropagationMode]},
|
|
|
})
|
|
|
continue
|
|
|
}
|
|
|
flags := syscall.MS_BIND | syscall.MS_REC
|
|
|
+ var pFlag int
|
|
|
if !m.Writable {
|
|
|
flags |= syscall.MS_RDONLY
|
|
|
}
|
|
|
- if m.Slave {
|
|
|
- flags |= syscall.MS_SLAVE
|
|
|
+
|
|
|
+ // Determine property of RootPropagation based on volume
|
|
|
+ // properties. If a volume is shared, then keep root propagtion
|
|
|
+ // shared. This should work for slave and private volumes too.
|
|
|
+ //
|
|
|
+ // For slave volumes, it can be either [r]shared/[r]slave.
|
|
|
+ //
|
|
|
+ // For private volumes any root propagation value should work.
|
|
|
+
|
|
|
+ pFlag = mountPropagationMap[m.Propagation]
|
|
|
+ if pFlag == mount.SHARED || pFlag == mount.RSHARED {
|
|
|
+ if err := ensureShared(m.Source); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ rootpg := container.RootPropagation
|
|
|
+ if rootpg != mount.SHARED && rootpg != mount.RSHARED {
|
|
|
+ execdriver.SetRootPropagation(container, mount.SHARED)
|
|
|
+ }
|
|
|
+ } else if pFlag == mount.SLAVE || pFlag == mount.RSLAVE {
|
|
|
+ if err := ensureSharedOrSlave(m.Source); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ rootpg := container.RootPropagation
|
|
|
+ if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE {
|
|
|
+ execdriver.SetRootPropagation(container, mount.RSLAVE)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- container.Mounts = append(container.Mounts, &configs.Mount{
|
|
|
+ mount := &configs.Mount{
|
|
|
Source: m.Source,
|
|
|
Destination: m.Destination,
|
|
|
Device: "bind",
|
|
|
Flags: flags,
|
|
|
- })
|
|
|
+ }
|
|
|
+
|
|
|
+ if pFlag != 0 {
|
|
|
+ mount.PropagationFlags = []int{pFlag}
|
|
|
+ }
|
|
|
+
|
|
|
+ container.Mounts = append(container.Mounts, mount)
|
|
|
}
|
|
|
+
|
|
|
+ checkResetVolumePropagation(container)
|
|
|
return nil
|
|
|
}
|
|
|
|