-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for volume sharing on gvproxy for Windows
Specifically, gvproxy will act as a 9p server, serving shares requested by the caller on HyperV vsocks, to allow the guest VM to access content on the host. The vsocks are intended to be managed by the caller in this model. As such, gvproxy receives the path to be shared and a vsock port number to share it on via CLI. An arbitrary number of these are accepted, as each share needs a separate server and vsock (they will be mounted by the Linux kernel 9p code within the guest VM, which does not support multiplexing multiple shares over a single vsock). Signed-off-by: Matthew Heon <[email protected]>
- Loading branch information
Showing
104 changed files
with
14,628 additions
and
212 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
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
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,94 @@ | ||
package plan9 | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/hugelgupf/p9/fsimpl/localfs" | ||
"github.com/hugelgupf/p9/p9" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type Plan9Server struct { | ||
server *p9.Server | ||
// TODO: Once server has a proper Close() we don't need this. | ||
// This is basically just a short-circuit to actually close the server | ||
// without that ability. | ||
listener net.Listener | ||
// Errors from the server being started will come out here. | ||
errChan chan error | ||
} | ||
|
||
// Expose a single directory (and all children) via the given net.Listener. | ||
// Directory given must be an absolute path and must exist. | ||
func New9pServer(listener net.Listener, exposeDir string) (*Plan9Server, error) { | ||
// Verify that exposeDir makes sense. | ||
if !filepath.IsAbs(exposeDir) { | ||
return nil, fmt.Errorf("path to expose to machine must be absolute: %s", exposeDir) | ||
} | ||
stat, err := os.Stat(exposeDir) | ||
if err != nil { | ||
return nil, fmt.Errorf("cannot stat path to expose to machine: %w", err) | ||
} | ||
if !stat.IsDir() { | ||
return nil, fmt.Errorf("path to expose to machine must be a directory: %s", exposeDir) | ||
} | ||
|
||
server := p9.NewServer(localfs.Attacher(exposeDir), []p9.ServerOpt{}...) | ||
if server == nil { | ||
return nil, fmt.Errorf("p9.NewServer returned nil") | ||
} | ||
|
||
errChan := make(chan error) | ||
|
||
// TODO: Use a channel to pass back this if it occurs within a | ||
// reasonable timeframe. | ||
go func() { | ||
errChan <- server.Serve(listener) | ||
close(errChan) | ||
}() | ||
|
||
toReturn := new(Plan9Server) | ||
toReturn.listener = listener | ||
toReturn.server = server | ||
toReturn.errChan = errChan | ||
|
||
// Just before returning, check to see if we got an error off server | ||
// startup. | ||
select { | ||
case err := <-errChan: | ||
return nil, fmt.Errorf("starting 9p server: %w", err) | ||
default: | ||
logrus.Infof("Successfully started 9p server for directory %s", exposeDir) | ||
} | ||
|
||
return toReturn, nil | ||
} | ||
|
||
// Stop a running server. | ||
// Please note that this does *BAD THINGS* to clients if they are still running | ||
// when the server stops. Processes get stuck in I/O deep sleep and zombify, and | ||
// nothing I do save restarting the VM can remove the zombies. | ||
func (s *Plan9Server) Stop() error { | ||
if s.server != nil { | ||
if err := s.listener.Close(); err != nil { | ||
return err | ||
} | ||
s.server = nil | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Wait for an error from a running server. | ||
func (s *Plan9Server) WaitForError() error { | ||
if s.server != nil { | ||
err := <-s.errChan | ||
return err | ||
} | ||
|
||
// Server already down, return nil | ||
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,12 @@ | ||
//go:build !windows | ||
// +build !windows | ||
|
||
package fileserver | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
func StartShares(mounts map[string]uint64) error { | ||
return fmt.Errorf("this platform does not support sharing directories") | ||
} |
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,59 @@ | ||
package fileserver | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/containers/gvisor-tap-vsock/pkg/vsock" | ||
"github.com/containers/gvisor-tap-vsock/pkg/fileserver/plan9" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
// Start serving the given shares on Windows HVSocks for use by a Hyper-V VM. | ||
// Mounts is formatted as a map of directory to be shared to vsock port number. | ||
// The vsocks used must already be defined before StartShares is called; it's | ||
// expected that the vsocks will be created and torn down by the program calling | ||
// gvproxy. | ||
// TODO: The map here probably doesn't make sense. | ||
// In the future, possibly accept a struct instead, so we can map to things | ||
// other than a vsock. | ||
func StartShares(mounts map[string]uint64) (defErr error) { | ||
for path, vsockNum := range mounts { | ||
hvsock, err := vsock.LoadHVSockRegistryEntry(vsockNum) | ||
if err != nil { | ||
return fmt.Errorf("retrieving vsock %d for share %s: %w", vsockNum, path, err) | ||
} | ||
|
||
listener, err := hvsock.Listener() | ||
if err != nil { | ||
return fmt.Errorf("retrieving listener for vsock %d (share %s): %w", vsockNum, path, err) | ||
} | ||
|
||
logrus.Debugf("Going to serve directory %s on vsock port %d", path, vsockNum) | ||
|
||
server, err := plan9.New9pServer(listener, path) | ||
if err != nil { | ||
return fmt.Errorf("serving directory %s on vsock %d: %w", path, vsockNum, err) | ||
} | ||
defer func() { | ||
if defErr != nil { | ||
if err := server.Stop(); err != nil { | ||
logrus.Errorf("Error stopping 9p server: %v", err) | ||
} | ||
} | ||
}() | ||
|
||
serverDir := path | ||
|
||
go func() { | ||
if err := server.WaitForError(); err != nil { | ||
logrus.Errorf("Error from 9p server for %s: %v", path, err) | ||
} else { | ||
// We do not expect server exits - this should | ||
// run until the program exits. | ||
logrus.Warnf("9p server for %s exited without error", serverDir) | ||
} | ||
}() | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.