device_unix.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // +build !windows
  2. package devices
  3. import (
  4. "errors"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "golang.org/x/sys/unix"
  9. )
  10. var (
  11. // ErrNotADevice denotes that a file is not a valid linux device.
  12. ErrNotADevice = errors.New("not a device node")
  13. )
  14. // Testing dependencies
  15. var (
  16. unixLstat = unix.Lstat
  17. ioutilReadDir = ioutil.ReadDir
  18. )
  19. func mkDev(d *Rule) (uint64, error) {
  20. if d.Major == Wildcard || d.Minor == Wildcard {
  21. return 0, errors.New("cannot mkdev() device with wildcards")
  22. }
  23. return unix.Mkdev(uint32(d.Major), uint32(d.Minor)), nil
  24. }
  25. // Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the
  26. // information about a linux device and return that information as a Device struct.
  27. func DeviceFromPath(path, permissions string) (*Device, error) {
  28. var stat unix.Stat_t
  29. err := unixLstat(path, &stat)
  30. if err != nil {
  31. return nil, err
  32. }
  33. var (
  34. devType Type
  35. mode = stat.Mode
  36. devNumber = uint64(stat.Rdev)
  37. major = unix.Major(devNumber)
  38. minor = unix.Minor(devNumber)
  39. )
  40. switch mode & unix.S_IFMT {
  41. case unix.S_IFBLK:
  42. devType = BlockDevice
  43. case unix.S_IFCHR:
  44. devType = CharDevice
  45. case unix.S_IFIFO:
  46. devType = FifoDevice
  47. default:
  48. return nil, ErrNotADevice
  49. }
  50. return &Device{
  51. Rule: Rule{
  52. Type: devType,
  53. Major: int64(major),
  54. Minor: int64(minor),
  55. Permissions: Permissions(permissions),
  56. },
  57. Path: path,
  58. FileMode: os.FileMode(mode &^ unix.S_IFMT),
  59. Uid: stat.Uid,
  60. Gid: stat.Gid,
  61. }, nil
  62. }
  63. // HostDevices returns all devices that can be found under /dev directory.
  64. func HostDevices() ([]*Device, error) {
  65. return GetDevices("/dev")
  66. }
  67. // GetDevices recursively traverses a directory specified by path
  68. // and returns all devices found there.
  69. func GetDevices(path string) ([]*Device, error) {
  70. files, err := ioutilReadDir(path)
  71. if err != nil {
  72. return nil, err
  73. }
  74. var out []*Device
  75. for _, f := range files {
  76. switch {
  77. case f.IsDir():
  78. switch f.Name() {
  79. // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
  80. // ".udev" added to address https://github.com/opencontainers/runc/issues/2093
  81. case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev":
  82. continue
  83. default:
  84. sub, err := GetDevices(filepath.Join(path, f.Name()))
  85. if err != nil {
  86. return nil, err
  87. }
  88. out = append(out, sub...)
  89. continue
  90. }
  91. case f.Name() == "console":
  92. continue
  93. }
  94. device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
  95. if err != nil {
  96. if err == ErrNotADevice {
  97. continue
  98. }
  99. if os.IsNotExist(err) {
  100. continue
  101. }
  102. return nil, err
  103. }
  104. if device.Type == FifoDevice {
  105. continue
  106. }
  107. out = append(out, device)
  108. }
  109. return out, nil
  110. }