devices_unix.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // +build linux freebsd
  2. package devices
  3. import (
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "syscall"
  10. "github.com/opencontainers/runc/libcontainer/configs"
  11. )
  12. var (
  13. ErrNotADevice = errors.New("not a device node")
  14. )
  15. // Testing dependencies
  16. var (
  17. osLstat = os.Lstat
  18. ioutilReadDir = ioutil.ReadDir
  19. )
  20. // Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
  21. func DeviceFromPath(path, permissions string) (*configs.Device, error) {
  22. fileInfo, err := osLstat(path)
  23. if err != nil {
  24. return nil, err
  25. }
  26. var (
  27. devType rune
  28. mode = fileInfo.Mode()
  29. fileModePermissionBits = os.FileMode.Perm(mode)
  30. )
  31. switch {
  32. case mode&os.ModeDevice == 0:
  33. return nil, ErrNotADevice
  34. case mode&os.ModeCharDevice != 0:
  35. fileModePermissionBits |= syscall.S_IFCHR
  36. devType = 'c'
  37. default:
  38. fileModePermissionBits |= syscall.S_IFBLK
  39. devType = 'b'
  40. }
  41. stat_t, ok := fileInfo.Sys().(*syscall.Stat_t)
  42. if !ok {
  43. return nil, fmt.Errorf("cannot determine the device number for device %s", path)
  44. }
  45. devNumber := int(stat_t.Rdev)
  46. return &configs.Device{
  47. Type: devType,
  48. Path: path,
  49. Major: Major(devNumber),
  50. Minor: Minor(devNumber),
  51. Permissions: permissions,
  52. FileMode: fileModePermissionBits,
  53. Uid: stat_t.Uid,
  54. Gid: stat_t.Gid,
  55. }, nil
  56. }
  57. func HostDevices() ([]*configs.Device, error) {
  58. return getDevices("/dev")
  59. }
  60. func getDevices(path string) ([]*configs.Device, error) {
  61. files, err := ioutilReadDir(path)
  62. if err != nil {
  63. return nil, err
  64. }
  65. out := []*configs.Device{}
  66. for _, f := range files {
  67. switch {
  68. case f.IsDir():
  69. switch f.Name() {
  70. // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
  71. case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
  72. continue
  73. default:
  74. sub, err := getDevices(filepath.Join(path, f.Name()))
  75. if err != nil {
  76. return nil, err
  77. }
  78. out = append(out, sub...)
  79. continue
  80. }
  81. case f.Name() == "console":
  82. continue
  83. }
  84. device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
  85. if err != nil {
  86. if err == ErrNotADevice {
  87. continue
  88. }
  89. if os.IsNotExist(err) {
  90. continue
  91. }
  92. return nil, err
  93. }
  94. out = append(out, device)
  95. }
  96. return out, nil
  97. }