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  }