Skip to content

Commit

Permalink
Merge pull request #20358 from mheon/9p
Browse files Browse the repository at this point in the history
Initial addition of 9p code to Podman
  • Loading branch information
openshift-ci[bot] authored Oct 31, 2023
2 parents f5b2cf1 + 7153124 commit 55b9ea3
Show file tree
Hide file tree
Showing 296 changed files with 45,145 additions and 83 deletions.
131 changes: 131 additions & 0 deletions cmd/podman/machine/client9p.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//go:build linux && (amd64 || arm64)
// +build linux
// +build amd64 arm64

package machine

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"

"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/mdlayher/vsock"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
client9pCommand = &cobra.Command{
Args: cobra.ExactArgs(2),
Use: "client9p PORT DIR",
Hidden: true,
Short: "Mount a remote directory using 9p over hvsock",
Long: "Connect to the given hvsock port using 9p and mount the served filesystem at the given directory",
RunE: remoteDirClient,
ValidArgsFunction: completion.AutocompleteNone,
Example: `podman system client9p 55000 /mnt`,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: client9pCommand,
Parent: machineCmd,
})
}

func remoteDirClient(cmd *cobra.Command, args []string) error {
port, err := strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("error parsing port number: %w", err)
}

if err := client9p(uint32(port), args[1]); err != nil {
return err
}

return nil
}

// This is Linux-only as we only intend for this function to be used inside the
// `podman machine` VM, which is guaranteed to be Linux.
func client9p(portNum uint32, mountPath string) error {
cleanPath, err := filepath.Abs(mountPath)
if err != nil {
return fmt.Errorf("absolute path for %s: %w", mountPath, err)
}
mountPath = cleanPath

// Mountpath needs to exist and be a directory
stat, err := os.Stat(mountPath)
if err != nil {
return fmt.Errorf("stat %s: %w", mountPath, err)
}
if !stat.IsDir() {
return fmt.Errorf("path %s is not a directory", mountPath)
}

logrus.Infof("Going to mount 9p on vsock port %d to directory %s", portNum, mountPath)

// Host connects to non-hypervisor processes on the host running the VM.
conn, err := vsock.Dial(vsock.Host, portNum, nil)
if err != nil {
return fmt.Errorf("dialing vsock port %d: %w", portNum, err)
}
defer func() {
if err := conn.Close(); err != nil {
logrus.Errorf("Error closing vsock: %v", err)
}
}()

// vsock doesn't give us direct access to the underlying FD. That's kind
// of inconvenient, because we have to pass it off to mount.
// However, it does give us the ability to get a syscall.RawConn, which
// has a method that allows us to run a function that takes the FD
// number as an argument.
// Which ought to be good enough? Probably?
// Overall, this is gross and I hate it, but I don't see a better way.
rawConn, err := conn.SyscallConn()
if err != nil {
return fmt.Errorf("getting vsock raw conn: %w", err)
}
errChan := make(chan error, 1)
runMount := func(fd uintptr) {
vsock := os.NewFile(fd, "vsock")
if vsock == nil {
errChan <- fmt.Errorf("could not convert vsock fd to os.File")
return
}

// This is ugly, but it lets us use real kernel mount code,
// instead of maintaining our own FUSE 9p implementation.
cmd := exec.Command("mount", "-t", "9p", "-o", "trans=fd,rfdno=3,wfdno=3,version=9p2000.L", "9p", mountPath)
cmd.ExtraFiles = []*os.File{vsock}

output, err := cmd.CombinedOutput()
if err != nil {
err = fmt.Errorf("running mount: %w\nOutput: %s", err, string(output))
} else {
logrus.Debugf("Mount output: %s", string(output))
logrus.Infof("Mounted directory %s using 9p", mountPath)
}

errChan <- err
close(errChan)
}
if err := rawConn.Control(runMount); err != nil {
return fmt.Errorf("running mount function for dir %s: %w", mountPath, err)
}

if err := <-errChan; err != nil {
return fmt.Errorf("mounting filesystem %s: %w", mountPath, err)
}

logrus.Infof("Mount of filesystem %s successful", mountPath)

return nil
}
101 changes: 101 additions & 0 deletions cmd/podman/machine/server9p.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//go:build windows && (amd64 || arm64)
// +build windows
// +build amd64 arm64

package machine

import (
"fmt"
"strconv"
"strings"
"time"

"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/fileserver"
psutil "github.com/shirou/gopsutil/v3/process"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
server9pCommand = &cobra.Command{
Args: cobra.ExactArgs(1),
Use: "server9p [options] PID",
Hidden: true,
Short: "Serve a directory using 9p over hvsock",
Long: "Start a number of 9p servers on given hvsock UUIDs, and run until the given PID exits",
RunE: remoteDirServer,
ValidArgsFunction: completion.AutocompleteNone,
Example: `podman system server9p --serve C:\Users\myuser:00000050-FACB-11E6-BD58-64006A7986D3 /mnt`,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: server9pCommand,
Parent: machineCmd,
})

flags := server9pCommand.Flags()

serveFlagName := "serve"
flags.StringArrayVar(&serveDirs, serveFlagName, []string{}, "directories to serve and UUID of vsock to serve on, colon-separated")
_ = server9pCommand.RegisterFlagCompletionFunc(serveFlagName, completion.AutocompleteNone)
}

var (
serveDirs []string
)

func remoteDirServer(cmd *cobra.Command, args []string) error {
pid, err := strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("parsing PID: %w", err)
}
if pid < 0 {
return fmt.Errorf("PIDs cannot be negative")
}

if len(serveDirs) == 0 {
return fmt.Errorf("must provide at least one directory to serve")
}

// TODO: need to support options here
shares := make(map[string]string, len(serveDirs))
for _, share := range serveDirs {
splitShare := strings.Split(share, ":")
if len(splitShare) < 2 {
return fmt.Errorf("paths passed to --share must include an hvsock GUID")
}

// Every element but the last one is the real filepath to share
path := strings.Join(splitShare[:len(splitShare)-1], ":")

shares[path] = splitShare[len(splitShare)-1]
}

if err := fileserver.StartShares(shares); err != nil {
return err
}

p, err := psutil.NewProcess(int32(pid))
if err != nil {
return fmt.Errorf("opening gvproxy PID: %w", err)
}
for {
running, err := p.IsRunning()
if err != nil {
return fmt.Errorf("checking is gvproxy is dead: %w", err)
}
if !running {
break
}

time.Sleep(1 * time.Second)
}

logrus.Infof("Exiting cleanly as PID %d has died", pid)

return nil
}
12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ require (
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hugelgupf/p9 v0.3.1-0.20230822151754-54f5c5530921
github.com/json-iterator/go v1.1.12
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2
github.com/mattn/go-shellwords v1.0.12
github.com/mattn/go-sqlite3 v1.14.17
github.com/mdlayher/vsock v1.2.1
github.com/moby/term v0.5.0
github.com/nxadm/tail v1.4.11
github.com/onsi/ginkgo/v2 v2.13.0
Expand All @@ -54,6 +57,7 @@ require (
github.com/opencontainers/selinux v1.11.0
github.com/openshift/imagebuilder v1.2.5
github.com/rootless-containers/rootlesskit v1.1.1
github.com/shirou/gopsutil/v3 v3.23.9
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
Expand Down Expand Up @@ -145,10 +149,12 @@ require (
github.com/kr/fs v0.1.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/letsencrypt/boulder v0.0.0-20230213213521-fdfea0d469b6 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand All @@ -165,11 +171,13 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/proglottis/gpgme v0.1.3 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sigstore/fulcio v1.4.0 // indirect
github.com/sigstore/rekor v1.2.2 // indirect
github.com/sigstore/sigstore v1.7.3 // indirect
Expand All @@ -179,10 +187,14 @@ require (
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/theupdateframework/go-tuf v0.5.2 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.opencensus.io v0.24.0 // indirect
Expand Down
Loading

1 comment on commit 55b9ea3

@packit-as-a-service
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podman-next COPR build failed. @containers/packit-build please check.

Please sign in to comment.