diff --git a/src/runtime/virtcontainers/pkg/cgroups/utils.go b/src/runtime/virtcontainers/pkg/cgroups/utils.go index 167d65554c..78f6aba5d1 100644 --- a/src/runtime/virtcontainers/pkg/cgroups/utils.go +++ b/src/runtime/virtcontainers/pkg/cgroups/utils.go @@ -7,8 +7,13 @@ package cgroups import ( "fmt" + "os" "path/filepath" "regexp" + + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" ) // prepend a kata specific string to oci cgroup path to @@ -63,3 +68,53 @@ func IsSystemdCgroup(cgroupPath string) bool { // it's a correct systemd cgroup path. return found != nil && cgroupPath[found[0]:found[1]] == cgroupPath } + +func DeviceToCgroupDevice(device string) (*configs.Device, error) { + var st unix.Stat_t + linuxDevice := configs.Device{ + Allow: true, + Permissions: "rwm", + Path: device, + } + + if err := unix.Stat(device, &st); err != nil { + return nil, err + } + + devType := st.Mode & unix.S_IFMT + + switch devType { + case unix.S_IFCHR: + linuxDevice.Type = 'c' + case unix.S_IFBLK: + linuxDevice.Type = 'b' + default: + return nil, fmt.Errorf("unsupported device type: %v", devType) + } + + major := int64(unix.Major(st.Rdev)) + minor := int64(unix.Minor(st.Rdev)) + linuxDevice.Major = major + linuxDevice.Minor = minor + + linuxDevice.Gid = st.Gid + linuxDevice.Uid = st.Uid + linuxDevice.FileMode = os.FileMode(st.Mode) + + return &linuxDevice, nil +} + +func DeviceToLinuxDevice(device string) (specs.LinuxDeviceCgroup, error) { + dev, err := DeviceToCgroupDevice(device) + if err != nil { + return specs.LinuxDeviceCgroup{}, err + } + + return specs.LinuxDeviceCgroup{ + Allow: dev.Allow, + Type: string(dev.Type), + Major: &dev.Major, + Minor: &dev.Minor, + Access: dev.Permissions, + }, nil +} diff --git a/src/runtime/virtcontainers/pkg/cgroups/utils_test.go b/src/runtime/virtcontainers/pkg/cgroups/utils_test.go index b920ce6e9c..fff34ad1c1 100644 --- a/src/runtime/virtcontainers/pkg/cgroups/utils_test.go +++ b/src/runtime/virtcontainers/pkg/cgroups/utils_test.go @@ -6,6 +6,8 @@ package cgroups import ( + "io/ioutil" + "os" "path/filepath" "strings" "testing" @@ -102,3 +104,60 @@ func TestValidCgroupPath(t *testing.T) { } } + +func TestDeviceToCgroupDevice(t *testing.T) { + assert := assert.New(t) + + f, err := ioutil.TempFile("", "device") + assert.NoError(err) + f.Close() + + // fail: regular file to device + dev, err := DeviceToCgroupDevice(f.Name()) + assert.Error(err) + assert.Nil(dev) + + // fail: no such file + os.Remove(f.Name()) + dev, err = DeviceToCgroupDevice(f.Name()) + assert.Error(err) + assert.Nil(dev) + + devPath := "/dev/null" + if _, err := os.Stat(devPath); os.IsNotExist(err) { + t.Skipf("no such device: %v", devPath) + return + } + dev, err = DeviceToCgroupDevice(devPath) + assert.NoError(err) + assert.NotNil(dev) + assert.Equal(dev.Type, 'c') + assert.Equal(dev.Path, devPath) + assert.NotZero(dev.Major) + assert.NotZero(dev.Minor) + assert.NotEmpty(dev.Permissions) + assert.NotZero(dev.FileMode) + assert.Zero(dev.Uid) + assert.Zero(dev.Gid) + assert.True(dev.Allow) +} + +func TestDeviceToLinuxDevice(t *testing.T) { + assert := assert.New(t) + + devPath := "/dev/null" + if _, err := os.Stat(devPath); os.IsNotExist(err) { + t.Skipf("no such device: %v", devPath) + return + } + dev, err := DeviceToLinuxDevice(devPath) + assert.NoError(err) + assert.NotNil(dev) + assert.Equal(dev.Type, "c") + assert.NotNil(dev.Major) + assert.NotZero(*dev.Major) + assert.NotNil(dev.Minor) + assert.NotZero(*dev.Minor) + assert.NotEmpty(dev.Access) + assert.True(dev.Allow) +}