diff --git a/daemon/container/mounts.go b/daemon/container/mounts.go new file mode 100644 index 0000000000..5826bae13c --- /dev/null +++ b/daemon/container/mounts.go @@ -0,0 +1,34 @@ +package container + +import ( + "cmp" + "os" + "path/filepath" + "slices" + "strings" +) + +// compareMountPaths compares mount paths by depth so that parent paths sort +// before paths beneath them. +func compareMountPaths(a, b string) int { + a = filepath.Clean(a) + b = filepath.Clean(b) + + aParts := strings.Count(a, string(os.PathSeparator)) + bParts := strings.Count(b, string(os.PathSeparator)) + if c := cmp.Compare(aParts, bParts); c != 0 { + return c + } + return cmp.Compare(a, b) +} + +// SortMounts sorts mounts by destination depth so that parent mounts are +// applied before mounts beneath them. Mounts at the same depth are sorted +// by destination to produce a deterministic order. +// +// For example, /etc must be mounted before /etc/resolv.conf. +func SortMounts(m []Mount) { + slices.SortStableFunc(m, func(a, b Mount) int { + return compareMountPaths(a.Destination, b.Destination) + }) +} diff --git a/daemon/volumes_test.go b/daemon/container/mounts_test.go similarity index 97% rename from daemon/volumes_test.go rename to daemon/container/mounts_test.go index e7e6f55440..71f999d4fb 100644 --- a/daemon/volumes_test.go +++ b/daemon/container/mounts_test.go @@ -1,4 +1,4 @@ -package daemon +package container_test import ( "testing" @@ -81,7 +81,7 @@ func TestSortMounts(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - sortMounts(tc.mounts) + container.SortMounts(tc.mounts) assert.DeepEqual(t, tc.expected, tc.mounts) }) } diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index cd7540aa73..330eaaa805 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -479,7 +479,7 @@ var ( // withMounts sets the container's mounts func withMounts(daemon *Daemon, daemonCfg *configStore, c *container.Container, mounts []container.Mount) coci.SpecOpts { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { - sortMounts(mounts) + container.SortMounts(mounts) userMounts := make(map[string]struct{}) for _, m := range mounts { diff --git a/daemon/volumes.go b/daemon/volumes.go index a4f234fa29..aa657e2f1b 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -1,15 +1,10 @@ package daemon import ( - "cmp" "context" "crypto/sha256" "encoding/hex" "maps" - "os" - "path/filepath" - "slices" - "strings" "time" "github.com/containerd/log" @@ -28,31 +23,6 @@ import ( var _ volume.LiveRestorer = (*volumeWrapper)(nil) -// compareMountPaths compares mount paths by depth so that parent paths sort -// before paths beneath them. -func compareMountPaths(a, b string) int { - a = filepath.Clean(a) - b = filepath.Clean(b) - - aParts := strings.Count(a, string(os.PathSeparator)) - bParts := strings.Count(b, string(os.PathSeparator)) - if c := cmp.Compare(aParts, bParts); c != 0 { - return c - } - return cmp.Compare(a, b) -} - -// sortMounts sorts mounts by destination depth so that parent mounts are -// applied before mounts beneath them. Mounts at the same depth are sorted -// by destination to produce a deterministic order. -// -// For example, /etc must be mounted before /etc/resolv.conf. -func sortMounts(m []container.Mount) { - slices.SortStableFunc(m, func(a, b container.Mount) int { - return compareMountPaths(a.Destination, b.Destination) - }) -} - // registerMountPoints initializes the container mount points with the configured volumes and bind mounts. // It follows the next sequence to decide what to mount in each final destination: // diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index f1089908d8..a64a84b931 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -103,7 +103,7 @@ func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ( } } - sortMounts(mounts) + container.SortMounts(mounts) netMounts := c.NetworkMounts() // if we are going to mount any of the network files from container // metadata, the ownership must be set properly for potential container diff --git a/daemon/volumes_windows.go b/daemon/volumes_windows.go index b30ae6bec9..0cff034e30 100644 --- a/daemon/volumes_windows.go +++ b/daemon/volumes_windows.go @@ -48,7 +48,7 @@ func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ( }) } - sortMounts(mnts) + container.SortMounts(mnts) return mnts, mntCleanups.Release(), nil }