func setupFAttrTest(t *testing.T, fs pathfs.FileSystem) (dir string, clean func()) { dir = testutil.TempDir() nfs := pathfs.NewPathNodeFs(fs, nil) opts := nodefs.NewOptions() opts.Debug = testutil.VerboseTest() state, _, err := nodefs.MountRoot(dir, nfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } clean = func() { if err := state.Unmount(); err != nil { t.Errorf("cleanup: Unmount: %v", err) } else { os.RemoveAll(dir) } } if state.KernelSettings().Flags&fuse.CAP_FILE_OPS == 0 { clean() t.Skip("Mount does not support file operations") } return dir, clean }
func NewNotifyTest(t *testing.T) *NotifyTest { me := &NotifyTest{} me.fs = newNotifyFs() me.dir = testutil.TempDir() entryTtl := 100 * time.Millisecond opts := &nodefs.Options{ EntryTimeout: entryTtl, AttrTimeout: entryTtl, NegativeTimeout: entryTtl, Debug: testutil.VerboseTest(), } me.pathfs = pathfs.NewPathNodeFs(me.fs, nil) var err error me.state, me.connector, err = nodefs.MountRoot(me.dir, me.pathfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go me.state.Serve() if err := me.state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } return me }
func TestOriginalIsSymlink(t *testing.T) { tmpDir := testutil.TempDir() defer os.RemoveAll(tmpDir) orig := tmpDir + "/orig" err := os.Mkdir(orig, 0755) if err != nil { t.Fatalf("Mkdir failed: %v", err) } link := tmpDir + "/link" mnt := tmpDir + "/mnt" if err := os.Mkdir(mnt, 0755); err != nil { t.Fatalf("Mkdir failed: %v", err) } if err := os.Symlink("orig", link); err != nil { t.Fatalf("Symlink failed: %v", err) } fs := pathfs.NewLoopbackFileSystem(link) nfs := pathfs.NewPathNodeFs(fs, nil) state, _, err := nodefs.MountRoot(mnt, nfs.Root(), nil) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } defer state.Unmount() go state.Serve() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } if _, err := os.Lstat(mnt); err != nil { t.Fatalf("Lstat failed: %v", err) } }
func setupCacheTest(t *testing.T) (string, *pathfs.PathNodeFs, func()) { dir := testutil.TempDir() os.Mkdir(dir+"/mnt", 0755) os.Mkdir(dir+"/orig", 0755) fs := &cacheFs{ pathfs.NewLoopbackFileSystem(dir + "/orig"), } pfs := pathfs.NewPathNodeFs(fs, &pathfs.PathNodeFsOptions{Debug: testutil.VerboseTest()}) opts := nodefs.NewOptions() opts.Debug = testutil.VerboseTest() state, _, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } return dir, pfs, func() { err := state.Unmount() if err == nil { os.RemoveAll(dir) } } }
func TestDefaultXAttr(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) root := &xattrNode{ Node: nodefs.NewDefaultNode(), } opts := nodefs.NewOptions() opts.Debug = testutil.VerboseTest() s, _, err := nodefs.MountRoot(dir, root, opts) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() if err := s.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer s.Unmount() var data [1024]byte sz, err := syscall.Getxattr(filepath.Join(dir, "child"), "attr", data[:]) if err != nil { t.Fatalf("Getxattr: %v", err) } else if val := string(data[:sz]); val != "value" { t.Fatalf("got %v, want 'value'", val) } }
func TestLiveness(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) root := nodefs.NewDefaultNode() s, _, err := nodefs.MountRoot(dir, root, nil) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() if err := s.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer s.Unmount() if _, err := ioutil.ReadDir(dir); err != nil { t.Fatalf("ReadDir: %v", err) } // We previously encountered a sitation where a finalizer would close our fd out from under us. Try to force both finalizers to run and object destruction to complete. runtime.GC() runtime.GC() if _, err := ioutil.ReadDir(dir); err != nil { t.Fatalf("ReadDir: %v", err) } }
func TestNonseekable(t *testing.T) { fs := &nonseekFs{FileSystem: pathfs.NewDefaultFileSystem()} fs.Length = 200 * 1024 dir := testutil.TempDir() defer os.RemoveAll(dir) nfs := pathfs.NewPathNodeFs(fs, nil) opts := nodefs.NewOptions() opts.Debug = testutil.VerboseTest() state, _, err := nodefs.MountRoot(dir, nfs.Root(), opts) if err != nil { t.Fatalf("failed: %v", err) } defer state.Unmount() go state.Serve() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } f, err := os.Open(dir + "/file") if err != nil { t.Fatalf("failed: %v", err) } defer f.Close() b := make([]byte, 200) n, err := f.ReadAt(b, 20) if err == nil || n > 0 { t.Errorf("file was opened nonseekable, but seek successful") } }
func TestDefaultNodeMount(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) root := nodefs.NewDefaultNode() s, conn, err := nodefs.MountRoot(dir, root, nil) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() if err := s.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer s.Unmount() if err := conn.Mount(root.Inode(), "sub", nodefs.NewDefaultNode(), nil); !err.Ok() { t.Fatalf("Mount: %v", err) } if entries, err := ioutil.ReadDir(dir); err != nil { t.Fatalf("ReadDir: %v", err) } else if len(entries) != 1 { t.Fatalf("got %d entries", len(entries)) } else if entries[0].Name() != "sub" { t.Fatalf("got %q, want %q", entries[0].Name(), "sub") } }
func setup(t *testing.T) (workdir string, server *fuse.Server, cleanup func()) { wd := testutil.TempDir() err := os.Mkdir(wd+"/mnt", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } err = os.Mkdir(wd+"/store", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } os.Mkdir(wd+"/ro", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } WriteFile(t, wd+"/ro/file1", "file1") WriteFile(t, wd+"/ro/file2", "file2") fs := NewAutoUnionFs(wd+"/store", testAOpts) nfs := pathfs.NewPathNodeFs(fs, nil) state, _, err := nodefs.MountRoot(wd+"/mnt", nfs.Root(), &testAOpts.Options) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() state.WaitMount() return wd, state, func() { state.Unmount() os.RemoveAll(wd) } }
func defaultReadTest(t *testing.T) (root string, cleanup func()) { fs := &DefaultReadFS{ FileSystem: pathfs.NewDefaultFileSystem(), size: 22, } var err error dir := testutil.TempDir() pathfs := pathfs.NewPathNodeFs(fs, nil) opts := nodefs.NewOptions() opts.Debug = testutil.VerboseTest() state, _, err := nodefs.MountRoot(dir, pathfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } return dir, func() { state.Unmount() os.Remove(dir) } }
func BenchmarkCFuseThreadedStat(b *testing.B) { b.StopTimer() wd, _ := os.Getwd() fileList := wd + "/testpaths.txt" lines := ReadLines(fileList) unique := map[string]int{} for _, l := range lines { unique[l] = 1 dir, _ := filepath.Split(l) for dir != "/" && dir != "" { unique[dir] = 1 dir = filepath.Clean(dir) dir, _ = filepath.Split(dir) } } out := []string{} for k := range unique { out = append(out, k) } f, err := ioutil.TempFile("", "") if err != nil { b.Fatalf("failed: %v", err) } sort.Strings(out) for _, k := range out { f.Write([]byte(fmt.Sprintf("/%s\n", k))) } f.Close() mountPoint := testutil.TempDir() cmd := exec.Command(wd+"/cstatfs", "-o", "entry_timeout=0.0,attr_timeout=0.0,ac_attr_timeout=0.0,negative_timeout=0.0", mountPoint) cmd.Env = append(os.Environ(), fmt.Sprintf("STATFS_INPUT=%s", f.Name())) cmd.Start() bin, err := exec.LookPath("fusermount") if err != nil { b.Fatalf("failed: %v", err) } stop := exec.Command(bin, "-u", mountPoint) if err != nil { b.Fatalf("failed: %v", err) } defer stop.Run() time.Sleep(100 * time.Millisecond) os.Lstat(mountPoint) threads := runtime.GOMAXPROCS(0) if err := TestingBOnePass(b, threads, fileList, mountPoint); err != nil { log.Fatalf("TestingBOnePass %v", err) } }
// Creates a temporary dir "wd" with 3 directories: // mnt ... overlayed (unionfs) mount // rw .... modifiable data // ro .... read-only data func setupUfs(t *testing.T) (wd string, cleanup func()) { // Make sure system setting does not affect test. syscall.Umask(0) wd = testutil.TempDir() err := os.Mkdir(wd+"/mnt", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } err = os.Mkdir(wd+"/rw", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } os.Mkdir(wd+"/ro", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } var fses []pathfs.FileSystem fses = append(fses, pathfs.NewLoopbackFileSystem(wd+"/rw")) fses = append(fses, NewCachingFileSystem(pathfs.NewLoopbackFileSystem(wd+"/ro"), 0)) ufs, err := NewUnionFs(fses, testOpts) if err != nil { t.Fatalf("NewUnionFs: %v", err) } // We configure timeouts are smaller, so we can check for // UnionFs's cache consistency. opts := &nodefs.Options{ EntryTimeout: entryTtl / 2, AttrTimeout: entryTtl / 2, NegativeTimeout: entryTtl / 2, PortableInodes: true, Debug: testutil.VerboseTest(), } pathfs := pathfs.NewPathNodeFs(ufs, &pathfs.PathNodeFsOptions{ClientInodes: true, Debug: opts.Debug, }) state, _, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() state.WaitMount() return wd, func() { err := state.Unmount() if err != nil { return } setRecursiveWritable(t, wd, true) os.RemoveAll(wd) } }
func TestCopyFile(t *testing.T) { d1 := testutil.TempDir() defer os.RemoveAll(d1) d2 := testutil.TempDir() defer os.RemoveAll(d2) fs1 := NewLoopbackFileSystem(d1) fs2 := NewLoopbackFileSystem(d2) content1 := "blabla" err := ioutil.WriteFile(d1+"/file", []byte(content1), 0644) if err != nil { t.Fatalf("WriteFile failed: %v", err) } code := CopyFile(fs1, fs2, "file", "file", nil) if !code.Ok() { t.Fatal("Unexpected ret code", code) } data, err := ioutil.ReadFile(d2 + "/file") if content1 != string(data) { t.Fatal("Unexpected content", string(data)) } content2 := "foobar" err = ioutil.WriteFile(d2+"/file", []byte(content2), 0644) if err != nil { t.Fatalf("WriteFile failed: %v", err) } // Copy back: should overwrite. code = CopyFile(fs2, fs1, "file", "file", nil) if !code.Ok() { t.Fatal("Unexpected ret code", code) } data, err = ioutil.ReadFile(d1 + "/file") if content2 != string(data) { t.Fatal("Unexpected content", string(data)) } }
func TestDefaultNodeGetAttr(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) opts := &nodefs.Options{ // Note: defaultNode.GetAttr() calling file.GetAttr() is only useful if // AttrTimeout is zero. // See https://github.com/JonathonReinhart/gitlab-fuse/issues/2 Owner: fuse.CurrentOwner(), } root := nodefs.NewDefaultNode() s, _, err := nodefs.MountRoot(dir, root, opts) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() if err := s.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer s.Unmount() // Attach another custom node type root.Inode().NewChild("foo", false, &myNode{ Node: nodefs.NewDefaultNode(), content: []byte("success"), }) filepath := path.Join(dir, "foo") // NewDefaultNode() should provide for stat that indicates 0-byte regular file fi, err := os.Stat(filepath) if err != nil { t.Fatalf("Stat: %v", err) } if mode := (fi.Mode() & os.ModeType); mode != 0 { // Mode() & ModeType should be zero for regular files t.Fatalf("Unexpected mode: %#o", mode) } if size := fi.Size(); size != 0 { t.Fatalf("Unexpected size: %d", size) } // But when we open the file, we should get the content content, err := ioutil.ReadFile(filepath) if err != nil { t.Fatalf("ReadFile: %v", err) } if string(content) != "success" { t.Fatalf("Unexpected content: %v", content) } }
// Create and mount filesystem. func NewTestCase(t *testing.T) *testCase { tc := &testCase{} tc.tester = t // Make sure system setting does not affect test. syscall.Umask(0) const name string = "hello.txt" const subdir string = "subdir" var err error tc.tmpDir = testutil.TempDir() tc.orig = tc.tmpDir + "/orig" tc.mnt = tc.tmpDir + "/mnt" tc.Mkdir(tc.orig, 0700) tc.Mkdir(tc.mnt, 0700) tc.mountFile = filepath.Join(tc.mnt, name) tc.mountSubdir = filepath.Join(tc.mnt, subdir) tc.origFile = filepath.Join(tc.orig, name) tc.origSubdir = filepath.Join(tc.orig, subdir) var pfs pathfs.FileSystem pfs = pathfs.NewLoopbackFileSystem(tc.orig) pfs = pathfs.NewLockingFileSystem(pfs) tc.pathFs = pathfs.NewPathNodeFs(pfs, &pathfs.PathNodeFsOptions{ ClientInodes: true}) tc.connector = nodefs.NewFileSystemConnector(tc.pathFs.Root(), &nodefs.Options{ EntryTimeout: testTtl, AttrTimeout: testTtl, NegativeTimeout: 0.0, Debug: testutil.VerboseTest(), }) tc.state, err = fuse.NewServer( fuse.NewRawFileSystem(tc.connector.RawFS()), tc.mnt, &fuse.MountOptions{ SingleThreaded: true, Debug: testutil.VerboseTest(), }) if err != nil { t.Fatal("NewServer:", err) } go tc.state.Serve() if err := tc.state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } return tc }
func xattrTestCase(t *testing.T, nm string, m map[string][]byte) (mountPoint string, cleanup func()) { xfs := NewXAttrFs(nm, m) mountPoint = testutil.TempDir() nfs := NewPathNodeFs(xfs, nil) state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), &nodefs.Options{Debug: VerboseTest()}) if err != nil { t.Fatalf("MountRoot failed: %v", err) } go state.Serve() return mountPoint, func() { state.Unmount() os.RemoveAll(mountPoint) } }
func TestGetAttrRace(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) os.Mkdir(dir+"/mnt", 0755) os.Mkdir(dir+"/orig", 0755) fs := pathfs.NewLoopbackFileSystem(dir + "/orig") pfs := pathfs.NewPathNodeFs(fs, &pathfs.PathNodeFsOptions{Debug: testutil.VerboseTest()}) state, _, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(), &nodefs.Options{Debug: testutil.VerboseTest()}) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer state.Unmount() var wg sync.WaitGroup n := 100 wg.Add(n) var statErr error for i := 0; i < n; i++ { go func() { defer wg.Done() fn := dir + "/mnt/file" err := ioutil.WriteFile(fn, []byte{42}, 0644) if err != nil { statErr = err return } _, err = os.Lstat(fn) if err != nil { statErr = err } }() } wg.Wait() if statErr != nil { t.Error(statErr) } }
func setupOwnerTest(t *testing.T, opts *nodefs.Options) (workdir string, cleanup func()) { wd := testutil.TempDir() fs := &ownerFs{NewDefaultFileSystem()} nfs := NewPathNodeFs(fs, nil) state, _, err := nodefs.MountRoot(wd, nfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } return wd, func() { state.Unmount() os.RemoveAll(wd) } }
func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) { root, err := NewArchiveFileSystem(testZipFile()) if err != nil { t.Fatalf("NewArchiveFileSystem failed: %v", err) } mountPoint = testutil.TempDir() state, _, err := nodefs.MountRoot(mountPoint, root, &nodefs.Options{ Debug: testutil.VerboseTest(), }) go state.Serve() state.WaitMount() return mountPoint, func() { state.Unmount() os.RemoveAll(mountPoint) } }
func TestCachingFs(t *testing.T) { wd := testutil.TempDir() defer os.RemoveAll(wd) fs := pathfs.NewLoopbackFileSystem(wd) cfs := NewCachingFileSystem(fs, 0) os.Mkdir(wd+"/orig", 0755) fi, code := cfs.GetAttr("orig", nil) if !code.Ok() { t.Fatal("GetAttr failure", code) } if !fi.IsDir() { t.Error("unexpected attr", fi) } os.Symlink("orig", wd+"/symlink") val, code := cfs.Readlink("symlink", nil) if val != "orig" { t.Error("unexpected readlink", val) } if !code.Ok() { t.Error("code !ok ", code) } stream, code := cfs.OpenDir("", nil) if !code.Ok() { t.Fatal("Readdir fail", code) } results := make(map[string]uint32) for _, v := range stream { results[v.Name] = v.Mode &^ 07777 } expected := map[string]uint32{ "symlink": syscall.S_IFLNK, "orig": fuse.S_IFDIR, } if !modeMapEq(results, expected) { t.Error("Unexpected readdir result", results, expected) } }
func setupFs(fs pathfs.FileSystem, N int) (string, func()) { opts := &nodefs.Options{ EntryTimeout: 0.0, AttrTimeout: 0.0, NegativeTimeout: 0.0, } mountPoint := testutil.TempDir() nfs := pathfs.NewPathNodeFs(fs, nil) state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), opts) if err != nil { panic(fmt.Sprintf("cannot mount %v", err)) // ugh - benchmark has no error methods. } lmap := NewLatencyMap() if testutil.VerboseTest() { state.RecordLatencies(lmap) } go state.Serve() return mountPoint, func() { if testutil.VerboseTest() { var total time.Duration for _, n := range []string{"LOOKUP", "GETATTR", "OPENDIR", "READDIR", "READDIRPLUS", "RELEASEDIR", "FLUSH", } { if count, dt := lmap.Get(n); count > 0 { total += dt log.Printf("%s %v/call n=%d", n, dt/time.Duration(count), count) } } log.Printf("total %v, %v/bench op", total, total/time.Duration(N)) } err := state.Unmount() if err != nil { log.Println("error during unmount", err) } else { os.RemoveAll(mountPoint) } } }
func setupMzfs(t *testing.T) (mountPoint string, state *fuse.Server, cleanup func()) { fs := NewMultiZipFs() mountPoint = testutil.TempDir() nfs := pathfs.NewPathNodeFs(fs, nil) state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), &nodefs.Options{ EntryTimeout: testTtl, AttrTimeout: testTtl, NegativeTimeout: 0.0, Debug: testutil.VerboseTest(), }) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } go state.Serve() state.WaitMount() return mountPoint, state, func() { state.Unmount() os.RemoveAll(mountPoint) } }
func TestXAttrCaching(t *testing.T) { wd := testutil.TempDir() defer os.RemoveAll(wd) os.Mkdir(wd+"/mnt", 0700) err := os.Mkdir(wd+"/rw", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } rwFS := pathfs.NewLoopbackFileSystem(wd + "/rw") roFS := &TestFS{ FileSystem: pathfs.NewDefaultFileSystem(), } ufs, err := NewUnionFs([]pathfs.FileSystem{rwFS, NewCachingFileSystem(roFS, entryTtl)}, testOpts) if err != nil { t.Fatalf("NewUnionFs: %v", err) } opts := &nodefs.Options{ EntryTimeout: entryTtl / 2, AttrTimeout: entryTtl / 2, NegativeTimeout: entryTtl / 2, Debug: testutil.VerboseTest(), } pathfs := pathfs.NewPathNodeFs(ufs, &pathfs.PathNodeFsOptions{ClientInodes: true, Debug: testutil.VerboseTest()}) server, _, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } 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 := Getxattr(wd+"/mnt/file", "user.attr", buf) if err != nil { t.Fatalf("Getxattr: %v", err) } want := "\x2a" got := string(buf[:n]) if got != want { t.Fatalf("Got %q want %q", got, err) } time.Sleep(entryTtl / 3) 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) } 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) } }
func TestUnionFsDisappearing(t *testing.T) { // This init is like setupUfs, but we want access to the // writable Fs. wd := testutil.TempDir() defer os.RemoveAll(wd) err := os.Mkdir(wd+"/mnt", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } err = os.Mkdir(wd+"/rw", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } os.Mkdir(wd+"/ro", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) } wrFs := newDisappearingFS(pathfs.NewLoopbackFileSystem(wd+"/rw"), pathfs.NewLoopbackFileSystem("/dev/null")) var fses []pathfs.FileSystem fses = append(fses, pathfs.NewLockingFileSystem(wrFs)) fses = append(fses, pathfs.NewLoopbackFileSystem(wd+"/ro")) ufs, err := NewUnionFs(fses, testOpts) if err != nil { t.Fatalf("NewUnionFs: %v", err) } opts := &nodefs.Options{ EntryTimeout: entryTtl, AttrTimeout: entryTtl, NegativeTimeout: entryTtl, Debug: testutil.VerboseTest(), } nfs := pathfs.NewPathNodeFs(ufs, nil) state, _, err := nodefs.MountRoot(wd+"/mnt", nfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } defer state.Unmount() go state.Serve() state.WaitMount() err = ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644) if err != nil { t.Fatalf("WriteFile failed: %v", err) } setRecursiveWritable(t, wd+"/ro", false) err = os.Remove(wd + "/mnt/file") if err != nil { t.Fatalf("Remove failed: %v", err) } wrFs.visibleChan <- false time.Sleep((3 * entryTtl) / 2) _, err = ioutil.ReadDir(wd + "/mnt") if err == nil { t.Fatal("Readdir should have failed") } err = ioutil.WriteFile(wd+"/mnt/file2", []byte("blabla"), 0644) if err == nil { t.Fatal("write should have failed") } // 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 { t.Fatal("Readdir should succeed", err) } err = ioutil.WriteFile(wd+"/mnt/file2", []byte("blabla"), 0644) if err != nil { t.Fatal("write should succeed", err) } }
func TestDeleteNotify(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) root := nodefs.NewMemNodeFSRoot(dir + "/backing") conn := nodefs.NewFileSystemConnector(root, &nodefs.Options{PortableInodes: true}) mnt := dir + "/mnt" err := os.Mkdir(mnt, 0755) if err != nil { t.Fatal(err) } state, err := fuse.NewServer(conn.RawFS(), mnt, &fuse.MountOptions{ Debug: testutil.VerboseTest(), }) if err != nil { t.Fatal(err) } go state.Serve() defer state.Unmount() if err := state.WaitMount(); err != nil { t.Fatal("WaitMount", err) } _, code := root.Mkdir("testdir", 0755, nil) if !code.Ok() { t.Fatal(code) } ch := root.Inode().RmChild("testdir") ch.Node().SetInode(nil) flip := flipNode{ Node: ch.Node(), ok: make(chan int), } root.Inode().NewChild("testdir", true, &flip) err = ioutil.WriteFile(mnt+"/testdir/testfile", []byte{42}, 0644) if err != nil { t.Fatal(err) } // Do the test here, so we surely have state.KernelSettings() if state.KernelSettings().Minor < 18 { t.Log("Kernel does not support deletion notify; aborting test.") return } buf := bytes.Buffer{} cmd := exec.Command("/usr/bin/tail", "-f", "testfile") cmd.Dir = mnt + "/testdir" cmd.Stdin = &buf cmd.Stdout = &bytes.Buffer{} cmd.Stderr = os.Stderr err = cmd.Start() if err != nil { t.Fatal(err) } defer func() { cmd.Process.Kill() time.Sleep(100 * time.Millisecond) }() // Wait until tail opened the file. time.Sleep(100 * time.Millisecond) err = os.Remove(mnt + "/testdir/testfile") if err != nil { t.Fatal(err) } // Simulate deletion+mkdir coming from the network close(flip.ok) oldCh := root.Inode().RmChild("testdir") _, code = root.Inode().Node().Mkdir("testdir", 0755, nil) if !code.Ok() { t.Fatal("mkdir status", code) } conn.DeleteNotify(root.Inode(), oldCh, "testdir") _, err = os.Lstat(mnt + "/testdir") if err != nil { t.Fatalf("lstat after del + mkdir failed: %v", err) } }