@@ -11,6 +11,7 @@ import (
1111 "reflect"
1212 "runtime"
1313 "strings"
14+ "syscall"
1415 "unicode"
1516 "unicode/utf8"
1617
@@ -496,9 +497,95 @@ func (v *Validator) CheckLinux() (msgs []string) {
496497 msgs = append (msgs , fmt .Sprintf ("On Linux, hostname requires a new UTS namespace to be specified as well" ))
497498 }
498499
500+ // Linux devices validation
501+ devList := make (map [string ]bool )
502+ typeList := make (map [string ]bool )
499503 for index := 0 ; index < len (v .spec .Linux .Devices ); index ++ {
500- if ! deviceValid (v .spec .Linux .Devices [index ]) {
501- msgs = append (msgs , fmt .Sprintf ("device %v is invalid." , v .spec .Linux .Devices [index ]))
504+ device := v .spec .Linux .Devices [index ]
505+ if ! deviceValid (device ) {
506+ msgs = append (msgs , fmt .Sprintf ("device %v is invalid." , device ))
507+ }
508+
509+ if _ , exists := devList [device .Path ]; exists {
510+ msgs = append (msgs , fmt .Sprintf ("device %s is duplicated" , device .Path ))
511+ } else {
512+ var rootfsPath string
513+ if filepath .IsAbs (v .spec .Root .Path ) {
514+ rootfsPath = v .spec .Root .Path
515+ } else {
516+ rootfsPath = filepath .Join (v .bundlePath , v .spec .Root .Path )
517+ }
518+ absPath := filepath .Join (rootfsPath , device .Path )
519+ fi , err := os .Stat (absPath )
520+ if os .IsNotExist (err ) {
521+ devList [device .Path ] = true
522+ } else if err != nil {
523+ msgs = append (msgs , err .Error ())
524+ } else {
525+ fStat , ok := fi .Sys ().(* syscall.Stat_t )
526+ if ! ok {
527+ msgs = append (msgs , fmt .Sprintf ("cannot determine state for device %s" , device .Path ))
528+ continue
529+ }
530+ var devType string
531+ switch fStat .Mode & syscall .S_IFMT {
532+ case syscall .S_IFCHR :
533+ devType = "c"
534+ case syscall .S_IFBLK :
535+ devType = "b"
536+ case syscall .S_IFIFO :
537+ devType = "p"
538+ default :
539+ devType = "unmatched"
540+ }
541+ if devType != device .Type || (devType == "c" && device .Type == "u" ) {
542+ msgs = append (msgs , fmt .Sprintf ("unmatched %s already exists in filesystem" , device .Path ))
543+ continue
544+ }
545+ if devType != "p" {
546+ dev := fStat .Rdev
547+ major := (dev >> 8 ) & 0xfff
548+ minor := (dev & 0xff ) | ((dev >> 12 ) & 0xfff00 )
549+ if int64 (major ) != device .Major || int64 (minor ) != device .Minor {
550+ msgs = append (msgs , fmt .Sprintf ("unmatched %s already exists in filesystem" , device .Path ))
551+ continue
552+ }
553+ }
554+ if device .FileMode != nil {
555+ expectedPerm := * device .FileMode & os .ModePerm
556+ actualPerm := fi .Mode () & os .ModePerm
557+ if expectedPerm != actualPerm {
558+ msgs = append (msgs , fmt .Sprintf ("unmatched %s already exists in filesystem" , device .Path ))
559+ continue
560+ }
561+ }
562+ if device .UID != nil {
563+ if * device .UID != fStat .Uid {
564+ msgs = append (msgs , fmt .Sprintf ("unmatched %s already exists in filesystem" , device .Path ))
565+ continue
566+ }
567+ }
568+ if device .GID != nil {
569+ if * device .GID != fStat .Gid {
570+ msgs = append (msgs , fmt .Sprintf ("unmatched %s already exists in filesystem" , device .Path ))
571+ continue
572+ }
573+ }
574+ }
575+ }
576+
577+ // unify u->c when comparing, they are synonyms
578+ var devID string
579+ if device .Type == "u" {
580+ devID = fmt .Sprintf ("%s:%d:%d" , "c" , device .Major , device .Minor )
581+ } else {
582+ devID = fmt .Sprintf ("%s:%d:%d" , device .Type , device .Major , device .Minor )
583+ }
584+
585+ if _ , exists := typeList [devID ]; exists {
586+ logrus .Warnf ("type:%s, major:%d and minor:%d for linux devices is duplicated" , device .Type , device .Major , device .Minor )
587+ } else {
588+ typeList [devID ] = true
502589 }
503590 }
504591
0 commit comments