Files
device-mapping-manager/main.go
Roman Vaníček 2a8b859562
All checks were successful
continuous-integration/drone/push Build is passing
Further application of /host prefix
2023-09-25 19:19:00 +02:00

225 lines
5.2 KiB
Go

//go:build linux
package main
// #include "ctypes.h"
import "C"
import (
"context"
"device-volume-driver/internal/cgroup"
"fmt"
"log"
"os"
"os/user"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/docker/go-plugins-helpers/volume"
_ "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
)
const pluginId = "dvd"
const rootPath = "/host"
type fakeDriver struct {
}
func Ptr[T any](v T) *T {
return &v
}
func main() {
listenForMounts()
}
func getDeviceInfo(devicePath string) (string, int64, int64, error) {
var stat unix.Stat_t
if err := unix.Stat(devicePath, &stat); err != nil {
log.Println(err)
return "", -1, -1, err
}
var deviceType string
switch stat.Mode & unix.S_IFMT {
case unix.S_IFBLK:
deviceType = "b"
case unix.S_IFCHR:
deviceType = "c"
default:
log.Println("aborting: device is neither a character or block device")
return "", -1, -1, fmt.Errorf("unsupported device type... aborting")
}
major := int64(unix.Major(stat.Rdev))
minor := int64(unix.Minor(stat.Rdev))
log.Printf("Found device: %s %s %d:%d\n", devicePath, deviceType, major, minor)
return deviceType, major, minor, nil
}
func listenForMounts() {
ctx := context.Background()
d := &fakeDriver{}
h := volume.NewHandler(d)
u, _ := user.Lookup("root")
gid, _ := strconv.Atoi(u.Gid)
go h.ServeUnix("/run/docker/plugins/dvd.sock", gid)
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
log.Fatal(err)
}
defer cli.Close()
msgs, errs := cli.Events(
ctx,
types.EventsOptions{Filters: filters.NewArgs(filters.Arg("event", "start"))},
)
for {
select {
case err := <-errs:
log.Fatal(err)
case msg := <-msgs:
info, err := cli.ContainerInspect(ctx, msg.Actor.ID)
if err != nil {
panic(err)
} else {
pid := info.State.Pid
version, err := cgroup.GetDeviceCGroupVersion(rootPath, pid)
log.Printf("The cgroup version for process %d is: %v\n", pid, version)
if err != nil {
log.Println(err)
break
}
log.Printf("Checking mounts for process %d\n", pid)
for _, mount := range info.Mounts {
log.Printf(
"%s/%v requested a volume mount for %s at %s\n",
msg.Actor.ID, info.State.Pid, mount.Source, mount.Destination,
)
if !strings.HasPrefix(mount.Source, "/dev") {
log.Printf("%s is not a device... skipping\n", mount.Source)
continue
}
api, err := cgroup.New(version)
cgroupPath, sysfsPath, err := api.GetDeviceCGroupMountPath(rootPath, pid)
if err != nil {
log.Println(err)
break
}
cgroupPath = path.Join(rootPath, sysfsPath, cgroupPath)
log.Printf("The cgroup path for process %d is at %v\n", pid, cgroupPath)
if fileInfo, err := os.Stat(mount.Source); err != nil {
log.Println(err)
continue
} else {
if fileInfo.IsDir() {
err := filepath.Walk(mount.Source,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
} else if info.IsDir() {
return nil
} else if err = applyDeviceRules(api, path, cgroupPath, pid); err != nil {
log.Println(err)
}
return nil
})
if err != nil {
log.Println(err)
}
} else {
if err = applyDeviceRules(api, mount.Source, cgroupPath, pid); err != nil {
log.Println(err)
}
}
}
}
}
}
}
}
func applyDeviceRules(api cgroup.Interface, mountPath string, cgroupPath string, pid int) error {
deviceType, major, minor, err := getDeviceInfo(mountPath)
if err != nil {
log.Println(err)
return err
} else {
log.Printf("Adding device rule for process %d at %s\n", pid, cgroupPath)
err = api.AddDeviceRules(cgroupPath, []cgroup.DeviceRule{
{
Access: "rwm",
Major: Ptr[int64](major),
Minor: Ptr[int64](minor),
Type: deviceType,
Allow: true,
},
})
if err != nil {
log.Println(err)
return err
}
}
return nil
}
func (d *fakeDriver) Capabilities() *volume.CapabilitiesResponse {
return &volume.CapabilitiesResponse{Capabilities: volume.Capability{Scope: "local"}}
}
func (d *fakeDriver) Create(r *volume.CreateRequest) error {
return fmt.Errorf("This driver device-mapping-manager cannot create volumes.")
}
func (d *fakeDriver) List() (*volume.ListResponse, error) {
var vols []*volume.Volume
return &volume.ListResponse{Volumes: vols}, nil
}
func (d *fakeDriver) Get(r *volume.GetRequest) (*volume.GetResponse, error) {
return &volume.GetResponse{}, fmt.Errorf("volume %s does not exist", r.Name)
}
func (d *fakeDriver) Remove(r *volume.RemoveRequest) error {
return fmt.Errorf("volume %s does not exist", r.Name)
}
func (d *fakeDriver) Path(r *volume.PathRequest) (*volume.PathResponse, error) {
return &volume.PathResponse{}, fmt.Errorf("volume %s does not exist", r.Name)
}
func (d *fakeDriver) Mount(r *volume.MountRequest) (*volume.MountResponse, error) {
return &volume.MountResponse{}, fmt.Errorf("volume %s does not exist", r.Name)
}
func (d *fakeDriver) Unmount(r *volume.UnmountRequest) error {
return fmt.Errorf("volume %s does not exist", r.Name)
}