-
Notifications
You must be signed in to change notification settings - Fork 256
9p file sharing #4866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
9p file sharing #4866
Changes from all commits
7892920
ba3ce22
080b96d
eff3c6e
3a8d64a
2fb2562
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| package fs9p | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "net" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
|
|
||
| "github.com/DeedleFake/p9" | ||
| "github.com/DeedleFake/p9/proto" | ||
| "github.com/crc-org/crc/v2/pkg/crc/constants" | ||
| "github.com/sirupsen/logrus" | ||
| ) | ||
|
|
||
| type Server struct { | ||
| // Listener this server is bound to | ||
| Listener net.Listener | ||
|
|
||
| // Plan9 Filesystem type that holds the exposed directory | ||
| Filesystem p9.FileSystem | ||
|
|
||
| // Directory this server exposes | ||
| ExposedDir string | ||
|
|
||
| // Errors from the server being started will come out here | ||
| ErrChan chan error | ||
| } | ||
|
|
||
| // New9pServer exposes a single directory (and all children) via the given net.Listener | ||
| // and returns the server struct. | ||
| // Directory given must be an absolute path and must exist. | ||
| func New9pServer(listener net.Listener, exposeDir string) (*Server, 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) | ||
| } | ||
|
|
||
| fs := p9.FileSystem(p9.Dir(exposeDir)) | ||
| // set size to 1 making channel buffered to prevent proto.Serve blocking | ||
| errChan := make(chan error, 1) | ||
|
|
||
| toReturn := new(Server) | ||
| toReturn.Listener = listener | ||
| toReturn.Filesystem = fs | ||
| toReturn.ExposedDir = exposeDir | ||
| toReturn.ErrChan = errChan | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| return toReturn, nil | ||
| } | ||
|
|
||
| // Start a server created by New9pServer. | ||
| func (s *Server) Start() error { | ||
| go func() { | ||
| s.ErrChan <- proto.Serve(s.Listener, p9.Proto(), p9.FSConnHandler(s.Filesystem, constants.Plan9Msize)) | ||
| close(s.ErrChan) | ||
| }() | ||
|
|
||
| // Just before returning, check to see if we got an error off server startup. | ||
| select { | ||
| case err := <-s.ErrChan: | ||
| return fmt.Errorf("starting 9p server: %w", err) | ||
| default: | ||
| logrus.Infof("started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir) | ||
| return 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 other than restarting the VM can remove the zombies. | ||
| func (s *Server) Stop() error { | ||
| if err := s.Listener.Close(); err != nil { | ||
| return err | ||
| } | ||
| logrus.Infof("stopped 9p server for directory %s", s.ExposedDir) | ||
| return nil | ||
| } | ||
|
redbeam marked this conversation as resolved.
|
||
|
|
||
| // WaitForError from a running server. | ||
| func (s *Server) WaitForError() error { | ||
| err := <-s.ErrChan | ||
| // captures "accept tcp: endpoint is in invalid state" errors on exit | ||
| var opErr *net.OpError | ||
| if errors.As(err, &opErr) && strings.Contains(opErr.Error(), "endpoint is in invalid state") { | ||
| return nil | ||
| } | ||
| return err | ||
| } | ||
|
redbeam marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| //go:build !windows | ||
|
|
||
| package fs9p | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net" | ||
| "runtime" | ||
| ) | ||
|
|
||
| // GetHvsockListener returns a net.Listener listening on the specified hvsock. | ||
| // The used hvsock must already be defined before this function is called. | ||
| func GetHvsockListener(hvsockGUID string) (net.Listener, error) { | ||
| return nil, fmt.Errorf("GetHvsockListener() not implemented on %s", runtime.GOOS) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| //go:build windows | ||
|
|
||
| package fs9p | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net" | ||
|
|
||
| "github.com/linuxkit/virtsock/pkg/hvsock" | ||
| ) | ||
|
|
||
| // GetHvsockListener returns a net.Listener listening on the specified hvsock. | ||
| // The used hvsock must already be defined before this function is called. | ||
| func GetHvsockListener(hvsockGUID string) (net.Listener, error) { | ||
| service, err := hvsock.GUIDFromString(hvsockGUID) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("parsing hvsock guid %s: %w", hvsockGUID, err) | ||
| } | ||
|
|
||
| listener, err := hvsock.Listen(hvsock.Addr{ | ||
| VMID: hvsock.GUIDWildcard, | ||
| ServiceID: service, | ||
| }) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("retrieving listener for hvsock %s: %w", hvsockGUID, err) | ||
| } | ||
|
|
||
| return listener, nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| @story_9pfs @windows | ||
| Feature: Verify 9pfs mount works | ||
|
|
||
| Verify 9pfs mount of user's home directory is mounted, accessible | ||
| and that basic file operations work. Only relevant on Windows. | ||
|
|
||
| Scenario: Test mounted directory functionality | ||
| Given directory "/mnt/c/Users/core" exists in VM | ||
| And filesystem is mounted | ||
| Then listing files in directory "/mnt/c/Users/core" should succeed | ||
| And basic file operations in directory "/mnt/c/Users/core" should succeed | ||
| And basic directory operations in directory "/mnt/c/Users/core" should succeed | ||
|
Comment on lines
+7
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider parameterizing the hardcoded mount path. The scenario hardcodes
Example improvement for line 9: - And filesystem is mounted
+ And filesystem is mounted at "/mnt/c/Users/core"This would require updating the step definition as suggested in the testsuite.go review.
🤖 Prompt for AI Agents |
||
Uh oh!
There was an error while loading. Please reload this page.