diff --git a/cmd/runtimetest/main.go b/cmd/runtimetest/main.go index ad30bef92..77e3e15b8 100644 --- a/cmd/runtimetest/main.go +++ b/cmd/runtimetest/main.go @@ -1091,14 +1091,32 @@ func mountMatch(configMount rspec.Mount, sysMount *mount.Info) error { return fmt.Errorf("mount destination expected: %v, actual: %v", configMount.Destination, sys.Destination) } - if configMount.Type != sys.Type { + isBind := false + for _, opt := range configMount.Options { + if opt == "bind" || opt == "rbind" { + isBind = true + break + } + } + // Type is an optional field in the spec: only check if it is set + if configMount.Type != "" && configMount.Type != sys.Type { return fmt.Errorf("mount %v type expected: %v, actual: %v", configMount.Destination, configMount.Type, sys.Type) } - if filepath.Clean(configMount.Source) != sys.Source { - return fmt.Errorf("mount %v source expected: %v, actual: %v", configMount.Destination, configMount.Source, sys.Source) + // For bind mounts, the source is not the block device but the path on the host that is being bind mounted. + // sysMount.Root is that path. + if isBind { + // Source is an optional field in the spec: only check if it is set + // We only test the base name here, in case the tests are being run in a chroot environment + if configMount.Source != "" && filepath.Base(configMount.Source) != filepath.Base(sysMount.Root) { + return fmt.Errorf("mount %v source expected: %v, actual: %v", configMount.Destination, filepath.Base(configMount.Source), filepath.Base(sysMount.Root)) + } + } else { + // Source is an optional field in the spec: only check if it is set + if configMount.Source != "" && filepath.Clean(configMount.Source) != sys.Source { + return fmt.Errorf("mount %v source expected: %v, actual: %v", configMount.Destination, configMount.Source, sys.Source) + } } - return nil } diff --git a/validation/mounts/mounts.go b/validation/mounts/mounts.go index 416014824..8cd373805 100644 --- a/validation/mounts/mounts.go +++ b/validation/mounts/mounts.go @@ -6,22 +6,85 @@ import ( ) func main() { + defaultOptions := []string{ + "nosuid", + "strictatime", + "mode=755", + "size=1k", + } + + // Different combinations of mount types, mount options, mount propagation modes + mounts := []rspec.Mount{ + rspec.Mount{ + Destination: "/tmp/test-shared", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"shared"}, + }, + rspec.Mount{ + Destination: "/tmp/test-slave", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"slave"}, + }, + rspec.Mount{ + Destination: "/tmp/test-private", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"private"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-shared", + Source: "/etc", + Options: []string{"bind", "shared"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-rshared", + Source: "/etc", + Options: []string{"rbind", "rshared"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-slave", + Source: "/etc", + Options: []string{"bind", "slave"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-rslave", + Source: "/etc", + Options: []string{"rbind", "rslave"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-private", + Source: "/etc", + Options: []string{"bind", "private"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-rprivate", + Source: "/etc", + Options: []string{"rbind", "rprivate"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-unbindable", + Source: "/etc", + Options: []string{"bind", "unbindable"}, + }, + rspec.Mount{ + Destination: "/mnt/etc-runbindable", + Source: "/etc", + Options: []string{"rbind", "runbindable"}, + }, + } + g, err := util.GetDefaultGenerator() if err != nil { util.Fatal(err) } - mount := rspec.Mount{ - Destination: "/tmp", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{ - "nosuid", - "strictatime", - "mode=755", - "size=1k", - }, + + for _, m := range mounts { + m.Options = append(defaultOptions, m.Options...) + + g.AddMount(m) } - g.AddMount(mount) err = util.RuntimeInsideValidate(g, nil, nil) if err != nil { util.Fatal(err)