Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions cmd/crc/cmd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"os/signal"
"regexp"
"runtime"
"syscall"
"time"

Expand All @@ -24,6 +25,7 @@ import (
"github.com/crc-org/crc/v2/pkg/crc/constants"
"github.com/crc-org/crc/v2/pkg/crc/daemonclient"
"github.com/crc-org/crc/v2/pkg/crc/logging"
"github.com/crc-org/crc/v2/pkg/fileserver/fs9p"
"github.com/crc-org/machine/libmachine/drivers"
"github.com/docker/go-units"
"github.com/gorilla/handlers"
Expand Down Expand Up @@ -177,7 +179,7 @@ func run(configuration *types.Configuration) error {
}
}()

ln, err := vn.Listen("tcp", fmt.Sprintf("%s:80", configuration.GatewayIP))
ln, err := vn.Listen("tcp", net.JoinHostPort(configuration.GatewayIP, "80"))
if err != nil {
return err
}
Expand All @@ -193,7 +195,7 @@ func run(configuration *types.Configuration) error {
}
}()

networkListener, err := vn.Listen("tcp", fmt.Sprintf("%s:80", hostVirtualIP))
networkListener, err := vn.Listen("tcp", net.JoinHostPort(hostVirtualIP, "80"))
if err != nil {
return err
}
Expand Down Expand Up @@ -248,6 +250,55 @@ func run(configuration *types.Configuration) error {
}
}()

// 9p home directory sharing
if runtime.GOOS == "windows" && config.Get(crcConfig.EnableSharedDirs).AsBool() {
// 9p over hvsock
listener9pHvsock, err := fs9p.GetHvsockListener(constants.Plan9HvsockGUID)
if err != nil {
return err
}
server9pHvsock, err := fs9p.New9pServer(listener9pHvsock, constants.GetHomeDir())
if err != nil {
return err
}
if err := server9pHvsock.Start(); err != nil {
return err
}
defer func() {
if err := server9pHvsock.Stop(); err != nil {
logging.Warnf("error stopping 9p server (hvsock): %v", err)
}
}()
go func() {
if err := server9pHvsock.WaitForError(); err != nil {
logging.Errorf("9p server (hvsock) error: %v", err)
}
}()

// 9p over TCP (as a backup)
listener9pTCP, err := vn.Listen("tcp", net.JoinHostPort(configuration.GatewayIP, fmt.Sprintf("%d", constants.Plan9TcpPort)))
if err != nil {
return err
}
server9pTCP, err := fs9p.New9pServer(listener9pTCP, constants.GetHomeDir())
if err != nil {
return err
}
if err := server9pTCP.Start(); err != nil {
return err
}
defer func() {
if err := server9pTCP.Stop(); err != nil {
logging.Warnf("error stopping 9p server (tcp): %v", err)
}
}()
go func() {
if err := server9pTCP.WaitForError(); err != nil {
logging.Errorf("9p server (tcp) error: %v", err)
}
}()
}

startupDone()

if logging.IsDebug() {
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.24.2

require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/DeedleFake/p9 v0.7.1
github.com/Masterminds/semver/v3 v3.4.0
github.com/Microsoft/go-winio v0.6.2
github.com/ProtonMail/go-crypto v1.3.0
Expand All @@ -27,6 +28,7 @@ require (
github.com/klauspost/compress v1.18.1
github.com/klauspost/cpuid/v2 v2.3.0
github.com/kofalt/go-memoize v0.0.0-20220914132407-0b5d6a304579
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2
github.com/mattn/go-colorable v0.1.14
github.com/mdlayher/vsock v1.2.1
github.com/onsi/ginkgo/v2 v2.27.2
Expand Down Expand Up @@ -130,7 +132,6 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkk
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DeedleFake/p9 v0.7.1 h1:HS4iPmbxR32pmtUyjNxXIf7eFih07Rxp1E0R6fqA7ik=
github.com/DeedleFake/p9 v0.7.1/go.mod h1:xFOErTOZm4lVysPCTrRtTm1c8J0Pfg0jGEC+re1AnIo=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
Expand Down
6 changes: 6 additions & 0 deletions packaging/windows/product.wxs.template
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
<RegistryValue Type="string" Name="ElementName" Value="gvisor-tap-vsock" KeyPath="yes"/>
</RegistryKey>
</Component>
<Component Id="Hvsock9pRegistryEntry" Guid="*">
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices\00009000-FACB-11E6-BD58-64006A7986D3">
<RegistryValue Type="string" Name="ElementName" Value="fs9p-hvsock" KeyPath="yes"/>
</RegistryKey>
</Component>
</Directory>
</Directory>
</Directory>
Expand Down Expand Up @@ -130,6 +135,7 @@
<ComponentRef Id="AddToPath"/>
<ComponentRef Id="AddUserToCrcUsers" />
<ComponentRef Id="VsockRegistryEntry" />
<ComponentRef Id="Hvsock9pRegistryEntry" />
</Feature>
<UI>
<UIRef Id="WixUI_ErrorProgressText"/>
Expand Down
5 changes: 5 additions & 0 deletions pkg/crc/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ const (
OpenShiftIngressHTTPSPort = 443

BackgroundLauncherExecutable = "crc-background-launcher.exe"

Plan9Msize = 1024 * 1024
Plan9TcpPort = 564
Plan9HvsockGUID = "00009000-FACB-11E6-BD58-64006A7986D3"
Plan9HvsockPort = 36864
)

var adminHelperExecutableForOs = map[string]string{
Expand Down
9 changes: 8 additions & 1 deletion pkg/crc/machine/libhvee/driver_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,14 @@ func configureShareDirs(machineConfig config.MachineConfig) []drivers.SharedDir
Type: "cifs",
Username: machineConfig.SharedDirUsername,
}
sharedDirs = append(sharedDirs, sharedDir)
sharedDir9p := drivers.SharedDir{
Source: dir,
Target: convertToUnixPath(dir) + "9p", // temporary solution until smb sharing is removed
Tag: "crc-dir0", // same as above
//Tag: fmt.Sprintf("dir%d", i),
Type: "9p",
}
sharedDirs = append(sharedDirs, sharedDir, sharedDir9p)
}
return sharedDirs
}
15 changes: 15 additions & 0 deletions pkg/crc/machine/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ func configureSharedDirs(vm *virtualMachine, sshRunner *crcssh.Runner) error {
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 {
return err
}

case "cifs":
smbUncPath := fmt.Sprintf("//%s/%s", hostVirtualIP, mount.Tag)
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 {
Expand All @@ -257,6 +258,20 @@ func configureSharedDirs(vm *virtualMachine, sshRunner *crcssh.Runner) error {
}
return fmt.Errorf("Failed to mount CIFS/SMB share '%s' please make sure configured password is correct: %w", mount.Tag, err)
}

case "9p":
// change owner to core user to allow mounting to it as a non-root user
if _, _, err := sshRunner.RunPrivileged("Changing owner of mount directory", "chown", "core:core", mount.Target); err != nil {
return err
}
if _, _, err := sshRunner.Run("9pfs -V -p", fmt.Sprintf("%d", constants.Plan9HvsockPort), "2", mount.Target); err != nil {
logging.Warnf("Failed to connect to 9p server over hvsock: %v", err)
logging.Warnf("Falling back to 9p over TCP")
if _, _, err := sshRunner.Run("9pfs", constants.VSockGateway, mount.Target); err != nil {
return err
}
}

default:
return fmt.Errorf("Unknown Shared dir type requested: %s", mount.Type)
}
Expand Down
98 changes: 98 additions & 0 deletions pkg/fileserver/fs9p/server.go
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

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
}

// 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
}
15 changes: 15 additions & 0 deletions pkg/fileserver/fs9p/server_fallback.go
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)
}
29 changes: 29 additions & 0 deletions pkg/fileserver/fs9p/server_windows.go
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
}
21 changes: 21 additions & 0 deletions vendor/github.com/DeedleFake/p9/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading