|
@@ -1,8 +1,11 @@
|
|
|
package devices
|
|
|
|
|
|
import (
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
+ "io/ioutil"
|
|
|
"os"
|
|
|
+ "path/filepath"
|
|
|
"syscall"
|
|
|
)
|
|
|
|
|
@@ -10,6 +13,10 @@ const (
|
|
|
Wildcard = -1
|
|
|
)
|
|
|
|
|
|
+var (
|
|
|
+ ErrNotADeviceNode = errors.New("not a device node")
|
|
|
+)
|
|
|
+
|
|
|
type Device struct {
|
|
|
Type rune `json:"type,omitempty"`
|
|
|
Path string `json:"path,omitempty"` // It is fine if this is an empty string in the case that you are using Wildcards
|
|
@@ -27,35 +34,27 @@ func GetDeviceNumberString(deviceNumber int64) string {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (device Device) GetCgroupAllowString() string {
|
|
|
+func (device *Device) GetCgroupAllowString() string {
|
|
|
return fmt.Sprintf("%c %s:%s %s", device.Type, GetDeviceNumberString(device.MajorNumber), GetDeviceNumberString(device.MinorNumber), device.CgroupPermissions)
|
|
|
}
|
|
|
|
|
|
// Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct.
|
|
|
-func GetDevice(path string, cgroupPermissions string) (Device, error) {
|
|
|
+func GetDevice(path string, cgroupPermissions string) (*Device, error) {
|
|
|
+ fileInfo, err := os.Stat(path)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
var (
|
|
|
- err error
|
|
|
- fileInfo os.FileInfo
|
|
|
- mode os.FileMode
|
|
|
- fileModePermissionBits os.FileMode
|
|
|
devType rune
|
|
|
- devNumber int
|
|
|
- stat_t *syscall.Stat_t
|
|
|
- ok bool
|
|
|
- device Device
|
|
|
+ mode = fileInfo.Mode()
|
|
|
+ fileModePermissionBits = os.FileMode.Perm(mode)
|
|
|
)
|
|
|
|
|
|
- fileInfo, err = os.Stat(path)
|
|
|
- if err != nil {
|
|
|
- return Device{}, err
|
|
|
- }
|
|
|
-
|
|
|
- mode = fileInfo.Mode()
|
|
|
- fileModePermissionBits = os.FileMode.Perm(mode)
|
|
|
switch {
|
|
|
- case (mode & os.ModeDevice) == 0:
|
|
|
- return Device{}, fmt.Errorf("%s is not a device", path)
|
|
|
- case (mode & os.ModeCharDevice) != 0:
|
|
|
+ case mode&os.ModeDevice == 0:
|
|
|
+ return nil, ErrNotADeviceNode
|
|
|
+ case mode&os.ModeCharDevice != 0:
|
|
|
fileModePermissionBits |= syscall.S_IFCHR
|
|
|
devType = 'c'
|
|
|
default:
|
|
@@ -63,177 +62,58 @@ func GetDevice(path string, cgroupPermissions string) (Device, error) {
|
|
|
devType = 'b'
|
|
|
}
|
|
|
|
|
|
- stat_t, ok = fileInfo.Sys().(*syscall.Stat_t)
|
|
|
+ stat_t, ok := fileInfo.Sys().(*syscall.Stat_t)
|
|
|
if !ok {
|
|
|
- return Device{}, fmt.Errorf("cannot determine the device number for device %s", path)
|
|
|
+ return nil, fmt.Errorf("cannot determine the device number for device %s", path)
|
|
|
}
|
|
|
- devNumber = int(stat_t.Rdev)
|
|
|
+ devNumber := int(stat_t.Rdev)
|
|
|
|
|
|
- device = Device{
|
|
|
+ return &Device{
|
|
|
Type: devType,
|
|
|
Path: path,
|
|
|
MajorNumber: Major(devNumber),
|
|
|
MinorNumber: Minor(devNumber),
|
|
|
CgroupPermissions: cgroupPermissions,
|
|
|
FileMode: fileModePermissionBits,
|
|
|
- }
|
|
|
- return device, nil
|
|
|
+ }, nil
|
|
|
}
|
|
|
|
|
|
-var (
|
|
|
- // These are devices that are to be both allowed and created.
|
|
|
-
|
|
|
- DefaultSimpleDevices = []Device{
|
|
|
- // /dev/null and zero
|
|
|
- {
|
|
|
- Path: "/dev/null",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 1,
|
|
|
- MinorNumber: 3,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- FileMode: 0666,
|
|
|
- },
|
|
|
- {
|
|
|
- Path: "/dev/zero",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 1,
|
|
|
- MinorNumber: 5,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- FileMode: 0666,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- Path: "/dev/full",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 1,
|
|
|
- MinorNumber: 7,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- FileMode: 0666,
|
|
|
- },
|
|
|
-
|
|
|
- // consoles and ttys
|
|
|
- {
|
|
|
- Path: "/dev/tty",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 5,
|
|
|
- MinorNumber: 0,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- FileMode: 0666,
|
|
|
- },
|
|
|
-
|
|
|
- // /dev/urandom,/dev/random
|
|
|
- {
|
|
|
- Path: "/dev/urandom",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 1,
|
|
|
- MinorNumber: 9,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- FileMode: 0666,
|
|
|
- },
|
|
|
- {
|
|
|
- Path: "/dev/random",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 1,
|
|
|
- MinorNumber: 8,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- FileMode: 0666,
|
|
|
- },
|
|
|
+func GetHostDeviceNodes() ([]*Device, error) {
|
|
|
+ return getDeviceNodes("/dev")
|
|
|
+}
|
|
|
+
|
|
|
+func getDeviceNodes(path string) ([]*Device, error) {
|
|
|
+ files, err := ioutil.ReadDir(path)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
- DefaultAllowedDevices = append([]Device{
|
|
|
- // allow mknod for any device
|
|
|
- {
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: Wildcard,
|
|
|
- MinorNumber: Wildcard,
|
|
|
- CgroupPermissions: "m",
|
|
|
- },
|
|
|
- {
|
|
|
- Type: 'b',
|
|
|
- MajorNumber: Wildcard,
|
|
|
- MinorNumber: Wildcard,
|
|
|
- CgroupPermissions: "m",
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- Path: "/dev/console",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 5,
|
|
|
- MinorNumber: 1,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
- {
|
|
|
- Path: "/dev/tty0",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 4,
|
|
|
- MinorNumber: 0,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
- {
|
|
|
- Path: "/dev/tty1",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 4,
|
|
|
- MinorNumber: 1,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
- // /dev/pts/ - pts namespaces are "coming soon"
|
|
|
- {
|
|
|
- Path: "",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 136,
|
|
|
- MinorNumber: Wildcard,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
- {
|
|
|
- Path: "",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 5,
|
|
|
- MinorNumber: 2,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
-
|
|
|
- // tuntap
|
|
|
- {
|
|
|
- Path: "",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 10,
|
|
|
- MinorNumber: 200,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
-
|
|
|
- /*// fuse
|
|
|
- {
|
|
|
- Path: "",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 10,
|
|
|
- MinorNumber: 229,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
-
|
|
|
- // rtc
|
|
|
- {
|
|
|
- Path: "",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 254,
|
|
|
- MinorNumber: 0,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
- */
|
|
|
- }, DefaultSimpleDevices...)
|
|
|
-
|
|
|
- DefaultAutoCreatedDevices = append([]Device{
|
|
|
- {
|
|
|
- // /dev/fuse is created but not allowed.
|
|
|
- // This is to allow java to work. Because java
|
|
|
- // Insists on there being a /dev/fuse
|
|
|
- // https://github.com/dotcloud/docker/issues/514
|
|
|
- // https://github.com/dotcloud/docker/issues/2393
|
|
|
- //
|
|
|
- Path: "/dev/fuse",
|
|
|
- Type: 'c',
|
|
|
- MajorNumber: 10,
|
|
|
- MinorNumber: 229,
|
|
|
- CgroupPermissions: "rwm",
|
|
|
- },
|
|
|
- }, DefaultSimpleDevices...)
|
|
|
-)
|
|
|
+ out := []*Device{}
|
|
|
+ for _, f := range files {
|
|
|
+ if f.IsDir() {
|
|
|
+ switch f.Name() {
|
|
|
+ case "pts", "shm", "fd":
|
|
|
+ continue
|
|
|
+ default:
|
|
|
+ sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ out = append(out, sub...)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ device, err := GetDevice(filepath.Join(path, f.Name()), "rwm")
|
|
|
+ if err != nil {
|
|
|
+ if err == ErrNotADeviceNode {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ out = append(out, device)
|
|
|
+ }
|
|
|
+
|
|
|
+ return out, nil
|
|
|
+}
|