diff --git a/libpod/util.go b/libpod/util.go index 67ba151c04..c03fada4a9 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -12,7 +12,7 @@ import ( "net/http" "os" "path/filepath" - "sort" + "slices" "strconv" "strings" "time" @@ -46,26 +46,28 @@ func MountExists(specMounts []spec.Mount, dest string) bool { return false } -type byDestination []spec.Mount - -func (m byDestination) Len() int { - return len(m) -} - -func (m byDestination) Less(i, j int) bool { - return m.parts(i) < m.parts(j) -} - -func (m byDestination) Swap(i, j int) { - m[i], m[j] = m[j], m[i] -} - -func (m byDestination) parts(i int) int { - return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) +func parts(m spec.Mount) int { + // We must special case a root mount /. + // The count of "/" and "/proc" are both 1 but of course logically "/" must + // be mounted before "/proc" as such set the count to 0. + if m.Destination == "/" { + return 0 + } + return strings.Count(filepath.Clean(m.Destination), string(os.PathSeparator)) } func sortMounts(m []spec.Mount) []spec.Mount { - sort.Sort(byDestination(m)) + slices.SortStableFunc(m, func(a, b spec.Mount) int { + aLen := parts(a) + bLen := parts(b) + if aLen < bLen { + return -1 + } + if aLen == bLen { + return 0 + } + return 1 + }) return m } diff --git a/libpod/util_test.go b/libpod/util_test.go new file mode 100644 index 0000000000..2ca5b36e39 --- /dev/null +++ b/libpod/util_test.go @@ -0,0 +1,69 @@ +//go:build !remote + +package libpod + +import ( + "testing" + + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" +) + +func Test_sortMounts(t *testing.T) { + tests := []struct { + name string + args []spec.Mount + want []spec.Mount + }{ + { + name: "simple nested mounts", + args: []spec.Mount{ + { + Destination: "/abc/123", + }, + { + Destination: "/abc", + }, + }, + want: []spec.Mount{ + { + Destination: "/abc", + }, + { + Destination: "/abc/123", + }, + }, + }, + { + name: "root mount", + args: []spec.Mount{ + { + Destination: "/abc", + }, + { + Destination: "/", + }, + { + Destination: "/def", + }, + }, + want: []spec.Mount{ + { + Destination: "/", + }, + { + Destination: "/abc", + }, + { + Destination: "/def", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := sortMounts(tt.args) + assert.Equal(t, tt.want, got) + }) + } +}