mirror of
https://github.com/containers/podman.git
synced 2025-05-20 08:36:23 +08:00
Privileged containers can now restart if the host devices change
If a privileged container is running, stops, and the devices on the host change, such as a USB device is unplugged, then a container would no longer start. Previously, the devices from the host were only being added to the container once: when the container was created. Now, this happens every time the container starts. I did this by adding a boolean to the container config that indicates whether to mount all of the devices or not, which can be set via an option. During spec generation, if the `MountAllDevices` option is set in the container config, all host devices are added to the container. Additionally, a couple of functions from `pkg/specgen/generate/config_linux.go` were moved into `pkg/util/utils_linux.go` as they were needed in multiple packages. Closes #13899 Signed-off-by: Jake Correnti <jcorrenti13@gmail.com>
This commit is contained in:
@ -3,13 +3,24 @@ package util
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"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/pkg/errors"
|
||||
"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
|
||||
@ -59,3 +70,134 @@ func FindDeviceNodes() (map[string]string, error) {
|
||||
|
||||
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 := ioutil.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.Mode()&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
|
||||
}
|
||||
|
Reference in New Issue
Block a user