github.com/moby/docker@v26.1.3+incompatible/daemon/volumes_unix.go (about)

     1  //go:build !windows
     2  
     3  package daemon // import "github.com/docker/docker/daemon"
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/containerd/log"
    14  	"github.com/docker/docker/api/types/events"
    15  	mounttypes "github.com/docker/docker/api/types/mount"
    16  	"github.com/docker/docker/container"
    17  	"github.com/docker/docker/internal/cleanups"
    18  	"github.com/docker/docker/internal/compatcontext"
    19  	volumemounts "github.com/docker/docker/volume/mounts"
    20  	"github.com/pkg/errors"
    21  )
    22  
    23  // setupMounts iterates through each of the mount points for a container and
    24  // calls Setup() on each. It also looks to see if is a network mount such as
    25  // /etc/resolv.conf, and if it is not, appends it to the array of mounts.
    26  //
    27  // The cleanup function should be called as soon as the container has been
    28  // started.
    29  func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ([]container.Mount, func(context.Context) error, error) {
    30  	var mounts []container.Mount
    31  	// TODO: tmpfs mounts should be part of Mountpoints
    32  	tmpfsMounts := make(map[string]bool)
    33  	tmpfsMountInfo, err := c.TmpfsMounts()
    34  	if err != nil {
    35  		return nil, nil, err
    36  	}
    37  	for _, m := range tmpfsMountInfo {
    38  		tmpfsMounts[m.Destination] = true
    39  	}
    40  
    41  	cleanups := cleanups.Composite{}
    42  	defer func() {
    43  		if err := cleanups.Call(compatcontext.WithoutCancel(ctx)); err != nil {
    44  			log.G(ctx).WithError(err).Warn("failed to cleanup temporary mounts created by MountPoint.Setup")
    45  		}
    46  	}()
    47  
    48  	for _, m := range c.MountPoints {
    49  		if tmpfsMounts[m.Destination] {
    50  			continue
    51  		}
    52  		if err := daemon.lazyInitializeVolume(c.ID, m); err != nil {
    53  			return nil, nil, err
    54  		}
    55  		// If the daemon is being shutdown, we should not let a container start if it is trying to
    56  		// mount the socket the daemon is listening on. During daemon shutdown, the socket
    57  		// (/var/run/docker.sock by default) doesn't exist anymore causing the call to m.Setup to
    58  		// create at directory instead. This in turn will prevent the daemon to restart.
    59  		checkfunc := func(m *volumemounts.MountPoint) error {
    60  			if _, exist := daemon.hosts[m.Source]; exist && daemon.IsShuttingDown() {
    61  				return fmt.Errorf("Could not mount %q to container while the daemon is shutting down", m.Source)
    62  			}
    63  			return nil
    64  		}
    65  
    66  		path, clean, err := m.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), checkfunc)
    67  		if err != nil {
    68  			return nil, nil, err
    69  		}
    70  		cleanups.Add(clean)
    71  
    72  		if !c.TrySetNetworkMount(m.Destination, path) {
    73  			mnt := container.Mount{
    74  				Source:      path,
    75  				Destination: m.Destination,
    76  				Writable:    m.RW,
    77  				Propagation: string(m.Propagation),
    78  			}
    79  			if m.Spec.Type == mounttypes.TypeBind && m.Spec.BindOptions != nil {
    80  				if !m.Spec.ReadOnly && m.Spec.BindOptions.ReadOnlyNonRecursive {
    81  					return nil, nil, errors.New("mount options conflict: !ReadOnly && BindOptions.ReadOnlyNonRecursive")
    82  				}
    83  				if !m.Spec.ReadOnly && m.Spec.BindOptions.ReadOnlyForceRecursive {
    84  					return nil, nil, errors.New("mount options conflict: !ReadOnly && BindOptions.ReadOnlyForceRecursive")
    85  				}
    86  				if m.Spec.BindOptions.ReadOnlyNonRecursive && m.Spec.BindOptions.ReadOnlyForceRecursive {
    87  					return nil, nil, errors.New("mount options conflict: ReadOnlyNonRecursive && BindOptions.ReadOnlyForceRecursive")
    88  				}
    89  				mnt.NonRecursive = m.Spec.BindOptions.NonRecursive
    90  				mnt.ReadOnlyNonRecursive = m.Spec.BindOptions.ReadOnlyNonRecursive
    91  				mnt.ReadOnlyForceRecursive = m.Spec.BindOptions.ReadOnlyForceRecursive
    92  			}
    93  			if m.Volume != nil {
    94  				daemon.LogVolumeEvent(m.Volume.Name(), events.ActionMount, map[string]string{
    95  					"driver":      m.Volume.DriverName(),
    96  					"container":   c.ID,
    97  					"destination": m.Destination,
    98  					"read/write":  strconv.FormatBool(m.RW),
    99  					"propagation": string(m.Propagation),
   100  				})
   101  			}
   102  			mounts = append(mounts, mnt)
   103  		}
   104  	}
   105  
   106  	mounts = sortMounts(mounts)
   107  	netMounts := c.NetworkMounts()
   108  	// if we are going to mount any of the network files from container
   109  	// metadata, the ownership must be set properly for potential container
   110  	// remapped root (user namespaces)
   111  	rootIDs := daemon.idMapping.RootPair()
   112  	for _, mnt := range netMounts {
   113  		// we should only modify ownership of network files within our own container
   114  		// metadata repository. If the user specifies a mount path external, it is
   115  		// up to the user to make sure the file has proper ownership for userns
   116  		if strings.Index(mnt.Source, daemon.repository) == 0 {
   117  			if err := os.Chown(mnt.Source, rootIDs.UID, rootIDs.GID); err != nil {
   118  				return nil, nil, err
   119  			}
   120  		}
   121  	}
   122  	return append(mounts, netMounts...), cleanups.Release(), nil
   123  }
   124  
   125  // sortMounts sorts an array of mounts in lexicographic order. This ensure that
   126  // when mounting, the mounts don't shadow other mounts. For example, if mounting
   127  // /etc and /etc/resolv.conf, /etc/resolv.conf must not be mounted first.
   128  func sortMounts(m []container.Mount) []container.Mount {
   129  	sort.Sort(mounts(m))
   130  	return m
   131  }
   132  
   133  // setBindModeIfNull is platform specific processing to ensure the
   134  // shared mode is set to 'z' if it is null. This is called in the case
   135  // of processing a named volume and not a typical bind.
   136  func setBindModeIfNull(bind *volumemounts.MountPoint) {
   137  	if bind.Mode == "" {
   138  		bind.Mode = "z"
   139  	}
   140  }