Skip to content

Commit b13ff6a

Browse files
author
Zhou Hao
authored
Merge pull request #625 from kinvolk/dongsu/fix-readable-dir
runtimetest: correctly check for a readable directory
2 parents b9d1c80 + 708de67 commit b13ff6a

File tree

3 files changed

+60
-9
lines changed

3 files changed

+60
-9
lines changed

cmd/runtimetest/main.go

+48-9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ var gitCommit = ""
3636
// VERSION file of the source code.
3737
var version = ""
3838

39+
// errAccess will be used for defining either a read error or a write error
40+
// from any type of files.
41+
var errAccess error
42+
3943
// PrGetNoNewPrivs isn't exposed in Golang so we define it ourselves copying the value from
4044
// the kernel
4145
const PrGetNoNewPrivs = 39
@@ -411,10 +415,37 @@ func testReadAccess(path string) (readable bool, err error) {
411415
if err != nil {
412416
return false, err
413417
}
414-
if fi.Mode()&os.ModeType == 0 {
418+
419+
// Check for readability in case of regular files, character device, or
420+
// directory. Although the runtime spec does not mandate the type of
421+
// masked files, we should check its Mode explicitly. A masked file
422+
// could be represented as a character file (/dev/null), which is the
423+
// case for runtimes like runc.
424+
switch fi.Mode() & os.ModeType {
425+
case 0, os.ModeDevice | os.ModeCharDevice:
415426
return testFileReadAccess(path)
427+
case os.ModeDir:
428+
return testDirectoryReadAccess(path)
416429
}
417-
return false, fmt.Errorf("cannot test read access for %q (mode %d)", path, fi.Mode())
430+
431+
errAccess = fmt.Errorf("cannot test read access for %q (mode %d)", path, fi.Mode())
432+
return false, errAccess
433+
}
434+
435+
func testDirectoryReadAccess(path string) (readable bool, err error) {
436+
files, err := ioutil.ReadDir(path)
437+
if err == io.EOF || len(files) == 0 {
438+
// Our validation/ tests only use non-empty directories for read-access
439+
// tests. So if we get an EOF on the first read, the runtime did
440+
// successfully block readability. So it should not be considered as test
441+
// failure, it just means that the test program successfully assessed
442+
// that the directory is not readable.
443+
return false, nil
444+
}
445+
if err != nil {
446+
return false, err
447+
}
448+
return true, nil
418449
}
419450

420451
func testFileReadAccess(path string) (readable bool, err error) {
@@ -441,12 +472,20 @@ func testWriteAccess(path string) (writable bool, err error) {
441472
if err != nil {
442473
return false, err
443474
}
444-
if fi.IsDir() {
445-
return testDirectoryWriteAccess(path)
446-
} else if fi.Mode()&os.ModeType == 0 {
475+
476+
// Check for writability in case of regular files, character device, or
477+
// directory. Although the runtime spec does not mandate the type of
478+
// masked files, we should check its Mode explicitly. A masked file
479+
// could be represented as a character file (/dev/null), which is the
480+
// case for runtimes like runc.
481+
switch fi.Mode() & os.ModeType {
482+
case 0, os.ModeDevice | os.ModeCharDevice:
447483
return testFileWriteAccess(path)
484+
case os.ModeDir:
485+
return testDirectoryWriteAccess(path)
448486
}
449-
return false, fmt.Errorf("cannot test write access for %q (mode %d)", path, fi.Mode())
487+
errAccess = fmt.Errorf("cannot test write access for %q (mode %d)", path, fi.Mode())
488+
return false, errAccess
450489
}
451490

452491
func testDirectoryWriteAccess(path string) (writable bool, err error) {
@@ -858,7 +897,7 @@ func (c *complianceTester) validateMaskedPaths(spec *rspec.Spec) error {
858897

859898
for _, maskedPath := range spec.Linux.MaskedPaths {
860899
readable, err := testReadAccess(maskedPath)
861-
if err != nil && !os.IsNotExist(err) {
900+
if err != nil && !os.IsNotExist(err) && err != errAccess {
862901
return err
863902
}
864903
c.harness.Ok(!readable, fmt.Sprintf("cannot read masked path %q", maskedPath))
@@ -901,15 +940,15 @@ func (c *complianceTester) validateROPaths(spec *rspec.Spec) error {
901940

902941
for i, path := range spec.Linux.ReadonlyPaths {
903942
readable, err := testReadAccess(path)
904-
if err != nil {
943+
if err != nil && err != errAccess {
905944
return err
906945
}
907946
if !readable {
908947
c.harness.Skip(1, fmt.Sprintf("%q (linux.readonlyPaths[%d]) is not readable", path, i))
909948
}
910949

911950
writable, err := testWriteAccess(path)
912-
if err != nil && !os.IsNotExist(err) {
951+
if err != nil && !os.IsNotExist(err) && err != errAccess {
913952
return err
914953
}
915954
c.harness.Ok(!writable, fmt.Sprintf("%q (linux.readonlyPaths[%d]) is not writable", path, i))

validation/linux_masked_paths.go

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ func main() {
2121
if err != nil {
2222
return err
2323
}
24+
// create a temp file to make testDir non-empty
25+
tmpfile, err := ioutil.TempFile(testDir, "tmp")
26+
if err != nil {
27+
return err
28+
}
29+
defer os.Remove(tmpfile.Name())
2430

2531
testFile := filepath.Join(path, "masked-file")
2632

validation/linux_readonly_paths.go

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ func main() {
2121
if err != nil {
2222
return err
2323
}
24+
// create a temp file to make testDir non-empty
25+
tmpfile, err := ioutil.TempFile(testDir, "tmp")
26+
if err != nil {
27+
return err
28+
}
29+
defer os.Remove(tmpfile.Name())
2430

2531
testFile := filepath.Join(path, "readonly-file")
2632

0 commit comments

Comments
 (0)