Skip to content

Commit

Permalink
EXPERIMENT vhost server for virtioFS
Browse files Browse the repository at this point in the history
  • Loading branch information
hanwen committed Nov 4, 2024
1 parent caecbf1 commit 80b23b7
Show file tree
Hide file tree
Showing 7 changed files with 1,599 additions and 7 deletions.
51 changes: 51 additions & 0 deletions example/virtiofs/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2024 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
"flag"
"log"
"net"

"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/vhostuser"
)

func main() {
log.SetFlags(log.Lmicroseconds)
flag.Parse()

sockpath := flag.Arg(0)
orig := flag.Arg(1)
l, err := net.ListenUnix("unix", &net.UnixAddr{sockpath, "unix"})
if err != nil {
log.Fatal("Listen", err)
}

root, err := fs.NewLoopbackRoot(orig)
if err != nil {
log.Fatal(err)
}
opts := &fs.Options{}
opts.Debug = true
opts.Logger = log.Default()
opts.MountOptions.Logger = opts.Logger
rawFS := fs.NewNodeFS(root, opts)
ps := fuse.NewProtocolServer(rawFS, &opts.MountOptions)

for {
conn, err := l.AcceptUnix()
if err != nil {
break
}

dev := vhostuser.NewFSDevice(ps)
srv := vhostuser.NewServer(conn, dev)
if err := srv.Serve(); err != nil {
log.Printf("Serve: %v %T", err, err)
}
}
}
21 changes: 21 additions & 0 deletions fuse/protocol-server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package fuse

import (
"log"
"testing"
)

func TestProtocolServerParse(t *testing.T) {
in := [][]byte{
[]byte("A\x00\x00\x00\x16\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x04\x00\x00\x00\x00\x00\x00"),
[]byte("\x00\x00\x00\x00\x00\x00\x00\x00security.selinux\x00"),
}
out := [][]byte{make([]byte, 16), make([]byte, 16)}

opts := MountOptions{}
opts.Debug = true
opts.Logger = log.Default()
ps := NewProtocolServer(NewDefaultRawFileSystem(), &opts)
n, status := ps.HandleRequest(in, out)
log.Println(n, status)
}
2 changes: 1 addition & 1 deletion fuse/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (r *request) InputDebug() string {
if h != nil && h.InType != nil {
val = Print(asType(r.inData(), h.InType))
}

log.Println(val)
names := ""
if h.FileNames == 1 {
names = fmt.Sprintf(" %q", r.filename())
Expand Down
11 changes: 5 additions & 6 deletions fuse/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,16 @@ func (ps *ProtocolServer) HandleRequest(in [][]byte,
inTogether := make([]byte, len(in[0])+len(in[1]))
copy(inTogether, in[0])
copy(inTogether[len(in[0]):], in[1])
h, inSize, outSize, payloadSize, errno := parseRequest(inTogether, nil)
h, inSize, outSize, outPayloadSize, errno := parseRequest(inTogether, nil)
if errno != 0 {
return 0, errno
}

log.Printf("h: %v %d %d %d %v", h, inSize, outSize, payloadSize, errno)
log.Printf("%d %d payloadSize %d", inSize, outSize, outPayloadSize)
req := request{
cancel: make(chan struct{}),
inputBuf: inTogether[:inSize],
outputBuf: make([]byte, outSize+int(sizeOfOutHeader)),
inPayload: make([]byte, payloadSize),
outputBuf: make([]byte, outSize+int(sizeOfOutHeader)+outPayloadSize),
inPayload: inTogether[inSize:],
}

ps.protocolServer.handleRequest(h, &req)
Expand All @@ -93,7 +92,7 @@ func (ps *ProtocolServer) HandleRequest(in [][]byte,
if len(req.outputBuf) > len(out[0]) {
copy(out[1], req.outputBuf[len(out[0]):])
}
if payloadSize > 0 {
if outPayloadSize > 0 {
copy(out[len(out)-1], req.outPayload)
}
return len(req.outPayload) + len(req.outputBuf), 0
Expand Down
81 changes: 81 additions & 0 deletions vhostuser/fs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package vhostuser

import (
"log"
"net"
"os"
"os/exec"
"testing"

"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
)

func listenVFS(sockpath string, rawFS fuse.RawFileSystem, opts *fuse.MountOptions) {
os.Remove(sockpath)
l, err := net.ListenUnix("unix", &net.UnixAddr{sockpath, "unix"})
if err != nil {
log.Fatal("Listen", err)
}
for {
conn, err := l.AcceptUnix()
if err != nil {
break
}

ps := fuse.NewProtocolServer(rawFS, opts)
dev := NewFSDevice(ps)
srv := NewServer(conn, dev)
if err := srv.Serve(); err != nil {
log.Printf("Serve: %v %T", err, err)
}
}
}

func TestBasic(t *testing.T) {
orig := t.TempDir()
if err := os.WriteFile(orig+"/file.txt", []byte("hello world\n"), 0666); err != nil {
t.Errorf("WriteFile: %v", err)
}
root, err := fs.NewLoopbackRoot(orig)
if err != nil {
t.Fatal(err)
}
opts := &fs.Options{}
opts.Debug = true
opts.Logger = log.Default()
opts.MountOptions.Logger = opts.Logger
rawFS := fs.NewNodeFS(root, opts)

bindir := os.Getenv("HOME") + "/.cache/go-fuse-virtiofs"
sockpath := "/tmp/vhostqemu"
go listenVFS(sockpath, rawFS, &opts.MountOptions)

cmd := exec.Command("qemu-system-x86_64",
"-M", "pc", "-m", "4G", "-cpu", "host", "-smp", "2",
"-enable-kvm",

// to create the communications socket
"-chardev", "socket,id=char0,path="+sockpath,

// instantiate the device
"-device", "vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=myfs",

// force use of memory sharable with virtiofsd.
"-object", "memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on", "-numa", "node,memdev=mem",

"-kernel", bindir+"/bzImage",
"-initrd", bindir+"/initramfs.cpio.gz",
"-nographic",
"-append",
"console=ttyS0",
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.Wait(); err != nil {
t.Fatal(err)
}
}
Loading

0 comments on commit 80b23b7

Please sign in to comment.