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 }