github.com/containers/podman/v4@v4.9.4/pkg/specgen/generate/config_freebsd.go (about)

     1  //go:build !remote
     2  // +build !remote
     3  
     4  package generate
     5  
     6  import (
     7  	"fmt"
     8  	"io/fs"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/opencontainers/runtime-tools/generate"
    14  	"github.com/sirupsen/logrus"
    15  	"golang.org/x/sys/unix"
    16  	"tags.cncf.io/container-device-interface/pkg/cdi"
    17  )
    18  
    19  // DevicesFromPath computes a list of devices
    20  func DevicesFromPath(g *generate.Generator, devicePath string) error {
    21  	if isCDIDevice(devicePath) {
    22  		registry := cdi.GetRegistry(
    23  			cdi.WithAutoRefresh(false),
    24  		)
    25  		if err := registry.Refresh(); err != nil {
    26  			logrus.Debugf("The following error was triggered when refreshing the CDI registry: %v", err)
    27  		}
    28  		_, err := registry.InjectDevices(g.Config, devicePath)
    29  		if err != nil {
    30  			return fmt.Errorf("setting up CDI devices: %w", err)
    31  		}
    32  		return nil
    33  	}
    34  	devs := strings.Split(devicePath, ":")
    35  	resolvedDevicePath := devs[0]
    36  	// check if it is a symbolic link
    37  	if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
    38  		if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil {
    39  			resolvedDevicePath = linkedPathOnHost
    40  		}
    41  	}
    42  	st, err := os.Stat(resolvedDevicePath)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	if st.IsDir() {
    47  		// For devfs, we need to add the directory as well
    48  		addDevice(g, resolvedDevicePath)
    49  
    50  		found := false
    51  		src := resolvedDevicePath
    52  		dest := src
    53  		var devmode string
    54  		if len(devs) > 1 {
    55  			if len(devs[1]) > 0 && devs[1][0] == '/' {
    56  				dest = devs[1]
    57  			} else {
    58  				devmode = devs[1]
    59  			}
    60  		}
    61  		if len(devs) > 2 {
    62  			if devmode != "" {
    63  				return fmt.Errorf("invalid device specification %s: %w", devicePath, unix.EINVAL)
    64  			}
    65  			devmode = devs[2]
    66  		}
    67  
    68  		// mount the internal devices recursively
    69  		if err := filepath.WalkDir(resolvedDevicePath, func(dpath string, d fs.DirEntry, e error) error {
    70  			if d.Type()&os.ModeDevice == os.ModeDevice {
    71  				found = true
    72  				device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src)))
    73  				if devmode != "" {
    74  					device = fmt.Sprintf("%s:%s", device, devmode)
    75  				}
    76  				if err := addDevice(g, device); err != nil {
    77  					return fmt.Errorf("failed to add %s device: %w", dpath, err)
    78  				}
    79  			}
    80  			return nil
    81  		}); err != nil {
    82  			return err
    83  		}
    84  		if !found {
    85  			return fmt.Errorf("no devices found in %s: %w", devicePath, unix.EINVAL)
    86  		}
    87  		return nil
    88  	}
    89  	return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
    90  }
    91  
    92  func addDevice(g *generate.Generator, device string) error {
    93  	src, dst, permissions, err := ParseDevice(device)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	if src != dst {
    98  		return fmt.Errorf("container device must be the same as host device on FreeBSD")
    99  	}
   100  	mode := 0
   101  	if strings.Contains(permissions, "r") {
   102  		mode |= unix.S_IRUSR
   103  	}
   104  	if strings.Contains(permissions, "w") {
   105  		mode |= unix.S_IWUSR
   106  	}
   107  	// Find the devfs mount so that we can add rules to expose the device
   108  	for k, m := range g.Config.Mounts {
   109  		if m.Type == "devfs" {
   110  			if dev, ok := strings.CutPrefix(src, "/dev/"); ok {
   111  				m.Options = append(m.Options,
   112  					fmt.Sprintf("rule=path %s unhide mode %04o", dev, mode))
   113  			} else {
   114  				return fmt.Errorf("expected device to start with \"/dev\": %v", dev)
   115  			}
   116  			g.Config.Mounts[k] = m
   117  			return nil
   118  		}
   119  	}
   120  	return fmt.Errorf("devfs not found in generator")
   121  }