mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
volumes: Implement subpath mount
`VolumeOptions` now has a `Subpath` field which allows to specify a path relative to the volume that should be mounted as a destination. Symlinks are supported, but they cannot escape the base volume directory. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
@@ -3,15 +3,18 @@
|
||||
package daemon // import "github.com/docker/docker/daemon"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/internal/cleanups"
|
||||
volumemounts "github.com/docker/docker/volume/mounts"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -19,23 +22,34 @@ import (
|
||||
// setupMounts iterates through each of the mount points for a container and
|
||||
// calls Setup() on each. It also looks to see if is a network mount such as
|
||||
// /etc/resolv.conf, and if it is not, appends it to the array of mounts.
|
||||
func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) {
|
||||
//
|
||||
// The cleanup function should be called as soon as the container has been
|
||||
// started.
|
||||
func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, func() error, error) {
|
||||
var mounts []container.Mount
|
||||
// TODO: tmpfs mounts should be part of Mountpoints
|
||||
tmpfsMounts := make(map[string]bool)
|
||||
tmpfsMountInfo, err := c.TmpfsMounts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, m := range tmpfsMountInfo {
|
||||
tmpfsMounts[m.Destination] = true
|
||||
}
|
||||
|
||||
cleanups := cleanups.Composite{}
|
||||
defer func() {
|
||||
if err := cleanups.Call(); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Warn("failed to cleanup temporary mounts created by MountPoint.Setup")
|
||||
}
|
||||
}()
|
||||
|
||||
for _, m := range c.MountPoints {
|
||||
if tmpfsMounts[m.Destination] {
|
||||
continue
|
||||
}
|
||||
if err := daemon.lazyInitializeVolume(c.ID, m); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
// If the daemon is being shutdown, we should not let a container start if it is trying to
|
||||
// mount the socket the daemon is listening on. During daemon shutdown, the socket
|
||||
@@ -48,10 +62,12 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
|
||||
return nil
|
||||
}
|
||||
|
||||
path, err := m.Setup(c.MountLabel, daemon.idMapping.RootPair(), checkfunc)
|
||||
path, clean, err := m.Setup(c.MountLabel, daemon.idMapping.RootPair(), checkfunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
cleanups.Add(clean)
|
||||
|
||||
if !c.TrySetNetworkMount(m.Destination, path) {
|
||||
mnt := container.Mount{
|
||||
Source: path,
|
||||
@@ -61,13 +77,13 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
|
||||
}
|
||||
if m.Spec.Type == mounttypes.TypeBind && m.Spec.BindOptions != nil {
|
||||
if !m.Spec.ReadOnly && m.Spec.BindOptions.ReadOnlyNonRecursive {
|
||||
return nil, errors.New("mount options conflict: !ReadOnly && BindOptions.ReadOnlyNonRecursive")
|
||||
return nil, nil, errors.New("mount options conflict: !ReadOnly && BindOptions.ReadOnlyNonRecursive")
|
||||
}
|
||||
if !m.Spec.ReadOnly && m.Spec.BindOptions.ReadOnlyForceRecursive {
|
||||
return nil, errors.New("mount options conflict: !ReadOnly && BindOptions.ReadOnlyForceRecursive")
|
||||
return nil, nil, errors.New("mount options conflict: !ReadOnly && BindOptions.ReadOnlyForceRecursive")
|
||||
}
|
||||
if m.Spec.BindOptions.ReadOnlyNonRecursive && m.Spec.BindOptions.ReadOnlyForceRecursive {
|
||||
return nil, errors.New("mount options conflict: ReadOnlyNonRecursive && BindOptions.ReadOnlyForceRecursive")
|
||||
return nil, nil, errors.New("mount options conflict: ReadOnlyNonRecursive && BindOptions.ReadOnlyForceRecursive")
|
||||
}
|
||||
mnt.NonRecursive = m.Spec.BindOptions.NonRecursive
|
||||
mnt.ReadOnlyNonRecursive = m.Spec.BindOptions.ReadOnlyNonRecursive
|
||||
@@ -98,11 +114,11 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
|
||||
// up to the user to make sure the file has proper ownership for userns
|
||||
if strings.Index(mnt.Source, daemon.repository) == 0 {
|
||||
if err := os.Chown(mnt.Source, rootIDs.UID, rootIDs.GID); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return append(mounts, netMounts...), nil
|
||||
return append(mounts, netMounts...), cleanups.Release(), nil
|
||||
}
|
||||
|
||||
// sortMounts sorts an array of mounts in lexicographic order. This ensure that
|
||||
|
||||
Reference in New Issue
Block a user