github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/util/utils_linux.go (about) 1 package util 2 3 import ( 4 "fmt" 5 "io/fs" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strings" 10 "syscall" 11 12 "github.com/hanks177/podman/v4/libpod/define" 13 "github.com/hanks177/podman/v4/pkg/rootless" 14 "github.com/containers/psgo" 15 spec "github.com/opencontainers/runtime-spec/specs-go" 16 "github.com/opencontainers/runtime-tools/generate" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 "golang.org/x/sys/unix" 20 ) 21 22 var ( 23 errNotADevice = errors.New("not a device node") 24 ) 25 26 // GetContainerPidInformationDescriptors returns a string slice of all supported 27 // format descriptors of GetContainerPidInformation. 28 func GetContainerPidInformationDescriptors() ([]string, error) { 29 return psgo.ListDescriptors(), nil 30 } 31 32 // FindDeviceNodes parses /dev/ into a set of major:minor -> path, where 33 // [major:minor] is the device's major and minor numbers formatted as, for 34 // example, 2:0 and path is the path to the device node. 35 // Symlinks to nodes are ignored. 36 func FindDeviceNodes() (map[string]string, error) { 37 nodes := make(map[string]string) 38 err := filepath.WalkDir("/dev", func(path string, d fs.DirEntry, err error) error { 39 if err != nil { 40 logrus.Warnf("Error descending into path %s: %v", path, err) 41 return filepath.SkipDir 42 } 43 44 // If we aren't a device node, do nothing. 45 if d.Type()&(os.ModeDevice|os.ModeCharDevice) == 0 { 46 return nil 47 } 48 49 info, err := d.Info() 50 if err != nil { 51 return err 52 } 53 // We are a device node. Get major/minor. 54 sysstat, ok := info.Sys().(*syscall.Stat_t) 55 if !ok { 56 return errors.Errorf("Could not convert stat output for use") 57 } 58 // We must typeconvert sysstat.Rdev from uint64->int to avoid constant overflow 59 rdev := int(sysstat.Rdev) 60 major := ((rdev >> 8) & 0xfff) | ((rdev >> 32) & ^0xfff) 61 minor := (rdev & 0xff) | ((rdev >> 12) & ^0xff) 62 63 nodes[fmt.Sprintf("%d:%d", major, minor)] = path 64 65 return nil 66 }) 67 if err != nil { 68 return nil, err 69 } 70 71 return nodes, nil 72 } 73 74 func AddPrivilegedDevices(g *generate.Generator) error { 75 hostDevices, err := getDevices("/dev") 76 if err != nil { 77 return err 78 } 79 g.ClearLinuxDevices() 80 81 if rootless.IsRootless() { 82 mounts := make(map[string]interface{}) 83 for _, m := range g.Mounts() { 84 mounts[m.Destination] = true 85 } 86 newMounts := []spec.Mount{} 87 for _, d := range hostDevices { 88 devMnt := spec.Mount{ 89 Destination: d.Path, 90 Type: define.TypeBind, 91 Source: d.Path, 92 Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"}, 93 } 94 if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") { 95 continue 96 } 97 if _, found := mounts[d.Path]; found { 98 continue 99 } 100 newMounts = append(newMounts, devMnt) 101 } 102 g.Config.Mounts = append(newMounts, g.Config.Mounts...) 103 if g.Config.Linux.Resources != nil { 104 g.Config.Linux.Resources.Devices = nil 105 } 106 } else { 107 for _, d := range hostDevices { 108 g.AddDevice(d) 109 } 110 // Add resources device - need to clear the existing one first. 111 if g.Config.Linux.Resources != nil { 112 g.Config.Linux.Resources.Devices = nil 113 } 114 g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm") 115 } 116 117 return nil 118 } 119 120 // based on getDevices from runc (libcontainer/devices/devices.go) 121 func getDevices(path string) ([]spec.LinuxDevice, error) { 122 files, err := ioutil.ReadDir(path) 123 if err != nil { 124 if rootless.IsRootless() && os.IsPermission(err) { 125 return nil, nil 126 } 127 return nil, err 128 } 129 out := []spec.LinuxDevice{} 130 for _, f := range files { 131 switch { 132 case f.IsDir(): 133 switch f.Name() { 134 // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 135 case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts": 136 continue 137 default: 138 sub, err := getDevices(filepath.Join(path, f.Name())) 139 if err != nil { 140 return nil, err 141 } 142 if sub != nil { 143 out = append(out, sub...) 144 } 145 continue 146 } 147 case f.Name() == "console": 148 continue 149 case f.Mode()&os.ModeSymlink != 0: 150 continue 151 } 152 153 device, err := DeviceFromPath(filepath.Join(path, f.Name())) 154 if err != nil { 155 if err == errNotADevice { 156 continue 157 } 158 if os.IsNotExist(err) { 159 continue 160 } 161 return nil, err 162 } 163 out = append(out, *device) 164 } 165 return out, nil 166 } 167 168 // Copied from github.com/opencontainers/runc/libcontainer/devices 169 // Given the path to a device look up the information about a linux device 170 func DeviceFromPath(path string) (*spec.LinuxDevice, error) { 171 var stat unix.Stat_t 172 err := unix.Lstat(path, &stat) 173 if err != nil { 174 return nil, err 175 } 176 var ( 177 devType string 178 mode = stat.Mode 179 devNumber = uint64(stat.Rdev) // nolint: unconvert 180 m = os.FileMode(mode) 181 ) 182 183 switch { 184 case mode&unix.S_IFBLK == unix.S_IFBLK: 185 devType = "b" 186 case mode&unix.S_IFCHR == unix.S_IFCHR: 187 devType = "c" 188 case mode&unix.S_IFIFO == unix.S_IFIFO: 189 devType = "p" 190 default: 191 return nil, errNotADevice 192 } 193 194 return &spec.LinuxDevice{ 195 Type: devType, 196 Path: path, 197 FileMode: &m, 198 UID: &stat.Uid, 199 GID: &stat.Gid, 200 Major: int64(unix.Major(devNumber)), 201 Minor: int64(unix.Minor(devNumber)), 202 }, nil 203 }