Commit 464ade3c authored by Kirill Smelkov's avatar Kirill Smelkov

Merge branch 'master' into y/nodefs-cancel

* master:
  fuse: decode MKNOD result in debug output
  fuse: fix mounting with MaxWrite < 8kiB
  fuse: add MaxPages to INIT debug output
  fs: update oldParent.children for exchange rename
  Fix IOCTL to return the error allowed by overlayfs
  fuse: prefer fusermount3 over fusermount; add debug output
parents e6ee85fd f57e95bd
...@@ -703,7 +703,7 @@ retry: ...@@ -703,7 +703,7 @@ retry:
} }
if destChild != nil { if destChild != nil {
oldParent.children[oldName] = oldChild oldParent.children[oldName] = destChild
oldParent.changeCounter++ oldParent.changeCounter++
destChild.parents.add(parentData{oldName, oldParent}) destChild.parents.add(parentData{oldName, oldParent})
......
...@@ -75,6 +75,22 @@ func TestRenameExchange(t *testing.T) { ...@@ -75,6 +75,22 @@ func TestRenameExchange(t *testing.T) {
if !reflect.DeepEqual(after2, before1) { if !reflect.DeepEqual(after2, before1) {
t.Errorf("after2, before1: %#v, %#v", after2, before1) t.Errorf("after2, before1: %#v, %#v", after2, before1)
} }
root := tc.loopback.EmbeddedInode().Root()
ino1 := root.GetChild("file")
if ino1 == nil {
t.Fatalf("root.GetChild(%q): null inode", "file")
}
ino2 := root.GetChild("dir").GetChild("file")
if ino2 == nil {
t.Fatalf("dir.GetChild(%q): null inode", "file")
}
if ino1.StableAttr().Ino != after1.Ino {
t.Errorf("got inode %d for %q, want %d", ino1.StableAttr().Ino, "file", after1.Ino)
}
if ino2.StableAttr().Ino != after2.Ino {
t.Errorf("got inode %d for %q want %d", ino2.StableAttr().Ino, "dir/file", after2.Ino)
}
} }
func TestRenameNoOverwrite(t *testing.T) { func TestRenameNoOverwrite(t *testing.T) {
......
...@@ -72,7 +72,8 @@ func mountDirect(mountPoint string, opts *MountOptions, ready chan<- error) (fd ...@@ -72,7 +72,8 @@ func mountDirect(mountPoint string, opts *MountOptions, ready chan<- error) (fd
// that it: // that it:
// * opens `/dev/fuse` // * opens `/dev/fuse`
// * mount()s this file descriptor to `mountPoint` // * mount()s this file descriptor to `mountPoint`
// * passes this file descriptor back to use via a unix domain socket // * passes this file descriptor back to us via a unix domain socket
// This file descriptor is returned as `fd`.
func callFusermount(mountPoint string, opts *MountOptions) (fd int, err error) { func callFusermount(mountPoint string, opts *MountOptions) (fd int, err error) {
local, remote, err := unixgramSocketpair() local, remote, err := unixgramSocketpair()
if err != nil { if err != nil {
...@@ -91,6 +92,9 @@ func callFusermount(mountPoint string, opts *MountOptions) (fd int, err error) { ...@@ -91,6 +92,9 @@ func callFusermount(mountPoint string, opts *MountOptions) (fd int, err error) {
if s := opts.optionsStrings(); len(s) > 0 { if s := opts.optionsStrings(); len(s) > 0 {
cmd = append(cmd, "-o", strings.Join(s, ",")) cmd = append(cmd, "-o", strings.Join(s, ","))
} }
if opts.Debug {
log.Printf("callFusermount: executing %q", cmd)
}
proc, err := os.StartProcess(bin, proc, err := os.StartProcess(bin,
cmd, cmd,
&os.ProcAttr{ &os.ProcAttr{
...@@ -168,6 +172,9 @@ func unmount(mountPoint string, opts *MountOptions) (err error) { ...@@ -168,6 +172,9 @@ func unmount(mountPoint string, opts *MountOptions) (err error) {
errBuf := bytes.Buffer{} errBuf := bytes.Buffer{}
cmd := exec.Command(bin, "-u", mountPoint) cmd := exec.Command(bin, "-u", mountPoint)
cmd.Stderr = &errBuf cmd.Stderr = &errBuf
if opts.Debug {
log.Printf("unmount: executing %q", cmd.Args)
}
err = cmd.Run() err = cmd.Run()
if errBuf.Len() > 0 { if errBuf.Len() > 0 {
return fmt.Errorf("%s (code %v)\n", return fmt.Errorf("%s (code %v)\n",
...@@ -215,7 +222,12 @@ func lookPathFallback(file string, fallbackDir string) (string, error) { ...@@ -215,7 +222,12 @@ func lookPathFallback(file string, fallbackDir string) (string, error) {
return exec.LookPath(abs) return exec.LookPath(abs)
} }
// fusermountBinary returns the path to the `fusermount3` binary, or, if not
// found, the `fusermount` binary.
func fusermountBinary() (string, error) { func fusermountBinary() (string, error) {
if path, err := lookPathFallback("fusermount3", "/bin"); err == nil {
return path, nil
}
return lookPathFallback("fusermount", "/bin") return lookPathFallback("fusermount", "/bin")
} }
......
...@@ -64,3 +64,37 @@ func TestMountDevFd(t *testing.T) { ...@@ -64,3 +64,37 @@ func TestMountDevFd(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
// TestMountMaxWrite makes sure that mounting works with all MaxWrite settings.
// We used to fail with EINVAL below 8k because readPool got too small.
func TestMountMaxWrite(t *testing.T) {
opts := []MountOptions{
{MaxWrite: 0}, // go-fuse default
{MaxWrite: 1},
{MaxWrite: 123},
{MaxWrite: 1 * 1024},
{MaxWrite: 4 * 1024},
{MaxWrite: 8 * 1024},
{MaxWrite: 64 * 1024}, // go-fuse default
{MaxWrite: 128 * 1024}, // limit in Linux v4.19 and older
{MaxWrite: 999 * 1024},
{MaxWrite: 1024 * 1024}, // limit in Linux v4.20+
}
for _, o := range opts {
name := fmt.Sprintf("MaxWrite%d", o.MaxWrite)
t.Run(name, func(t *testing.T) {
mnt, err := ioutil.TempDir("", name)
if err != nil {
t.Fatal(err)
}
fs := NewDefaultRawFileSystem()
srv, err := NewServer(fs, mnt, &o)
if err != nil {
t.Error(err)
} else {
go srv.Serve()
srv.Unmount()
}
})
}
}
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"log" "log"
"reflect" "reflect"
"runtime" "runtime"
"syscall"
"time" "time"
"unsafe" "unsafe"
) )
...@@ -442,7 +443,7 @@ func doStatFs(server *Server, req *request) { ...@@ -442,7 +443,7 @@ func doStatFs(server *Server, req *request) {
} }
func doIoctl(server *Server, req *request) { func doIoctl(server *Server, req *request) {
req.status = ENOSYS req.status = Status(syscall.ENOTTY)
} }
func doDestroy(server *Server, req *request) { func doDestroy(server *Server, req *request) {
...@@ -735,6 +736,7 @@ func init() { ...@@ -735,6 +736,7 @@ func init() {
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) }, _OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) }, _OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) },
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) }, _OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_MKNOD: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_NOTIFY_INVAL_ENTRY: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalEntryOut)(ptr) }, _OP_NOTIFY_INVAL_ENTRY: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalEntryOut)(ptr) },
_OP_NOTIFY_INVAL_INODE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalInodeOut)(ptr) }, _OP_NOTIFY_INVAL_INODE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalInodeOut)(ptr) },
_OP_NOTIFY_STORE_CACHE: func(ptr unsafe.Pointer) interface{} { return (*NotifyStoreOut)(ptr) }, _OP_NOTIFY_STORE_CACHE: func(ptr unsafe.Pointer) interface{} { return (*NotifyStoreOut)(ptr) },
......
...@@ -161,17 +161,17 @@ func (in *OpenOut) string() string { ...@@ -161,17 +161,17 @@ func (in *OpenOut) string() string {
} }
func (in *InitIn) string() string { func (in *InitIn) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s}", return fmt.Sprintf("{%d.%d Ra %d %s}",
in.Major, in.Minor, in.MaxReadAhead, in.Major, in.Minor, in.MaxReadAhead,
flagString(initFlagNames, int64(in.Flags), "")) flagString(initFlagNames, int64(in.Flags), ""))
} }
func (o *InitOut) string() string { func (o *InitOut) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s %d/%d Wr 0x%x Tg 0x%x}", return fmt.Sprintf("{%d.%d Ra %d %s %d/%d Wr %d Tg %d MaxPages %d}",
o.Major, o.Minor, o.MaxReadAhead, o.Major, o.Minor, o.MaxReadAhead,
flagString(initFlagNames, int64(o.Flags), ""), flagString(initFlagNames, int64(o.Flags), ""),
o.CongestionThreshold, o.MaxBackground, o.MaxWrite, o.CongestionThreshold, o.MaxBackground, o.MaxWrite,
o.TimeGran) o.TimeGran, o.MaxPages)
} }
func (s *FsyncIn) string() string { func (s *FsyncIn) string() string {
......
...@@ -24,6 +24,10 @@ const ( ...@@ -24,6 +24,10 @@ const (
// The kernel caps writes at 128k. // The kernel caps writes at 128k.
MAX_KERNEL_WRITE = 128 * 1024 MAX_KERNEL_WRITE = 128 * 1024
// Linux kernel constant from include/uapi/linux/fuse.h
// Reads from /dev/fuse that are smaller fail with EINVAL.
_FUSE_MIN_READ_BUFFER = 8192
minMaxReaders = 2 minMaxReaders = 2
maxMaxReaders = 16 maxMaxReaders = 16
) )
...@@ -207,8 +211,12 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server ...@@ -207,8 +211,12 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server
} }
} }
ms.readPool.New = func() interface{} { ms.readPool.New = func() interface{} {
buf := make([]byte, o.MaxWrite+int(maxInputSize)+logicalBlockSize) targetSize := o.MaxWrite + int(maxInputSize)
buf = alignSlice(buf, unsafe.Sizeof(WriteIn{}), logicalBlockSize, uintptr(o.MaxWrite)+maxInputSize) if targetSize < _FUSE_MIN_READ_BUFFER {
targetSize = _FUSE_MIN_READ_BUFFER
}
buf := make([]byte, targetSize+logicalBlockSize)
buf = alignSlice(buf, unsafe.Sizeof(WriteIn{}), logicalBlockSize, uintptr(targetSize))
return buf return buf
} }
mountPoint = filepath.Clean(mountPoint) mountPoint = filepath.Clean(mountPoint)
......
...@@ -5,8 +5,11 @@ ...@@ -5,8 +5,11 @@
package test package test
import ( import (
"flag"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"syscall" "syscall"
"testing" "testing"
"time" "time"
...@@ -14,8 +17,18 @@ import ( ...@@ -14,8 +17,18 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal/testutil"
) )
var enableOverlayfsTest bool
var enableOverlayfsTestFlag string = "test.overlayfs"
func init() {
// flag to enable test with overayfs. This would not work on kernel 5.15
// so don't enable on that kernel.
flag.BoolVar(&enableOverlayfsTest, enableOverlayfsTestFlag, false, "enable tests with overlayfs (would fail on kernel 5.15)")
}
func TestTouch(t *testing.T) { func TestTouch(t *testing.T) {
ts := NewTestCase(t) ts := NewTestCase(t)
defer ts.Cleanup() defer ts.Cleanup()
...@@ -118,6 +131,40 @@ func clearStatfs(s *syscall.Statfs_t) { ...@@ -118,6 +131,40 @@ func clearStatfs(s *syscall.Statfs_t) {
s.Flags = 0 s.Flags = 0
} }
// Check that fuse mount can serve as a overlayfs lowerdir.
func TestOverlayfs(t *testing.T) {
if !enableOverlayfsTest {
t.Skipf("this test must be enabled through the flag %q", enableOverlayfsTestFlag)
}
if os.Getuid() != 0 {
t.Skip("this test requires root")
}
tc := NewTestCase(t)
defer tc.Cleanup()
testfile := "test"
content := randomData(125)
tc.Mkdir(tc.origSubdir, 0777)
tc.WriteFile(filepath.Join(tc.origSubdir, testfile), content, 0700)
tmpMergedDir := testutil.TempDir()
defer os.RemoveAll(tmpMergedDir)
tmpWorkDir := testutil.TempDir()
defer os.RemoveAll(tmpWorkDir)
tmpUpperDir := testutil.TempDir()
defer os.RemoveAll(tmpUpperDir)
if err := unix.Mount("overlay", tmpMergedDir, "overlay", 0,
fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", tc.mnt, tmpUpperDir, tmpWorkDir)); err != nil {
t.Fatalf("failed to mount overlay: %v", err)
}
defer unix.Unmount(tmpMergedDir, 0)
err := os.Chtimes(filepath.Join(tmpMergedDir, "subdir", testfile), time.Unix(42, 0), time.Unix(43, 0))
if err != nil {
t.Fatalf("Chtimes failed: %v", err)
}
}
func TestFallocate(t *testing.T) { func TestFallocate(t *testing.T) {
ts := NewTestCase(t) ts := NewTestCase(t)
defer ts.Cleanup() defer ts.Cleanup()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment