-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20358 from mheon/9p
Initial addition of 9p code to Podman
- Loading branch information
Showing
296 changed files
with
45,145 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
55b9ea3
There was a problem hiding this comment.
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.