// 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 New(root, hostport string) (pathfs.FileSystem, error) { return &cloudFileSystem{ root: root, hostport: hostport, meta: pathfs.NewLoopbackFileSystem(path.Join(root, metaName)), staging: pathfs.NewLoopbackFileSystem(path.Join(root, stagingName)), remote: blkstore.NewMemStore() }, nil }
func setupCacheTest(t *testing.T) (string, *pathfs.PathNodeFs, func()) { dir, err := ioutil.TempDir("", "go-fuse-cachetest") if err != nil { t.Fatalf("TempDir failed: %v", err) } os.Mkdir(dir+"/mnt", 0755) os.Mkdir(dir+"/orig", 0755) fs := &cacheFs{ pathfs.NewLoopbackFileSystem(dir + "/orig"), } pfs := pathfs.NewPathNodeFs(fs, nil) state, conn, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(), nil) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } state.SetDebug(VerboseTest()) conn.SetDebug(VerboseTest()) pfs.SetDebug(VerboseTest()) go state.Serve() return dir, pfs, func() { err := state.Unmount() if err == nil { os.RemoveAll(dir) } } }
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) } } }
// Encrypted FUSE overlay filesystem func NewFS(args Args) *FS { return &FS{ CryptFS: cryptfs.NewCryptFS(args.Masterkey, args.OpenSSL, args.PlaintextNames, args.GCMIV128), FileSystem: pathfs.NewLoopbackFileSystem(args.Cipherdir), args: args, } }
// Encrypted FUSE overlay filesystem func NewFS(key []byte, backing string, useOpenssl bool) *FS { return &FS{ CryptFS: cryptfs.NewCryptFS(key, useOpenssl), FileSystem: pathfs.NewLoopbackFileSystem(backing), backing: backing, } }
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) } }
// 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, err = ioutil.TempDir("", "go-fuse") if err != nil { t.Fatalf("TempDir failed: %v", err) } 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: VerboseTest(), }) tc.state, err = fuse.NewServer( fuse.NewRawFileSystem(tc.connector.RawFS()), tc.mnt, &fuse.MountOptions{ SingleThreaded: true, Debug: 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 }
// Create and mount filesystem. func NewTestCase(t *testing.T) *testCase { me := &testCase{} me.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 me.tmpDir, err = ioutil.TempDir("", "go-fuse") if err != nil { t.Fatalf("TempDir failed: %v", err) } me.orig = me.tmpDir + "/orig" me.mnt = me.tmpDir + "/mnt" os.Mkdir(me.orig, 0700) os.Mkdir(me.mnt, 0700) me.mountFile = filepath.Join(me.mnt, name) me.mountSubdir = filepath.Join(me.mnt, subdir) me.origFile = filepath.Join(me.orig, name) me.origSubdir = filepath.Join(me.orig, subdir) var pfs pathfs.FileSystem pfs = pathfs.NewLoopbackFileSystem(me.orig) pfs = pathfs.NewLockingFileSystem(pfs) me.pathFs = pathfs.NewPathNodeFs(pfs, &pathfs.PathNodeFsOptions{ ClientInodes: true}) me.connector = nodefs.NewFileSystemConnector(me.pathFs.Root(), &nodefs.Options{ EntryTimeout: testTtl, AttrTimeout: testTtl, NegativeTimeout: 0.0, }) me.connector.SetDebug(VerboseTest()) me.state, err = fuse.NewServer( fuse.NewRawFileSystem(me.connector.RawFS()), me.mnt, &fuse.MountOptions{SingleThreaded: true}) if err != nil { t.Fatal("NewServer:", err) } me.state.SetDebug(VerboseTest()) // Unthreaded, but in background. go me.state.Serve() me.state.WaitMount() return me }
// NewFS returns a new encrypted FUSE overlay filesystem. func NewFS(args Args) *FS { cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) nameTransform := nametransform.New(cryptoCore, args.LongNames, args.Raw64) return &FS{ FileSystem: pathfs.NewLoopbackFileSystem(args.Cipherdir), args: args, nameTransform: nameTransform, contentEnc: contentEnc, } }
func startLoopbackServer(root string) (*loopbackServer, error) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return nil, err } s := grpc.NewServer() nfs := pathfs.NewLoopbackFileSystem(root) pb.RegisterPathFSServer(s, server.New(nfs)) go s.Serve(l) return &loopbackServer{ Server: s, Addr: l.Addr().String(), }, nil }
func TestMountRename(t *testing.T) { ts := NewTestCase(t) defer ts.Cleanup() fs := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil) code := ts.connector.Mount(ts.rootNode(), "mnt", fs.Root(), nil) if !code.Ok() { t.Fatal("mount should succeed") } err := os.Rename(ts.mnt+"/mnt", ts.mnt+"/foobar") if fuse.ToStatus(err) != fuse.EBUSY { t.Fatal("rename mount point should fail with EBUSY:", err) } ts.pathFs.Unmount("mnt") }
func TestGetAttrRace(t *testing.T) { dir, err := ioutil.TempDir("", "go-fuse-cache_test") if err != nil { t.Fatalf("failed: %v", err) } defer os.RemoveAll(dir) os.Mkdir(dir+"/mnt", 0755) os.Mkdir(dir+"/orig", 0755) fs := pathfs.NewLoopbackFileSystem(dir + "/orig") pfs := pathfs.NewPathNodeFs(fs, nil) state, conn, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(), &nodefs.Options{}) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } state.SetDebug(VerboseTest()) conn.SetDebug(VerboseTest()) pfs.SetDebug(VerboseTest()) go state.Serve() 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 TestRecursiveMount(t *testing.T) { ts := NewTestCase(t) defer ts.Cleanup() err := ioutil.WriteFile(ts.orig+"/hello.txt", []byte("blabla"), 0644) if err != nil { t.Fatalf("WriteFile failed: %v", err) } fs := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil) code := ts.connector.Mount(ts.rootNode(), "mnt", fs.Root(), nil) if !code.Ok() { t.Fatal("mount should succeed") } submnt := ts.mnt + "/mnt" _, err = os.Lstat(submnt) if err != nil { t.Fatalf("Lstat failed: %v", err) } _, err = os.Lstat(filepath.Join(submnt, "hello.txt")) if err != nil { t.Fatalf("Lstat failed: %v", err) } f, err := os.Open(filepath.Join(submnt, "hello.txt")) if err != nil { t.Fatalf("Open failed: %v", err) } code = ts.pathFs.Unmount("mnt") if code != fuse.EBUSY { t.Error("expect EBUSY") } if err := f.Close(); err != nil { t.Errorf("close: %v", err) } // We can't avoid a sleep here: the file handle release is not // synchronized. t.Log("Waiting for kernel to flush file-close to fuse...") time.Sleep(testTtl) code = ts.pathFs.Unmount("mnt") if code != fuse.OK { t.Error("umount failed.", code) } }
// Instantiate a new HookFS object func NewHookFs(original string, mountpoint string, hook Hook) (*HookFs, error) { log.WithFields(log.Fields{ "original": original, "mountpoint": mountpoint, }).Debug("Hooking a fs") loopbackfs := pathfs.NewLoopbackFileSystem(original) hookfs := &HookFs{ Original: original, Mountpoint: mountpoint, FsName: "hookfs", fs: loopbackfs, hook: hook, } return hookfs, nil }
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 TestMountReaddir(t *testing.T) { ts := NewTestCase(t) defer ts.Cleanup() fs := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil) code := ts.connector.Mount(ts.rootNode(), "mnt", fs.Root(), nil) if !code.Ok() { t.Fatal("mount should succeed") } entries, err := ioutil.ReadDir(ts.mnt) if err != nil { t.Fatalf("ReadDir failed: %v", err) } if len(entries) != 1 || entries[0].Name() != "mnt" { t.Error("wrong readdir result", entries) } ts.pathFs.Unmount("mnt") }
func (n *configNode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) { dir := content components := strings.Split(content, ":") if len(components) > 2 || len(components) == 0 { return nil, fuse.Status(syscall.EINVAL) } var root nodefs.Node if len(components) == 2 { dir = components[0] } if fi, err := os.Lstat(dir); err != nil { return nil, fuse.ToStatus(err) } else if !fi.IsDir() { return nil, fuse.Status(syscall.ENOTDIR) } var opts *nodefs.Options if len(components) == 1 { root = pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(content), nil).Root() } else { var err error root, err = NewGitFSRoot(content, n.fs.opts) if err != nil { log.Printf("NewGitFSRoot(%q): %v", content, err) return nil, fuse.ENOENT } opts = &nodefs.Options{ EntryTimeout: time.Hour, NegativeTimeout: time.Hour, AttrTimeout: time.Hour, PortableInodes: true, } } if code := n.fs.fsConn.Mount(n.corresponding.Inode(), name, root, opts); !code.Ok() { return nil, code } linkNode := newGitConfigNode(content) return n.Inode().NewChild(name, false, linkNode), fuse.OK }
func main() { // Scans the arg list and sets up flags debug := flag.Bool("debug", false, "print debugging messages.") other := flag.Bool("allow-other", false, "mount with -o allowother.") flag.Parse() if flag.NArg() < 2 { // TODO - where to get program name? fmt.Println("usage: main MOUNTPOINT ORIGINAL") os.Exit(2) } var finalFs pathfs.FileSystem orig := flag.Arg(1) loopbackfs := pathfs.NewLoopbackFileSystem(orig) finalFs = loopbackfs opts := &nodefs.Options{ // These options are to be compatible with libfuse defaults, // making benchmarking easier. NegativeTimeout: time.Second, AttrTimeout: time.Second, EntryTimeout: time.Second, } pathFs := pathfs.NewPathNodeFs(finalFs, nil) conn := nodefs.NewFileSystemConnector(pathFs.Root(), opts) mountPoint := flag.Arg(0) origAbs, _ := filepath.Abs(orig) mOpts := &fuse.MountOptions{ AllowOther: *other, Name: "loopbackfs", FsName: origAbs, } state, err := fuse.NewServer(conn.RawFS(), mountPoint, mOpts) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } state.SetDebug(*debug) fmt.Println("Mounted!") state.Serve() }
// NewFS returns an encrypted FUSE overlay filesystem. // In this case (reverse mode) the backing directory is plain-text and // ReverseFS provides an encrypted view. func NewFS(args fusefrontend.Args) *ReverseFS { if args.CryptoBackend != cryptocore.BackendAESSIV { log.Panic("reverse mode must use AES-SIV, everything else is insecure") } initLongnameCache() cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) nameTransform := nametransform.New(cryptoCore, args.LongNames, args.Raw64) return &ReverseFS{ // pathfs.defaultFileSystem returns ENOSYS for all operations FileSystem: pathfs.NewDefaultFileSystem(), loopbackfs: pathfs.NewLoopbackFileSystem(args.Cipherdir), args: args, nameTransform: nameTransform, contentEnc: contentEnc, inoGen: newInoGen(), inoMap: map[fusefrontend.DevInoStruct]uint64{}, } }
// Must run inside mutex. func (me *memNode) promote() { if me.backing == "" { me.backing = me.fs.getFilename() destfs := pathfs.NewLoopbackFileSystem("/") pathfs.CopyFile(me.fs.readonly, destfs, me.original, strings.TrimLeft(me.backing, "/"), nil) me.original = "" files := me.Inode().Files(0) for _, f := range files { mf := f.File.(*memNodeFile) inner := mf.File osFile, err := os.Open(me.backing) if err != nil { panic("error opening backing file") } mf.File = nodefs.NewLoopbackFile(osFile) inner.Flush() inner.Release() } } }
func setupMemUfs(t *testing.T) (workdir string, ufs *MemUnionFs, cleanup func()) { // Make sure system setting does not affect test. syscall.Umask(0) wd, _ := ioutil.TempDir("", "") err := os.Mkdir(wd+"/mnt", 0700) CheckSuccess(err) err = os.Mkdir(wd+"/backing", 0700) CheckSuccess(err) os.Mkdir(wd+"/ro", 0700) CheckSuccess(err) roFs := pathfs.NewLoopbackFileSystem(wd + "/ro") memFs, err := NewMemUnionFs(wd+"/backing", roFs) if err != nil { t.Fatal(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, } state, conn, err := nodefs.MountRoot(wd+"/mnt", memFs.Root(), opts) CheckSuccess(err) conn.SetDebug(VerboseTest()) state.SetDebug(VerboseTest()) go state.Serve() return wd, memFs, func() { state.Unmount() os.RemoveAll(wd) } }
func TestDeletedUnmount(t *testing.T) { ts := NewTestCase(t) defer ts.Cleanup() submnt := filepath.Join(ts.mnt, "mnt") pfs2 := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil) code := ts.connector.Mount(ts.rootNode(), "mnt", pfs2, nil) if !code.Ok() { t.Fatal("Mount error", code) } f, err := os.Create(filepath.Join(submnt, "hello.txt")) if err != nil { t.Fatalf("Create failed: %v", err) } t.Log("Removing") err = os.Remove(filepath.Join(submnt, "hello.txt")) if err != nil { t.Fatalf("Remove failed: %v", err) } t.Log("Removing") _, err = f.Write([]byte("bla")) if err != nil { t.Fatalf("Write failed: %v", err) } code = ts.pathFs.Unmount("mnt") if code != fuse.EBUSY { t.Error("expect EBUSY for unmount with open files", code) } f.Close() time.Sleep((3 * testTtl) / 2) code = ts.pathFs.Unmount("mnt") if !code.Ok() { t.Error("should succeed", code) } }
func TestOriginalIsSymlink(t *testing.T) { tmpDir, err := ioutil.TempDir("", "go-fuse-loopback_test") if err != nil { t.Fatalf("TempDir failed: %v", err) } 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" err = os.Mkdir(mnt, 0755) if err != nil { t.Fatalf("Mkdir failed: %v", err) } err = os.Symlink("orig", link) if err != nil { t.Fatalf("Symlink failed: %v", err) } fs := pathfs.NewLoopbackFileSystem(link) nfs := pathfs.NewPathNodeFs(fs, nil) state, _, err := nodefs.MountFileSystem(mnt, nfs, nil) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } defer state.Unmount() go state.Serve() _, err = os.Lstat(mnt) if err != nil { t.Fatalf("Lstat failed: %v", err) } }
func NewUnionFsFromRoots(roots []string, opts *UnionFsOptions, roCaching bool) (pathfs.FileSystem, error) { fses := make([]pathfs.FileSystem, 0) for i, r := range roots { var fs pathfs.FileSystem fi, err := os.Stat(r) if err != nil { return nil, err } if fi.IsDir() { fs = pathfs.NewLoopbackFileSystem(r) } if fs == nil { return nil, err } if i > 0 && roCaching { fs = NewCachingFileSystem(fs, 0) } fses = append(fses, fs) } return NewUnionFs(fses, *opts) }
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 main() { log.SetFlags(log.Lmicroseconds) // Scans the arg list and sets up flags debug := flag.Bool("debug", false, "print debugging messages.") other := flag.Bool("allow-other", false, "mount with -o allowother.") enableLinks := flag.Bool("l", false, "Enable hard link support") cpuprofile := flag.String("cpuprofile", "", "write cpu profile to this file") memprofile := flag.String("memprofile", "", "write memory profile to this file") flag.Parse() if flag.NArg() < 2 { fmt.Printf("usage: %s MOUNTPOINT ORIGINAL\n", path.Base(os.Args[0])) fmt.Printf("\noptions:\n") flag.PrintDefaults() os.Exit(2) } if *cpuprofile != "" { fmt.Printf("Writing cpu profile to %s\n", *cpuprofile) f, err := os.Create(*cpuprofile) if err != nil { fmt.Println(err) os.Exit(3) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if *memprofile != "" { log.Printf("send SIGUSR1 to %d to dump memory profile", os.Getpid()) profSig := make(chan os.Signal, 1) signal.Notify(profSig, syscall.SIGUSR1) go writeMemProfile(*memprofile, profSig) } if *cpuprofile != "" || *memprofile != "" { fmt.Printf("Note: You must unmount gracefully, otherwise the profile file(s) will stay empty!\n") } var finalFs pathfs.FileSystem orig := flag.Arg(1) loopbackfs := pathfs.NewLoopbackFileSystem(orig) finalFs = loopbackfs opts := &nodefs.Options{ // These options are to be compatible with libfuse defaults, // making benchmarking easier. NegativeTimeout: time.Second, AttrTimeout: time.Second, EntryTimeout: time.Second, } // Enable ClientInodes so hard links work pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: *enableLinks} pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts) conn := nodefs.NewFileSystemConnector(pathFs.Root(), opts) mountPoint := flag.Arg(0) origAbs, _ := filepath.Abs(orig) mOpts := &fuse.MountOptions{ AllowOther: *other, Name: "loopbackfs", FsName: origAbs, Debug: *debug, } state, err := fuse.NewServer(conn.RawFS(), mountPoint, mOpts) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } fmt.Println("Mounted!") state.Serve() }
func TestXAttrCaching(t *testing.T) { wd, _ := ioutil.TempDir("", "unionfs") 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, } pathfs := pathfs.NewPathNodeFs(ufs, &pathfs.PathNodeFsOptions{ClientInodes: true}) server, conn, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } server.SetDebug(VerboseTest()) conn.SetDebug(VerboseTest()) pathfs.SetDebug(VerboseTest()) defer server.Unmount() go server.Serve() 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) 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 / 2) n, err = syscall.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) } server.Unmount() if roFS.xattrRead != 1 { t.Errorf("got xattrRead=%d, want 1", roFS.xattrRead) } }
func (me *memNode) StatFs() *fuse.StatfsOut { backingFs := pathfs.NewLoopbackFileSystem(me.fs.backingStore) return backingFs.StatFs("") }
func TestUnionFsDisappearing(t *testing.T) { // This init is like setupUfs, but we want access to the // writable Fs. wd, _ := ioutil.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, } 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() state.SetDebug(VerboseTest()) go state.Serve() 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") } // Restore, and wait for caches to catch up. wrFs.visibleChan <- true time.Sleep((3 * entryTtl) / 2) _, 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) } }