Files
podman/pkg/util/utils_linux.go
Eng Zer Jun 118546c6a7 refactor: use os.ReadDir for lightweight directory reading
`os.ReadDir` was added in Go 1.16 as part of the deprecation of `ioutil`
package. It is a more efficient implementation than `ioutil.ReadDir`.

Reference: https://pkg.go.dev/io/ioutil#ReadDir
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2022-09-11 15:58:31 +08:00

203 lines
4.9 KiB
Go

package util
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/psgo"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
var (
errNotADevice = errors.New("not a device node")
)
// GetContainerPidInformationDescriptors returns a string slice of all supported
// format descriptors of GetContainerPidInformation.
func GetContainerPidInformationDescriptors() ([]string, error) {
return psgo.ListDescriptors(), nil
}
// FindDeviceNodes parses /dev/ into a set of major:minor -> path, where
// [major:minor] is the device's major and minor numbers formatted as, for
// example, 2:0 and path is the path to the device node.
// Symlinks to nodes are ignored.
func FindDeviceNodes() (map[string]string, error) {
nodes := make(map[string]string)
err := filepath.WalkDir("/dev", func(path string, d fs.DirEntry, err error) error {
if err != nil {
logrus.Warnf("Error descending into path %s: %v", path, err)
return filepath.SkipDir
}
// If we aren't a device node, do nothing.
if d.Type()&(os.ModeDevice|os.ModeCharDevice) == 0 {
return nil
}
info, err := d.Info()
if err != nil {
return err
}
// We are a device node. Get major/minor.
sysstat, ok := info.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("could not convert stat output for use")
}
// We must typeconvert sysstat.Rdev from uint64->int to avoid constant overflow
rdev := int(sysstat.Rdev)
major := ((rdev >> 8) & 0xfff) | ((rdev >> 32) & ^0xfff)
minor := (rdev & 0xff) | ((rdev >> 12) & ^0xff)
nodes[fmt.Sprintf("%d:%d", major, minor)] = path
return nil
})
if err != nil {
return nil, err
}
return nodes, nil
}
func AddPrivilegedDevices(g *generate.Generator) error {
hostDevices, err := getDevices("/dev")
if err != nil {
return err
}
g.ClearLinuxDevices()
if rootless.IsRootless() {
mounts := make(map[string]interface{})
for _, m := range g.Mounts() {
mounts[m.Destination] = true
}
newMounts := []spec.Mount{}
for _, d := range hostDevices {
devMnt := spec.Mount{
Destination: d.Path,
Type: define.TypeBind,
Source: d.Path,
Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"},
}
if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") {
continue
}
if _, found := mounts[d.Path]; found {
continue
}
newMounts = append(newMounts, devMnt)
}
g.Config.Mounts = append(newMounts, g.Config.Mounts...)
if g.Config.Linux.Resources != nil {
g.Config.Linux.Resources.Devices = nil
}
} else {
for _, d := range hostDevices {
g.AddDevice(d)
}
// Add resources device - need to clear the existing one first.
if g.Config.Linux.Resources != nil {
g.Config.Linux.Resources.Devices = nil
}
g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm")
}
return nil
}
// based on getDevices from runc (libcontainer/devices/devices.go)
func getDevices(path string) ([]spec.LinuxDevice, error) {
files, err := os.ReadDir(path)
if err != nil {
if rootless.IsRootless() && os.IsPermission(err) {
return nil, nil
}
return nil, err
}
out := []spec.LinuxDevice{}
for _, f := range files {
switch {
case f.IsDir():
switch f.Name() {
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
continue
default:
sub, err := getDevices(filepath.Join(path, f.Name()))
if err != nil {
return nil, err
}
if sub != nil {
out = append(out, sub...)
}
continue
}
case f.Name() == "console":
continue
case f.Type()&os.ModeSymlink != 0:
continue
}
device, err := DeviceFromPath(filepath.Join(path, f.Name()))
if err != nil {
if err == errNotADevice {
continue
}
if os.IsNotExist(err) {
continue
}
return nil, err
}
out = append(out, *device)
}
return out, nil
}
// Copied from github.com/opencontainers/runc/libcontainer/devices
// Given the path to a device look up the information about a linux device
func DeviceFromPath(path string) (*spec.LinuxDevice, error) {
var stat unix.Stat_t
err := unix.Lstat(path, &stat)
if err != nil {
return nil, err
}
var (
devType string
mode = stat.Mode
devNumber = uint64(stat.Rdev) //nolint: unconvert
m = os.FileMode(mode)
)
switch {
case mode&unix.S_IFBLK == unix.S_IFBLK:
devType = "b"
case mode&unix.S_IFCHR == unix.S_IFCHR:
devType = "c"
case mode&unix.S_IFIFO == unix.S_IFIFO:
devType = "p"
default:
return nil, errNotADevice
}
return &spec.LinuxDevice{
Type: devType,
Path: path,
FileMode: &m,
UID: &stat.Uid,
GID: &stat.Gid,
Major: int64(unix.Major(devNumber)),
Minor: int64(unix.Minor(devNumber)),
}, nil
}