Skip to content

Commit 3c6ea2c

Browse files
committed
9p: initial version
1 parent 74e5cf5 commit 3c6ea2c

File tree

7 files changed

+197
-0
lines changed

7 files changed

+197
-0
lines changed

cmd/crc/cmd/daemon.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/crc-org/crc/v2/pkg/crc/constants"
2525
"github.com/crc-org/crc/v2/pkg/crc/daemonclient"
2626
"github.com/crc-org/crc/v2/pkg/crc/logging"
27+
"github.com/crc-org/crc/v2/pkg/fileserver/fs9p"
2728
"github.com/crc-org/machine/libmachine/drivers"
2829
"github.com/docker/go-units"
2930
"github.com/gorilla/handlers"
@@ -248,6 +249,15 @@ func run(configuration *types.Configuration) error {
248249
}
249250
}()
250251

252+
// 9p home directory sharing
253+
listener9p, err := vn.Listen("tcp", net.JoinHostPort(configuration.GatewayIP, fmt.Sprintf("%d", constants.Plan9Port)))
254+
if err != nil {
255+
return err
256+
}
257+
if err := fs9p.StartShares([]fs9p.Mount9p{{Listener: listener9p, Path: constants.GetHomeDir()}}); err != nil {
258+
return err
259+
}
260+
251261
startupDone()
252262

253263
if logging.IsDebug() {

pkg/crc/constants/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ const (
5555
OpenShiftIngressHTTPSPort = 443
5656

5757
BackgroundLauncherExecutable = "crc-background-launcher.exe"
58+
59+
Plan9Port = 564
60+
Plan9Msize = 1024 * 1024
5861
)
5962

6063
var adminHelperExecutableForOs = map[string]string{

pkg/crc/machine/start.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ func configureSharedDirs(vm *virtualMachine, sshRunner *crcssh.Runner) error {
248248
if _, _, err := sshRunner.RunPrivileged(fmt.Sprintf("Mounting %s", mount.Target), "mount", "-o", "context=\"system_u:object_r:container_file_t:s0\"", "-t", mount.Type, mount.Tag, mount.Target); err != nil {
249249
return err
250250
}
251+
251252
case "cifs":
252253
smbUncPath := fmt.Sprintf("//%s/%s", hostVirtualIP, mount.Tag)
253254
if _, _, err := sshRunner.RunPrivate("sudo", "mount", "-o", fmt.Sprintf("rw,uid=core,gid=core,username='%s',password='%s'", mount.Username, mount.Password), "-t", mount.Type, smbUncPath, mount.Target); err != nil {
@@ -257,6 +258,16 @@ func configureSharedDirs(vm *virtualMachine, sshRunner *crcssh.Runner) error {
257258
}
258259
return fmt.Errorf("Failed to mount CIFS/SMB share '%s' please make sure configured password is correct: %w", mount.Tag, err)
259260
}
261+
262+
case "9p":
263+
// change owner to core user to allow mounting to it as a non-root user
264+
if _, _, err := sshRunner.RunPrivileged("Changing owner of mount directory", "chown core:core", mount.Target); err != nil {
265+
return err
266+
}
267+
if _, _, err := sshRunner.Run("9pfs 192.168.127.1", mount.Target); err != nil {
268+
return err
269+
}
270+
260271
default:
261272
return fmt.Errorf("Unknown Shared dir type requested: %s", mount.Type)
262273
}

pkg/fileserver/fs9p/server.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package fs9p
2+
3+
import (
4+
"fmt"
5+
"net"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/DeedleFake/p9"
10+
"github.com/DeedleFake/p9/proto"
11+
"github.com/crc-org/crc/v2/pkg/crc/constants"
12+
"github.com/sirupsen/logrus"
13+
)
14+
15+
type Server struct {
16+
// Listener this server is bound to
17+
Listener net.Listener
18+
// Directory this server exposes
19+
ExposedDir string
20+
// Errors from the server being started will come out here
21+
ErrChan chan error
22+
}
23+
24+
// New9pServer exposes a single directory (and all children) via the given net.Listener.
25+
// Directory given must be an absolute path and must exist.
26+
func New9pServer(listener net.Listener, exposeDir string) (*Server, error) {
27+
// Verify that exposeDir makes sense.
28+
if !filepath.IsAbs(exposeDir) {
29+
return nil, fmt.Errorf("path to expose to machine must be absolute: %s", exposeDir)
30+
}
31+
stat, err := os.Stat(exposeDir)
32+
if err != nil {
33+
return nil, fmt.Errorf("cannot stat path to expose to machine: %w", err)
34+
}
35+
if !stat.IsDir() {
36+
return nil, fmt.Errorf("path to expose to machine must be a directory: %s", exposeDir)
37+
}
38+
39+
errChan := make(chan error)
40+
41+
fs := p9.FileSystem(p9.Dir(exposeDir))
42+
43+
go func() {
44+
errChan <- proto.Serve(listener, p9.Proto(), p9.FSConnHandler(fs, constants.Plan9Msize))
45+
close(errChan)
46+
}()
47+
48+
toReturn := new(Server)
49+
toReturn.Listener = listener
50+
toReturn.ExposedDir = exposeDir
51+
toReturn.ErrChan = errChan
52+
53+
// Just before returning, check to see if we got an error off server startup.
54+
select {
55+
case err := <-errChan:
56+
return nil, fmt.Errorf("starting 9p server: %w", err)
57+
default:
58+
logrus.Infof("Successfully started 9p server on %s for directory %s", listener.Addr().String(), exposeDir)
59+
}
60+
61+
return toReturn, nil
62+
}
63+
64+
// Stop a running server.
65+
// Please note that this does *BAD THINGS* to clients if they are still running
66+
// when the server stops. Processes get stuck in I/O deep sleep and zombify, and
67+
// nothing I do other than restarting the VM can remove the zombies.
68+
func (s *Server) Stop() error {
69+
if err := s.Listener.Close(); err != nil {
70+
return err
71+
}
72+
logrus.Infof("Successfully stopped 9p server for directory %s", s.ExposedDir)
73+
return nil
74+
}
75+
76+
// WaitForError from a running server.
77+
func (s *Server) WaitForError() error {
78+
err := <-s.ErrChan
79+
return err
80+
}

pkg/fileserver/fs9p/shares.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package fs9p
2+
3+
import (
4+
"fmt"
5+
"net"
6+
7+
"github.com/sirupsen/logrus"
8+
)
9+
10+
type Mount struct {
11+
Listener net.Listener
12+
Path string
13+
}
14+
15+
func StartShares(plan9Mounts []Mount) (defErr error) {
16+
for _, m := range plan9Mounts {
17+
server, err := New9pServer(m.Listener, m.Path)
18+
if err != nil {
19+
return fmt.Errorf("serving directory %s on %s: %w", m.Path, m.Listener.Addr().String(), err)
20+
}
21+
defer func() {
22+
if defErr != nil {
23+
if err := server.Stop(); err != nil {
24+
logrus.Errorf("Error stopping 9p server: %v", err)
25+
}
26+
}
27+
}()
28+
29+
serverDir := m.Path
30+
31+
go func() {
32+
if err := server.WaitForError(); err != nil {
33+
logrus.Errorf("Error from 9p server on %s for %s: %v", m.Listener.Addr().String(), serverDir, err)
34+
} else {
35+
// We do not expect server exits - this should run until the program exits.
36+
logrus.Warnf("9p server on %s for %s exited without error", m.Listener.Addr().String(), serverDir)
37+
}
38+
}()
39+
}
40+
41+
return nil
42+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//go:build !windows
2+
3+
package fs9p
4+
5+
import (
6+
"fmt"
7+
)
8+
9+
func StartHvsockShares(mounts map[string]string) error {
10+
if len(mounts) == 0 {
11+
return nil
12+
}
13+
14+
return fmt.Errorf("this platform does not support sharing directories")
15+
}

pkg/fileserver/fs9p/shares_windows.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package fs9p
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/linuxkit/virtsock/pkg/hvsock"
7+
"github.com/sirupsen/logrus"
8+
)
9+
10+
// Start serving the given shares on Windows HVSocks for use by a Hyper-V VM.
11+
// Mounts is formatted as a map of directory to be shared to vsock GUID.
12+
// The vsocks used must already be defined before StartShares is called; it's
13+
// expected that the vsocks will be created and torn down by the program calling
14+
// gvproxy.
15+
// TODO: The map here probably doesn't make sense.
16+
func StartHvsockShares(mounts map[string]string) (defErr error) {
17+
plan9Mounts := []Mount{}
18+
for path, guid := range mounts {
19+
service, err := hvsock.GUIDFromString(guid)
20+
if err != nil {
21+
return fmt.Errorf("parsing vsock guid %s: %w", guid, err)
22+
}
23+
24+
listener, err := hvsock.Listen(hvsock.Addr{
25+
VMID: hvsock.GUIDWildcard,
26+
ServiceID: service,
27+
})
28+
if err != nil {
29+
return fmt.Errorf("retrieving listener for vsock %s: %w", guid, err)
30+
}
31+
32+
logrus.Debugf("Going to serve directory %s on vsock %s", path, guid)
33+
plan9Mounts = append(plan9Mounts, Mount{Listener: listener, Path: path})
34+
}
35+
return StartShares(plan9Mounts)
36+
}

0 commit comments

Comments
 (0)