Commit 0155dffe authored by Adam H. Leventhal's avatar Adam H. Leventhal Committed by Han-Wen Nienhuys

Fixed all tests on Darwin

Fixed handling of ENOSYS in unionfs.
parent bddfd80b
......@@ -104,8 +104,7 @@ go-fuse works somewhat on OSX. Known limitations:
* OSX has trouble with concurrent reads from the FUSE device, leading
to performance concerns.
* A bunch of tests under fuse/test/ fail, and the functionality they
test for OSX is likely broken.
* Tests are expected to pass; report any failure as a bug!
CREDITS
......
......@@ -17,11 +17,12 @@ func (fs *loopbackFileSystem) StatFs(name string) *fuse.StatfsOut {
if err == nil {
return &fuse.StatfsOut{
Blocks: s.Blocks,
Bsize: uint32(s.Bsize),
Bfree: s.Bfree,
Bavail: s.Bavail,
Files: s.Files,
Ffree: s.Ffree,
Bsize: uint32(s.Iosize), // Iosize translates to Bsize: the optimal transfer size.
Frsize: s.Bsize, // Bsize translates to Frsize: the minimum transfer size.
}
}
return nil
......@@ -58,6 +59,6 @@ func (fs *loopbackFileSystem) Utimens(path string, a *time.Time, m *time.Time, c
tv[1] = timeToTimeval(m)
}
err := syscall.Utimes(path, tv)
err := syscall.Utimes(fs.GetPath(path), tv)
return fuse.ToStatus(err)
}
......@@ -10,9 +10,15 @@ import (
func clearStatfs(s *syscall.Statfs_t) {
empty := syscall.Statfs_t{}
s.Type = 0
s.Fsid = empty.Fsid
// s.Spare = empty.Spare
// TODO - figure out what this is for.
s.Flags = 0
// FUSE can only set the following fields.
empty.Blocks = s.Blocks
empty.Bfree = s.Bfree
empty.Bavail = s.Bavail
empty.Files = s.Files
empty.Ffree = s.Ffree
empty.Iosize = s.Iosize
empty.Bsize = s.Bsize
// Clear out the rest.
*s = empty
}
......@@ -301,40 +301,33 @@ func (fs *autoUnionFs) GetXAttr(name string, attr string, context *fuse.Context)
}
func (fs *autoUnionFs) GetAttr(path string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
a := &fuse.Attr{
Owner: *fuse.CurrentOwner(),
}
if path == "" || path == _CONFIG || path == _STATUS {
a := &fuse.Attr{
Mode: fuse.S_IFDIR | 0755,
}
a.Mode = fuse.S_IFDIR | 0755
return a, fuse.OK
}
if path == filepath.Join(_STATUS, _VERSION) {
a := &fuse.Attr{
Mode: fuse.S_IFREG | 0644,
Size: uint64(len(fs.options.Version)),
}
a.Mode = fuse.S_IFREG | 0644
a.Size = uint64(len(fs.options.Version))
return a, fuse.OK
}
if path == filepath.Join(_STATUS, _DEBUG) {
a := &fuse.Attr{
Mode: fuse.S_IFREG | 0644,
Size: uint64(len(fs.DebugData())),
}
a.Mode = fuse.S_IFREG | 0644
a.Size = uint64(len(fs.DebugData()))
return a, fuse.OK
}
if path == filepath.Join(_STATUS, _ROOT) {
a := &fuse.Attr{
Mode: syscall.S_IFLNK | 0644,
}
a.Mode = syscall.S_IFLNK | 0644
return a, fuse.OK
}
if path == filepath.Join(_CONFIG, _SCAN_CONFIG) {
a := &fuse.Attr{
Mode: fuse.S_IFREG | 0644,
}
a.Mode = fuse.S_IFREG | 0644
return a, fuse.OK
}
comps := strings.Split(path, string(filepath.Separator))
......@@ -346,9 +339,7 @@ func (fs *autoUnionFs) GetAttr(path string, context *fuse.Context) (*fuse.Attr,
return nil, fuse.ENOENT
}
a := &fuse.Attr{
Mode: syscall.S_IFLNK | 0644,
}
a.Mode = syscall.S_IFLNK | 0644
return a, fuse.OK
}
......
......@@ -23,6 +23,7 @@ var testAOpts = AutoUnionFsOptions{
EntryTimeout: entryTtl,
AttrTimeout: entryTtl,
NegativeTimeout: 0,
Debug: VerboseTest(),
},
HideReadonly: true,
Version: "version",
......@@ -39,7 +40,7 @@ func WriteFile(t *testing.T, name string, contents string) {
}
}
func setup(t *testing.T) (workdir string, cleanup func()) {
func setup(t *testing.T) (workdir string, server *fuse.Server, cleanup func()) {
wd, _ := ioutil.TempDir("", "")
err := os.Mkdir(wd+"/mnt", 0700)
if err != nil {
......@@ -66,15 +67,16 @@ func setup(t *testing.T) (workdir string, cleanup func()) {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
go state.Serve()
state.WaitMount()
return wd, func() {
return wd, state, func() {
state.Unmount()
os.RemoveAll(wd)
}
}
func TestDebug(t *testing.T) {
wd, clean := setup(t)
wd, _, clean := setup(t)
defer clean()
c, err := ioutil.ReadFile(wd + "/mnt/status/debug")
......@@ -87,7 +89,7 @@ func TestDebug(t *testing.T) {
}
func TestVersion(t *testing.T) {
wd, clean := setup(t)
wd, _, clean := setup(t)
defer clean()
c, err := ioutil.ReadFile(wd + "/mnt/status/gounionfs_version")
......@@ -100,7 +102,7 @@ func TestVersion(t *testing.T) {
}
func TestAutoFsSymlink(t *testing.T) {
wd, clean := setup(t)
wd, server, clean := setup(t)
defer clean()
err := os.Mkdir(wd+"/store/backing1", 0755)
......@@ -142,14 +144,22 @@ func TestAutoFsSymlink(t *testing.T) {
t.Error("error writing:", err)
}
fi, _ = os.Lstat(wd + "/mnt/manual1")
if fi != nil {
t.Error("Should not have file:", fi)
}
_, err = ioutil.ReadDir(wd + "/mnt/config")
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
// If FUSE supports invalid inode notifications we expect this node to be gone. Otherwise we'll just make sure that it's not reachable.
if server.KernelSettings().SupportsNotify(fuse.NOTIFY_INVAL_INODE) {
fi, _ = os.Lstat(wd + "/mnt/manual1")
if fi != nil {
t.Error("Should not have file:", fi)
}
} else {
entries, err = ioutil.ReadDir(wd + "/mnt")
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
}
for _, e := range entries {
if e.Name() == "manual1" {
t.Error("Should not have entry: ", e)
}
}
}
_, err = os.Lstat(wd + "/mnt/backing1/file1")
......@@ -159,7 +169,7 @@ func TestAutoFsSymlink(t *testing.T) {
}
func TestDetectSymlinkedDirectories(t *testing.T) {
wd, clean := setup(t)
wd, _, clean := setup(t)
defer clean()
err := os.Mkdir(wd+"/backing1", 0755)
......@@ -190,7 +200,7 @@ func TestDetectSymlinkedDirectories(t *testing.T) {
}
func TestExplicitScan(t *testing.T) {
wd, clean := setup(t)
wd, _, clean := setup(t)
defer clean()
err := os.Mkdir(wd+"/store/backing1", 0755)
......@@ -225,7 +235,7 @@ func TestExplicitScan(t *testing.T) {
}
func TestCreationChecks(t *testing.T) {
wd, clean := setup(t)
wd, _, clean := setup(t)
defer clean()
err := os.Mkdir(wd+"/store/foo", 0755)
......
......@@ -570,7 +570,7 @@ func (fs *unionFS) Chmod(name string, mode uint32, context *fuse.Context) (code
func (fs *unionFS) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
// We always allow writing.
mode = mode &^ fuse.W_OK
if name == "" {
if name == "" || name == _DROP_CACHE {
return fuse.OK
}
r := fs.getBranch(name)
......
......@@ -106,6 +106,7 @@ func setupUfs(t *testing.T) (wd string, cleanup func()) {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
go state.Serve()
state.WaitMount()
return wd, func() {
err := state.Unmount()
......@@ -243,8 +244,9 @@ func TestUnionFsChtimes(t *testing.T) {
}
fi, err := os.Lstat(wd + "/mnt/file")
stat := fuse.ToStatT(fi)
if stat.Atim.Sec != 82 || stat.Mtim.Sec != 83 {
attr := &fuse.Attr{}
attr.FromStat(fuse.ToStatT(fi))
if attr.Atime != 82 || attr.Mtime != 83 {
t.Error("Incorrect timestamp", fi)
}
}
......@@ -1168,6 +1170,7 @@ func TestUnionFsDisappearing(t *testing.T) {
}
defer state.Unmount()
go state.Serve()
state.WaitMount()
err = ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644)
if err != nil {
......@@ -1193,9 +1196,9 @@ func TestUnionFsDisappearing(t *testing.T) {
t.Fatal("write should have failed")
}
// Restore, and wait for caches to catch up.
wrFs.visibleChan <- true
// Wait for the caches to purge, and then restore.
time.Sleep((3 * entryTtl) / 2)
wrFs.visibleChan <- true
_, err = ioutil.ReadDir(wd + "/mnt")
if err != nil {
......@@ -1281,41 +1284,6 @@ func TestUnionFsDoubleOpen(t *testing.T) {
}
}
func TestUnionFsFdLeak(t *testing.T) {
beforeEntries, err := ioutil.ReadDir("/proc/self/fd")
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
}
wd, clean := setupUfs(t)
err = ioutil.WriteFile(wd+"/ro/file", []byte("blablabla"), 0644)
if err != nil {
t.Fatalf("WriteFile failed: %v", err)
}
setRecursiveWritable(t, wd+"/ro", false)
contents, err := ioutil.ReadFile(wd + "/mnt/file")
if err != nil {
t.Fatalf("ReadFile failed: %v", err)
}
err = ioutil.WriteFile(wd+"/mnt/file", contents, 0644)
if err != nil {
t.Fatalf("WriteFile failed: %v", err)
}
clean()
afterEntries, err := ioutil.ReadDir("/proc/self/fd")
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
}
if len(afterEntries) != len(beforeEntries) {
t.Errorf("/proc/self/fd changed size: after %v before %v", len(beforeEntries), len(afterEntries))
}
}
func TestUnionFsStatFs(t *testing.T) {
wd, clean := setupUfs(t)
defer clean()
......
// Copyright 2016 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 unionfs
import (
"io/ioutil"
)
func TestUnionFsFdLeak(t *testing.T) {
beforeEntries, err := ioutil.ReadDir("/proc/self/fd")
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
}
wd, clean := setupUfs(t)
err = ioutil.WriteFile(wd+"/ro/file", []byte("blablabla"), 0644)
if err != nil {
t.Fatalf("WriteFile failed: %v", err)
}
setRecursiveWritable(t, wd+"/ro", false)
contents, err := ioutil.ReadFile(wd + "/mnt/file")
if err != nil {
t.Fatalf("ReadFile failed: %v", err)
}
err = ioutil.WriteFile(wd+"/mnt/file", contents, 0644)
if err != nil {
t.Fatalf("WriteFile failed: %v", err)
}
clean()
afterEntries, err := ioutil.ReadDir("/proc/self/fd")
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
}
if len(afterEntries) != len(beforeEntries) {
t.Errorf("/proc/self/fd changed size: after %v before %v", len(beforeEntries), len(afterEntries))
}
}
......@@ -7,7 +7,6 @@ package unionfs
import (
"io/ioutil"
"os"
"syscall"
"testing"
"time"
......@@ -76,13 +75,14 @@ func TestXAttrCaching(t *testing.T) {
}
defer server.Unmount()
go server.Serve()
server.WaitMount()
if fi, err := os.Lstat(wd + "/mnt"); err != nil || !fi.IsDir() {
t.Fatalf("root not readable: %v, %v", err, fi)
}
buf := make([]byte, 1024)
n, err := syscall.Getxattr(wd+"/mnt/file", "user.attr", buf)
n, err := Getxattr(wd+"/mnt/file", "user.attr", buf)
if err != nil {
t.Fatalf("Getxattr: %v", err)
}
......@@ -91,8 +91,10 @@ func TestXAttrCaching(t *testing.T) {
if got != want {
t.Fatalf("Got %q want %q", got, err)
}
time.Sleep(entryTtl / 2)
n, err = syscall.Getxattr(wd+"/mnt/file", "user.attr", buf)
time.Sleep(entryTtl / 3)
n, err = Getxattr(wd+"/mnt/file", "user.attr", buf)
if err != nil {
t.Fatalf("Getxattr: %v", err)
}
......@@ -100,7 +102,21 @@ func TestXAttrCaching(t *testing.T) {
if got != want {
t.Fatalf("Got %q want %q", got, err)
}
server.Unmount()
time.Sleep(entryTtl / 3)
// Make sure that an interceding Getxattr() to a filesystem that doesn't implement GetXAttr() doesn't affect future calls.
Getxattr(wd, "whatever", buf)
n, err = Getxattr(wd+"/mnt/file", "user.attr", buf)
if err != nil {
t.Fatalf("Getxattr: %v", err)
}
got = string(buf[:n])
if got != want {
t.Fatalf("Got %q want %q", got, err)
}
if roFS.xattrRead != 1 {
t.Errorf("got xattrRead=%d, want 1", roFS.xattrRead)
}
......
// Copyright 2016 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 unionfs
import (
"syscall"
"unsafe"
)
// Darwin doesn't have support for syscall.Getxattr() so we pull it into its own file and implement it by hand on Darwin.
func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
var _p0 *byte
_p0, err = syscall.BytePtrFromString(path)
if err != nil {
return
}
var _p1 *byte
_p1, err = syscall.BytePtrFromString(attr)
if err != nil {
return
}
var _p2 unsafe.Pointer
if len(dest) > 0 {
_p2 = unsafe.Pointer(&dest[0])
} else {
var _zero uintptr
_p2 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
sz = int(r0)
if e1 != 0 {
err = e1
}
return
}
// Copyright 2016 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 unionfs
import (
"syscall"
)
// Darwin doesn't have support for syscall.Getxattr() so we pull it into its own file and implement it by hand on Darwin.
func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
return syscall.Getxattr(path, attr, dest)
}
......@@ -87,6 +87,7 @@ func (fs *MultiZipFs) OpenDir(name string, context *fuse.Context) (stream []fuse
func (fs *MultiZipFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
a := &fuse.Attr{}
a.Owner = *fuse.CurrentOwner()
if name == "" {
// Should not write in top dir.
a.Mode = fuse.S_IFDIR | 0500
......@@ -137,7 +138,7 @@ func (fs *MultiZipFs) Unlink(name string, context *fuse.Context) (code fuse.Stat
delete(fs.zips, basename)
delete(fs.dirZipFileMap, basename)
// Drop lock to ensure that notify doesn't cause deadlock.
// Drop the lock to ensure that notify doesn't cause a deadlock.
fs.lock.Unlock()
code = fs.nodeFs.UnmountNode(root.Inode())
fs.lock.Lock()
......
......@@ -11,6 +11,7 @@ import (
"testing"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
......@@ -23,7 +24,7 @@ func VerboseTest() bool {
const testTtl = 100 * time.Millisecond
func setupMzfs(t *testing.T) (mountPoint string, cleanup func()) {
func setupMzfs(t *testing.T) (mountPoint string, state *fuse.Server, cleanup func()) {
fs := NewMultiZipFs()
mountPoint, _ = ioutil.TempDir("", "")
nfs := pathfs.NewPathNodeFs(fs, nil)
......@@ -37,15 +38,16 @@ func setupMzfs(t *testing.T) (mountPoint string, cleanup func()) {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
go state.Serve()
state.WaitMount()
return mountPoint, func() {
return mountPoint, state, func() {
state.Unmount()
os.RemoveAll(mountPoint)
}
}
func TestMultiZipReadonly(t *testing.T) {
mountPoint, cleanup := setupMzfs(t)
mountPoint, _, cleanup := setupMzfs(t)
defer cleanup()
_, err := os.Create(mountPoint + "/random")
......@@ -60,7 +62,7 @@ func TestMultiZipReadonly(t *testing.T) {
}
func TestMultiZipFs(t *testing.T) {
mountPoint, cleanup := setupMzfs(t)
mountPoint, server, cleanup := setupMzfs(t)
defer cleanup()
zipFile := testZipFile()
......@@ -122,8 +124,22 @@ func TestMultiZipFs(t *testing.T) {
t.Fatalf("Remove failed: %v", err)
}
fi, err = os.Stat(mountPoint + "/zipmount")
if err == nil {
t.Errorf("stat should fail after unmount, got %#v", fi)
// If FUSE supports invalid inode notifications we expect this node to be gone. Otherwise we'll just make sure that it's not reachable.
if server.KernelSettings().SupportsNotify(fuse.NOTIFY_INVAL_INODE) {
fi, err = os.Stat(mountPoint + "/zipmount")
if err == nil {
t.Errorf("stat should fail after unmount, got %#v", fi)
}
} else {
entries, err = ioutil.ReadDir(mountPoint)
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
}
for _, e := range entries {
if e.Name() == "zipmount" {
t.Error("Should not have entry: ", e)
}
}
}
}
......@@ -36,6 +36,7 @@ func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) {
})
go state.Serve()
state.WaitMount()
return mountPoint, func() {
state.Unmount()
......
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