@@ -21,6 +21,7 @@ package cdi
2121import (
2222 "errors"
2323 "fmt"
24+ "os"
2425
2526 "golang.org/x/sys/unix"
2627)
@@ -31,16 +32,28 @@ const (
3132 fifoDevice = "p"
3233)
3334
35+ type deviceInfo struct {
36+ // cgroup properties
37+ deviceType string
38+ major int64
39+ minor int64
40+
41+ // device node properties
42+ fileMode os.FileMode
43+ }
44+
3445// deviceInfoFromPath takes the path to a device and returns its type,
3546// major and minor device numbers.
3647//
3748// It was adapted from https://github.com/opencontainers/runc/blob/v1.1.9/libcontainer/devices/device_unix.go#L30-L69
38- func deviceInfoFromPath (path string ) (devType string , major , minor int64 , _ error ) {
49+ func deviceInfoFromPath (path string ) (* deviceInfo , error ) {
3950 var stat unix.Stat_t
4051 err := unix .Lstat (path , & stat )
4152 if err != nil {
42- return "" , 0 , 0 , err
53+ return nil , err
4354 }
55+
56+ var devType string
4457 switch stat .Mode & unix .S_IFMT {
4558 case unix .S_IFBLK :
4659 devType = blockDevice
@@ -49,38 +62,71 @@ func deviceInfoFromPath(path string) (devType string, major, minor int64, _ erro
4962 case unix .S_IFIFO :
5063 devType = fifoDevice
5164 default :
52- return "" , 0 , 0 , errors .New ("not a device node" )
65+ return nil , errors .New ("not a device node" )
5366 }
5467 devNumber := uint64 (stat .Rdev ) //nolint:unconvert // Rdev is uint32 on e.g. MIPS.
55- return devType , int64 (unix .Major (devNumber )), int64 (unix .Minor (devNumber )), nil
68+
69+ di := deviceInfo {
70+ deviceType : devType ,
71+ major : int64 (unix .Major (devNumber )),
72+ minor : int64 (unix .Minor (devNumber )),
73+ fileMode : os .FileMode (stat .Mode &^ unix .S_IFMT ),
74+ }
75+
76+ return & di , nil
5677}
5778
5879// fillMissingInfo fills in missing mandatory attributes from the host device.
5980func (d * DeviceNode ) fillMissingInfo () error {
81+ hasMinimalSpecification := d .Type != "" && (d .Major != 0 || d .Type == fifoDevice )
82+
83+ // Ensure that the host path and the container path match.
6084 if d .HostPath == "" {
6185 d .HostPath = d .Path
6286 }
6387
64- if d .Type != "" && (d .Major != 0 || d .Type == fifoDevice ) {
88+ // Try to extract the device info from the host path.
89+ di , err := deviceInfoFromPath (d .HostPath )
90+ if err != nil {
91+ // The error is only considered fatal if the device is not already
92+ // minimally specified since it is allowed for a device vendor to fully
93+ // specify a device node specification.
94+ if ! hasMinimalSpecification {
95+ return fmt .Errorf ("failed to stat CDI host device %q: %w" , d .HostPath , err )
96+ }
6597 return nil
6698 }
6799
68- deviceType , major , minor , err := deviceInfoFromPath (d .HostPath )
69- if err != nil {
70- return fmt .Errorf ("failed to stat CDI host device %q: %w" , d .HostPath , err )
100+ // Even for minimally-specified device nodes, we update the file mode if
101+ // required. This is useful for rootless containers where device node
102+ // requests may be treated as bind mounts.
103+ if d .FileMode == nil {
104+ d .FileMode = & di .fileMode
105+ }
106+
107+ // If the device is minimally specified, we make no further updates and
108+ // don't perform additional checks.
109+ if hasMinimalSpecification {
110+ return nil
71111 }
72112
73113 if d .Type == "" {
74- d .Type = deviceType
75- } else {
76- if d .Type != deviceType {
77- return fmt .Errorf ("CDI device (%q, %q), host type mismatch (%s, %s)" ,
78- d .Path , d .HostPath , d .Type , deviceType )
79- }
114+ d .Type = di .deviceType
80115 }
81- if d .Major == 0 && d .Type != fifoDevice {
82- d .Major = major
83- d .Minor = minor
116+ if d .Type != di .deviceType {
117+ return fmt .Errorf ("CDI device (%q, %q), host type mismatch (%s, %s)" ,
118+ d .Path , d .HostPath , d .Type , di .deviceType )
119+ }
120+
121+ // For a fifoDevice, we do not update the major and minor number.
122+ if d .Type == fifoDevice {
123+ return nil
124+ }
125+
126+ // Update the major and minor number for the device node if required.
127+ if d .Major == 0 {
128+ d .Major = di .major
129+ d .Minor = di .minor
84130 }
85131
86132 return nil
0 commit comments