mirror of
https://github.com/containers/podman.git
synced 2025-12-02 02:58:03 +08:00
These files should never be included on the remote client. There only there to finalize the spec on the server side. This makes sure it will not get reimported by accident and bloat the remote client again. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
122 lines
3.3 KiB
Go
122 lines
3.3 KiB
Go
//go:build !remote
|
|
// +build !remote
|
|
|
|
package generate
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
|
"github.com/opencontainers/runtime-tools/generate"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// DevicesFromPath computes a list of devices
|
|
func DevicesFromPath(g *generate.Generator, devicePath string) error {
|
|
if isCDIDevice(devicePath) {
|
|
registry := cdi.GetRegistry(
|
|
cdi.WithAutoRefresh(false),
|
|
)
|
|
if err := registry.Refresh(); err != nil {
|
|
logrus.Debugf("The following error was triggered when refreshing the CDI registry: %v", err)
|
|
}
|
|
_, err := registry.InjectDevices(g.Config, devicePath)
|
|
if err != nil {
|
|
return fmt.Errorf("setting up CDI devices: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
devs := strings.Split(devicePath, ":")
|
|
resolvedDevicePath := devs[0]
|
|
// check if it is a symbolic link
|
|
if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
|
|
if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil {
|
|
resolvedDevicePath = linkedPathOnHost
|
|
}
|
|
}
|
|
st, err := os.Stat(resolvedDevicePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if st.IsDir() {
|
|
// For devfs, we need to add the directory as well
|
|
addDevice(g, resolvedDevicePath)
|
|
|
|
found := false
|
|
src := resolvedDevicePath
|
|
dest := src
|
|
var devmode string
|
|
if len(devs) > 1 {
|
|
if len(devs[1]) > 0 && devs[1][0] == '/' {
|
|
dest = devs[1]
|
|
} else {
|
|
devmode = devs[1]
|
|
}
|
|
}
|
|
if len(devs) > 2 {
|
|
if devmode != "" {
|
|
return fmt.Errorf("invalid device specification %s: %w", devicePath, unix.EINVAL)
|
|
}
|
|
devmode = devs[2]
|
|
}
|
|
|
|
// mount the internal devices recursively
|
|
if err := filepath.WalkDir(resolvedDevicePath, func(dpath string, d fs.DirEntry, e error) error {
|
|
if d.Type()&os.ModeDevice == os.ModeDevice {
|
|
found = true
|
|
device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src)))
|
|
if devmode != "" {
|
|
device = fmt.Sprintf("%s:%s", device, devmode)
|
|
}
|
|
if err := addDevice(g, device); err != nil {
|
|
return fmt.Errorf("failed to add %s device: %w", dpath, err)
|
|
}
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("no devices found in %s: %w", devicePath, unix.EINVAL)
|
|
}
|
|
return nil
|
|
}
|
|
return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
|
|
}
|
|
|
|
func addDevice(g *generate.Generator, device string) error {
|
|
src, dst, permissions, err := ParseDevice(device)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if src != dst {
|
|
return fmt.Errorf("container device must be the same as host device on FreeBSD")
|
|
}
|
|
mode := 0
|
|
if strings.Contains(permissions, "r") {
|
|
mode |= unix.S_IRUSR
|
|
}
|
|
if strings.Contains(permissions, "w") {
|
|
mode |= unix.S_IWUSR
|
|
}
|
|
// Find the devfs mount so that we can add rules to expose the device
|
|
for k, m := range g.Config.Mounts {
|
|
if m.Type == "devfs" {
|
|
if dev, ok := strings.CutPrefix(src, "/dev/"); ok {
|
|
m.Options = append(m.Options,
|
|
fmt.Sprintf("rule=path %s unhide mode %04o", dev, mode))
|
|
} else {
|
|
return fmt.Errorf("expected device to start with \"/dev\": %v", dev)
|
|
}
|
|
g.Config.Mounts[k] = m
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("devfs not found in generator")
|
|
}
|