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:
Paweł Gronowski
2023-06-09 20:43:45 +02:00
parent f2e1105056
commit bfb810445c
20 changed files with 727 additions and 30 deletions

View File

@@ -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