diff --git a/validation/linux_readonly_paths.go b/validation/linux_readonly_paths.go index 1f2d1263..6a784065 100644 --- a/validation/linux_readonly_paths.go +++ b/validation/linux_readonly_paths.go @@ -1,22 +1,39 @@ package main import ( + "fmt" "io/ioutil" "os" "path/filepath" "github.com/opencontainers/runtime-tools/validation/util" + "golang.org/x/sys/unix" ) -func main() { +func checkReadonlyPaths() error { g, err := util.GetDefaultGenerator() if err != nil { - util.Fatal(err) + return err } - g.AddLinuxReadonlyPaths("/readonly-dir") - g.AddLinuxReadonlyPaths("/readonly-file") + + readonlyDir := "readonly-dir" + readonlySubDir := "readonly-subdir" + readonlyFile := "readonly-file" + + readonlyDirTop := filepath.Join("/", readonlyDir) + readonlyFileTop := filepath.Join("/", readonlyFile) + + readonlyDirSub := filepath.Join(readonlyDirTop, readonlySubDir) + readonlyFileSub := filepath.Join(readonlyDirTop, readonlyFile) + readonlyFileSubSub := filepath.Join(readonlyDirSub, readonlyFile) + + g.AddLinuxReadonlyPaths(readonlyDirTop) + g.AddLinuxReadonlyPaths(readonlyFileTop) + g.AddLinuxReadonlyPaths(readonlyDirSub) + g.AddLinuxReadonlyPaths(readonlyFileSub) + g.AddLinuxReadonlyPaths(readonlyFileSubSub) err = util.RuntimeInsideValidate(g, func(path string) error { - testDir := filepath.Join(path, "readonly-dir") + testDir := filepath.Join(path, readonlyDirSub) err = os.MkdirAll(testDir, 0777) if err != nil { return err @@ -28,13 +45,130 @@ func main() { } defer os.Remove(tmpfile.Name()) - testFile := filepath.Join(path, "readonly-file") - // runtimetest cannot check the readability of empty files, so // write something. + testSubSubFile := filepath.Join(path, readonlyFileSubSub) + if err := ioutil.WriteFile(testSubSubFile, []byte("immutable"), 0777); err != nil { + return err + } + + testSubFile := filepath.Join(path, readonlyFileSub) + if err := ioutil.WriteFile(testSubFile, []byte("immutable"), 0777); err != nil { + return err + } + + testFile := filepath.Join(path, readonlyFile) return ioutil.WriteFile(testFile, []byte("immutable"), 0777) }) + return err +} + +func checkReadonlyRelPaths() error { + g, err := util.GetDefaultGenerator() + if err != nil { + return err + } + + // Deliberately set a relative path to be read-only, and expect an error + readonlyRelPath := "readonly-relpath" + + g.AddLinuxReadonlyPaths(readonlyRelPath) + err = util.RuntimeInsideValidate(g, func(path string) error { + testFile := filepath.Join(path, readonlyRelPath) + if _, err := os.Stat(testFile); err != nil && os.IsNotExist(err) { + return err + } + + return nil + }) + if err != nil { + return nil + } + return fmt.Errorf("expected: err != nil, actual: err == nil") +} + +func checkReadonlySymlinks() error { + g, err := util.GetDefaultGenerator() if err != nil { + return err + } + + // Deliberately create a read-only symlink that points an invalid file, + // and expect an error. + readonlySymlink := "/readonly-symlink" + + g.AddLinuxReadonlyPaths(readonlySymlink) + err = util.RuntimeInsideValidate(g, func(path string) error { + testFile := filepath.Join(path, readonlySymlink) + // ln -s .. /readonly-symlink ; readlink -f /readonly-symlink; ls -L /readonly-symlink + if err := os.Symlink("../readonly-symlink", testFile); err != nil { + return err + } + rPath, errR := os.Readlink(testFile) + if errR != nil { + return errR + } + _, errS := os.Stat(rPath) + if errS != nil && os.IsNotExist(errS) { + return errS + } + + return nil + }) + if err != nil { + return nil + } + return fmt.Errorf("expected: err != nil, actual: err == nil") +} + +func checkReadonlyDeviceNodes(mode uint32) error { + g, err := util.GetDefaultGenerator() + if err != nil { + return err + } + + readonlyDevice := "/readonly-device" + + g.AddLinuxReadonlyPaths(readonlyDevice) + return util.RuntimeInsideValidate(g, func(path string) error { + testFile := filepath.Join(path, readonlyDevice) + + if err := unix.Mknod(testFile, mode, 0); err != nil { + return err + } + + if _, err := os.Stat(testFile); err != nil && os.IsNotExist(err) { + return err + } + + return nil + }) +} + +func main() { + if err := checkReadonlyPaths(); err != nil { + util.Fatal(err) + } + + if err := checkReadonlyRelPaths(); err != nil { util.Fatal(err) } + + if err := checkReadonlySymlinks(); err != nil { + util.Fatal(err) + } + + // test creation of different type of devices, i.e. block device, + // character device, and FIFO. + modes := []uint32{ + unix.S_IFBLK | 0666, + unix.S_IFCHR | 0666, + unix.S_IFIFO | 0666, + } + + for _, m := range modes { + if err := checkReadonlyDeviceNodes(m); err != nil { + util.Fatal(err) + } + } }