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