Skip to content

Commit 33b65b4

Browse files
authored
Merge pull request #275 from elezar/add-extra-device-info
Ensure that filemode is set for device nodes
2 parents 52948e5 + 457489d commit 33b65b4

File tree

2 files changed

+77
-25
lines changed

2 files changed

+77
-25
lines changed

pkg/cdi/container-edits_test.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package cdi
1818

1919
import (
20+
"os"
2021
"runtime"
2122
"testing"
2223

@@ -314,6 +315,9 @@ func TestValidateContainerEdits(t *testing.T) {
314315
func TestApplyContainerEdits(t *testing.T) {
315316
nullDeviceMajor := int64(1)
316317
nullDeviceMinor := int64(3)
318+
319+
mode := uint32(0666)
320+
nullDeviceFileMode := (*os.FileMode)(&mode)
317321
if runtime.GOOS == "darwin" {
318322
nullDeviceMajor = 3
319323
nullDeviceMinor = 2
@@ -360,10 +364,11 @@ func TestApplyContainerEdits(t *testing.T) {
360364
Linux: &oci.Linux{
361365
Devices: []oci.LinuxDevice{
362366
{
363-
Path: "/dev/null",
364-
Type: "c",
365-
Major: nullDeviceMajor,
366-
Minor: nullDeviceMinor,
367+
Path: "/dev/null",
368+
Type: "c",
369+
Major: nullDeviceMajor,
370+
Minor: nullDeviceMinor,
371+
FileMode: nullDeviceFileMode,
367372
},
368373
},
369374
Resources: &oci.LinuxResources{
@@ -403,10 +408,11 @@ func TestApplyContainerEdits(t *testing.T) {
403408
Linux: &oci.Linux{
404409
Devices: []oci.LinuxDevice{
405410
{
406-
Path: "/dev/null",
407-
Type: "c",
408-
Major: nullDeviceMajor,
409-
Minor: nullDeviceMinor,
411+
Path: "/dev/null",
412+
Type: "c",
413+
Major: nullDeviceMajor,
414+
Minor: nullDeviceMinor,
415+
FileMode: nullDeviceFileMode,
410416
},
411417
},
412418
Resources: &oci.LinuxResources{

pkg/cdi/container-edits_unix.go

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package cdi
2121
import (
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.
5980
func (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 == "p") {
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 != "p" {
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

Comments
 (0)