@@ -24,6 +24,8 @@ import (
2424 "github.com/opencontainers/runtime-tools/cmd/runtimetest/mount"
2525 rfc2119 "github.com/opencontainers/runtime-tools/error"
2626 "github.com/opencontainers/runtime-tools/specerror"
27+
28+ "golang.org/x/sys/unix"
2729)
2830
2931// PrGetNoNewPrivs isn't exposed in Golang so we define it ourselves copying the value from
@@ -346,6 +348,71 @@ func validateRootFS(spec *rspec.Spec) error {
346348 return nil
347349}
348350
351+ func validateRootfsPropagation (spec * rspec.Spec ) error {
352+ if spec .Linux == nil || spec .Linux .RootfsPropagation == "" {
353+ return nil
354+ }
355+
356+ targetDir , err := ioutil .TempDir ("/" , "target" )
357+ if err != nil {
358+ return err
359+ }
360+ defer os .RemoveAll (targetDir )
361+
362+ switch spec .Linux .RootfsPropagation {
363+ case "shared" , "slave" , "private" :
364+ mountDir , err := ioutil .TempDir ("/" , "mount" )
365+ if err != nil {
366+ return err
367+ }
368+ defer os .RemoveAll (mountDir )
369+
370+ testDir , err := ioutil .TempDir ("/" , "test" )
371+ if err != nil {
372+ return err
373+ }
374+ defer os .RemoveAll (testDir )
375+
376+ tmpfile , err := ioutil .TempFile (testDir , "example" )
377+ if err != nil {
378+ return err
379+ }
380+ defer os .Remove (tmpfile .Name ())
381+
382+ if err := unix .Mount ("/" , targetDir , "" , unix .MS_BIND | unix .MS_REC , "" ); err != nil {
383+ return err
384+ }
385+ defer unix .Unmount (targetDir , unix .MNT_DETACH )
386+ if err := unix .Mount (testDir , mountDir , "" , unix .MS_BIND | unix .MS_REC , "" ); err != nil {
387+ return err
388+ }
389+ defer unix .Unmount (mountDir , unix .MNT_DETACH )
390+ if _ , err := os .Stat (filepath .Join (targetDir , filepath .Join (mountDir , filepath .Base (tmpfile .Name ())))); os .IsNotExist (err ) {
391+ if spec .Linux .RootfsPropagation == "shared" {
392+ return fmt .Errorf ("rootfs should be %s, but not" , spec .Linux .RootfsPropagation )
393+ }
394+ return nil
395+ }
396+ if spec .Linux .RootfsPropagation == "shared" {
397+ return nil
398+ }
399+ return fmt .Errorf ("rootfs should be %s, but not" , spec .Linux .RootfsPropagation )
400+ case "unbindable" :
401+ if err := unix .Mount ("/" , targetDir , "" , unix .MS_BIND | unix .MS_REC , "" ); err != nil {
402+ if err == syscall .EINVAL {
403+ return nil
404+ }
405+ return err
406+ }
407+ defer unix .Unmount (targetDir , unix .MNT_DETACH )
408+ return fmt .Errorf ("rootfs expected to be unbindable, but not" )
409+ default :
410+ logrus .Warnf ("unrecognized linux.rootfsPropagation %s" , spec .Linux .RootfsPropagation )
411+ }
412+
413+ return nil
414+ }
415+
349416func validateDefaultFS (spec * rspec.Spec ) error {
350417 mountInfos , err := mount .GetMounts ()
351418 if err != nil {
@@ -779,6 +846,10 @@ func run(context *cli.Context) error {
779846 test : validateROPaths ,
780847 description : "read only paths" ,
781848 },
849+ {
850+ test : validateRootfsPropagation ,
851+ description : "rootfs propagation" ,
852+ },
782853 {
783854 test : validateSysctls ,
784855 description : "sysctls" ,
0 commit comments