func setupUfs(t *testing.T) (workdir string, cleanup func()) { wd := fuse.MakeTempDir() err := os.Mkdir(wd+"/mount", 0700) fuse.CheckSuccess(err) err = os.Mkdir(wd+"/rw", 0700) fuse.CheckSuccess(err) os.Mkdir(wd+"/ro", 0700) fuse.CheckSuccess(err) var fses []fuse.FileSystem fses = append(fses, fuse.NewLoopbackFileSystem(wd+"/rw")) fses = append(fses, NewCachingFileSystem(fuse.NewLoopbackFileSystem(wd+"/ro"), 0)) ufs := NewUnionFs(fses, testOpts) // We configure timeouts are smaller, so we can check for // UnionFs's cache consistency. opts := &fuse.FileSystemOptions{ EntryTimeout: .5 * entryTtl, AttrTimeout: .5 * entryTtl, NegativeTimeout: .5 * entryTtl, } state, _, err := fuse.MountFileSystem(wd+"/mount", ufs, opts) CheckSuccess(err) state.Debug = true go state.Loop(false) return wd, func() { state.Unmount() os.RemoveAll(wd) } }
func setupUfs(t *testing.T) (workdir string, cleanup func()) { wd := fuse.MakeTempDir() err := os.Mkdir(wd+"/mount", 0700) fuse.CheckSuccess(err) err = os.Mkdir(wd+"/rw", 0700) fuse.CheckSuccess(err) os.Mkdir(wd+"/ro", 0700) fuse.CheckSuccess(err) var fses []fuse.FileSystem fses = append(fses, fuse.NewLoopbackFileSystem(wd+"/rw")) fses = append(fses, fuse.NewLoopbackFileSystem(wd+"/ro")) ufs := NewUnionFs("testFs", fses, testOpts) opts := &fuse.FileSystemOptions{ EntryTimeout: entryTtl, AttrTimeout: entryTtl, NegativeTimeout: entryTtl, } state, _, err := fuse.MountFileSystem(wd+"/mount", ufs, opts) CheckSuccess(err) state.Debug = true go state.Loop(false) return wd, func() { state.Unmount() os.RemoveAll(wd) } }
// Creates 3 directories on a temporary dir: /mnt with the overlayed // (unionfs) mount, rw with modifiable data, and ro on the bottom. func setupUfs(t *testing.T) (workdir string, cleanup func()) { // Make sure system setting does not affect test. syscall.Umask(0) wd, _ := ioutil.TempDir("", "unionfs") 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 []fuse.FileSystem fses = append(fses, fuse.NewLoopbackFileSystem(wd+"/rw")) fses = append(fses, NewCachingFileSystem(fuse.NewLoopbackFileSystem(wd+"/ro"), 0)) ufs := NewUnionFs(fses, testOpts) // We configure timeouts are smaller, so we can check for // UnionFs's cache consistency. opts := &fuse.FileSystemOptions{ EntryTimeout: entryTtl / 2, AttrTimeout: entryTtl / 2, NegativeTimeout: entryTtl / 2, } pathfs := fuse.NewPathNodeFs(ufs, &fuse.PathNodeFsOptions{ClientInodes: true}) state, conn, err := fuse.MountNodeFileSystem(wd+"/mnt", pathfs, opts) if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } conn.Debug = fuse.VerboseTest() state.Debug = fuse.VerboseTest() go state.Loop() return wd, func() { err := state.Unmount() if err != nil { return } setRecursiveWritable(t, wd, true) os.RemoveAll(wd) } }
func NewUnionFsFromRoots(roots []string, opts *UnionFsOptions, roCaching bool) (*UnionFs, os.Error) { fses := make([]fuse.FileSystem, 0) for i, r := range roots { var fs fuse.FileSystem fi, err := os.Stat(r) if err != nil { return nil, err } if fi.IsDirectory() { fs = fuse.NewLoopbackFileSystem(r) } if fs == nil { fs, err = zipfs.NewArchiveFileSystem(r) } if fs == nil { return nil, err } if i > 0 && roCaching { fs = NewCachingFileSystem(fs, 0) } fses = append(fses, fs) } return NewUnionFs(fses, *opts), nil }
func (me *WorkerDaemon) newWorkerTask(req *WorkRequest, rep *WorkReply) (*WorkerTask, os.Error) { fs, err := me.getFileServer(req.FileServer) if err != nil { return nil, err } w := &WorkerTask{ WorkRequest: req, WorkReply: rep, daemon: me, } tmpDir, err := ioutil.TempDir("", "rpcfs-tmp") type dirInit struct { dst *string val string } for _, v := range []dirInit{ dirInit{&w.rwDir, "rw"}, dirInit{&w.mount, "mnt"}, } { *v.dst = filepath.Join(tmpDir, v.val) err = os.Mkdir(*v.dst, 0700) if err != nil { return nil, err } } rwFs := fuse.NewLoopbackFileSystem(w.rwDir) roFs := NewRpcFs(fs, me.contentCache) // High ttl, since all writes come through fuse. ttl := 100.0 opts := unionfs.UnionFsOptions{ BranchCacheTTLSecs: ttl, DeletionCacheTTLSecs: ttl, DeletionDirName: _DELETIONS, } mOpts := fuse.FileSystemOptions{ EntryTimeout: ttl, AttrTimeout: ttl, NegativeTimeout: ttl, } ufs := unionfs.NewUnionFs("ufs", []fuse.FileSystem{rwFs, roFs}, opts) conn := fuse.NewFileSystemConnector(ufs, &mOpts) state := fuse.NewMountState(conn) state.Mount(w.mount, &fuse.MountOptions{AllowOther: true}) if err != nil { return nil, err } w.MountState = state go state.Loop(true) return w, nil }
func setupUfs(t *testing.T) (workdir string, cleanup func()) { // Make sure system setting does not affect test. syscall.Umask(0) wd, _ := ioutil.TempDir("", "") err := os.Mkdir(wd+"/mnt", 0700) fuse.CheckSuccess(err) err = os.Mkdir(wd+"/rw", 0700) fuse.CheckSuccess(err) os.Mkdir(wd+"/ro", 0700) fuse.CheckSuccess(err) var fses []fuse.FileSystem fses = append(fses, fuse.NewLoopbackFileSystem(wd+"/rw")) fses = append(fses, NewCachingFileSystem(fuse.NewLoopbackFileSystem(wd+"/ro"), 0)) ufs := NewUnionFs(fses, testOpts) // We configure timeouts are smaller, so we can check for // UnionFs's cache consistency. opts := &fuse.FileSystemOptions{ EntryTimeout: .5 * entryTtl, AttrTimeout: .5 * entryTtl, NegativeTimeout: .5 * entryTtl, } pathfs := fuse.NewPathNodeFs(ufs, &fuse.PathNodeFsOptions{ClientInodes: true}) state, conn, err := fuse.MountNodeFileSystem(wd+"/mnt", pathfs, opts) CheckSuccess(err) conn.Debug = fuse.VerboseTest() state.Debug = fuse.VerboseTest() go state.Loop() return wd, func() { state.Unmount() os.RemoveAll(wd) } }
func NewProcFs() *ProcFs { return &ProcFs{ LoopbackFileSystem: fuse.NewLoopbackFileSystem("/proc"), StripPrefix: "/", AllowedRootFiles: map[string]int{ "meminfo": 1, "cpuinfo": 1, "iomem": 1, "ioport": 1, "loadavg": 1, "stat": 1, "self": 1, "filesystems": 1, "mounts": 1, }, } }
func TestCachingFs(t *testing.T) { wd := fuse.MakeTempDir() defer os.RemoveAll(wd) fs := fuse.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.IsDirectory() { 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 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 fuse.FileSystem orig := flag.Arg(1) loopbackfs := fuse.NewLoopbackFileSystem(orig) finalFs = loopbackfs opts := &fuse.FileSystemOptions{ // These options are to be compatible with libfuse defaults, // making benchmarking easier. NegativeTimeout: time.Second, AttrTimeout: time.Second, EntryTimeout: time.Second, } pathFs := fuse.NewPathNodeFs(finalFs, nil) conn := fuse.NewFileSystemConnector(pathFs, opts) state := fuse.NewMountState(conn) state.Debug = *debug mountPoint := flag.Arg(0) fmt.Println("Mounting") mOpts := &fuse.MountOptions{ AllowOther: *other, } err := state.Mount(mountPoint, mOpts) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } fmt.Println("Mounted!") state.Loop() }
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) fuse.CheckSuccess(err) err = os.Mkdir(wd+"/backing", 0700) fuse.CheckSuccess(err) os.Mkdir(wd+"/ro", 0700) fuse.CheckSuccess(err) roFs := fuse.NewLoopbackFileSystem(wd + "/ro") memFs := NewMemUnionFs(wd+"/backing", roFs) // We configure timeouts are smaller, so we can check for // UnionFs's cache consistency. opts := &fuse.FileSystemOptions{ EntryTimeout: entryTtl / 2, AttrTimeout: entryTtl / 2, NegativeTimeout: entryTtl / 2, PortableInodes: true, } state, conn, err := fuse.MountNodeFileSystem(wd+"/mnt", memFs, opts) CheckSuccess(err) conn.Debug = fuse.VerboseTest() state.Debug = fuse.VerboseTest() go state.Loop() return wd, memFs, func() { state.Unmount() os.RemoveAll(wd) } }
func NewUnionFsFromRoots(roots []string, opts *UnionFsOptions) (*UnionFs, os.Error) { fses := make([]fuse.FileSystem, 0) for _, r := range roots { var fs fuse.FileSystem fi, err := os.Stat(r) if err != nil { return nil, err } if fi.IsDirectory() { fs = fuse.NewLoopbackFileSystem(r) } if fs == nil { fs, err = zipfs.NewArchiveFileSystem(r) } if fs == nil { return nil, err } fses = append(fses, fs) } identifier := fmt.Sprintf("%v", roots) return NewUnionFs(identifier, fses, *opts), nil }
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) fuse.CheckSuccess(err) err = os.Mkdir(wd+"/rw", 0700) fuse.CheckSuccess(err) os.Mkdir(wd+"/ro", 0700) fuse.CheckSuccess(err) wrFs := fuse.NewLoopbackFileSystem(wd + "/rw") var fses []fuse.FileSystem fses = append(fses, wrFs) fses = append(fses, fuse.NewLoopbackFileSystem(wd+"/ro")) ufs := NewUnionFs(fses, testOpts) opts := &fuse.FileSystemOptions{ EntryTimeout: entryTtl, AttrTimeout: entryTtl, NegativeTimeout: entryTtl, } nfs := fuse.NewPathNodeFs(ufs, nil) state, _, err := fuse.MountNodeFileSystem(wd+"/mnt", nfs, opts) CheckSuccess(err) defer state.Unmount() state.Debug = fuse.VerboseTest() go state.Loop() log.Println("TestUnionFsDisappearing2") err = ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644) CheckSuccess(err) freezeRo(wd + "/ro") err = os.Remove(wd + "/mnt/file") CheckSuccess(err) oldRoot := wrFs.Root wrFs.Root = "/dev/null" time.Sleep(1.5 * entryTtl * 1e9) _, err = ioutil.ReadDir(wd + "/mnt") if err == nil { t.Fatal("Readdir should have failed") } log.Println("expected readdir failure:", err) err = ioutil.WriteFile(wd+"/mnt/file2", []byte("blabla"), 0644) if err == nil { t.Fatal("write should have failed") } log.Println("expected write failure:", err) // Restore, and wait for caches to catch up. wrFs.Root = oldRoot time.Sleep(1.5 * entryTtl * 1e9) _, 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 newWorkerFuseFs(tmpDir string, rpcFs fuse.FileSystem, writableRoot string, nobody *User) (*workerFuseFs, error) { tmpDir, err := ioutil.TempDir(tmpDir, "termite-task") if err != nil { return nil, err } me := &workerFuseFs{ tmpDir: tmpDir, writableRoot: strings.TrimLeft(writableRoot, "/"), tasks: map[*WorkerTask]bool{}, } type dirInit struct { dst *string val string } tmpBacking := "" for _, v := range []dirInit{ {&me.rwDir, "rw"}, {&me.mount, "mnt"}, {&tmpBacking, "tmp-backing"}, } { *v.dst = filepath.Join(me.tmpDir, v.val) err = os.Mkdir(*v.dst, 0700) if err != nil { return nil, err } } fuseOpts := fuse.MountOptions{} if os.Geteuid() == 0 { fuseOpts.AllowOther = true } me.rpcNodeFs = fuse.NewPathNodeFs(rpcFs, nil) ttl := 30 * time.Second mOpts := fuse.FileSystemOptions{ EntryTimeout: ttl, AttrTimeout: ttl, NegativeTimeout: ttl, // 32-bit programs have trouble with 64-bit inode // numbers. PortableInodes: true, } me.fsConnector = fuse.NewFileSystemConnector(me.rpcNodeFs, &mOpts) me.MountState = fuse.NewMountState(me.fsConnector) err = me.MountState.Mount(me.mount, &fuseOpts) if err != nil { return nil, err } go me.MountState.Loop() me.unionFs = fs.NewMemUnionFs( me.rwDir, &fuse.PrefixFileSystem{rpcFs, me.writableRoot}) me.procFs = fs.NewProcFs() me.procFs.StripPrefix = me.mount if nobody != nil { me.procFs.Uid = nobody.Uid } type submount struct { mountpoint string fs fuse.NodeFileSystem } mounts := []submount{ {"proc", fuse.NewPathNodeFs(me.procFs, nil)}, {"sys", fuse.NewPathNodeFs(&fuse.ReadonlyFileSystem{fuse.NewLoopbackFileSystem("/sys")}, nil)}, {"tmp", fuse.NewMemNodeFs(tmpBacking + "/tmp")}, {"dev", fs.NewDevNullFs()}, {"var/tmp", fuse.NewMemNodeFs(tmpBacking + "/vartmp")}, } for _, s := range mounts { subOpts := &mOpts if s.mountpoint == "proc" { subOpts = nil } code := me.rpcNodeFs.Mount(s.mountpoint, s.fs, subOpts) if !code.Ok() { if err := me.MountState.Unmount(); err != nil { log.Fatal("FUSE unmount error during cleanup:", err) } return nil, errors.New(fmt.Sprintf("submount error for %s: %v", s.mountpoint, code)) } } if strings.HasPrefix(me.writableRoot, "tmp/") { parent, _ := filepath.Split(me.writableRoot) err := os.MkdirAll(filepath.Join(me.mount, parent), 0755) if err != nil { if err := me.MountState.Unmount(); err != nil { log.Fatal("FUSE unmount error during cleanup:", err) } return nil, errors.New(fmt.Sprintf("Mkdir of %q in /tmp fail: %v", parent, err)) } // This is hackish, but we don't want rpcfs/fsserver // getting confused by asking for tmp/foo/bar // directly. rpcFs.GetAttr("tmp", nil) rpcFs.GetAttr(me.writableRoot, nil) } code := me.rpcNodeFs.Mount(me.writableRoot, me.unionFs, &mOpts) if !code.Ok() { if err := me.MountState.Unmount(); err != nil { log.Fatal("FUSE unmount error during cleanup:", err) } return nil, errors.New(fmt.Sprintf("submount error for %s: %v", me.writableRoot, code)) } return me, nil }
func main() { // Scans the arg list and sets up flags debug := flag.Bool("debug", false, "print debugging messages.") latencies := flag.Bool("latencies", false, "record latencies.") threaded := flag.Bool("threaded", true, "switch off threading; print debugging messages.") flag.Parse() if flag.NArg() < 2 { // TODO - where to get program name? fmt.Println("usage: main MOUNTPOINT ORIGINAL") os.Exit(2) } var finalFs fuse.FileSystem orig := flag.Arg(1) loopbackfs := fuse.NewLoopbackFileSystem(orig) finalFs = loopbackfs debugFs := fuse.NewFileSystemDebug() if *latencies { timing := fuse.NewTimingFileSystem(finalFs) debugFs.AddTimingFileSystem(timing) finalFs = timing } opts := &fuse.FileSystemOptions{ // These options are to be compatible with libfuse defaults, // making benchmarking easier. NegativeTimeout: 1.0, AttrTimeout: 1.0, EntryTimeout: 1.0, } if *latencies { debugFs.FileSystem = finalFs finalFs = debugFs } conn := fuse.NewFileSystemConnector(finalFs, opts) var finalRawFs fuse.RawFileSystem = conn if *latencies { rawTiming := fuse.NewTimingRawFileSystem(conn) debugFs.AddRawTimingFileSystem(rawTiming) finalRawFs = rawTiming } state := fuse.NewMountState(finalRawFs) state.Debug = *debug if *latencies { state.SetRecordStatistics(true) debugFs.AddMountState(state) debugFs.AddFileSystemConnector(conn) } mountPoint := flag.Arg(0) fmt.Println("Mounting") err := state.Mount(mountPoint, nil) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } fmt.Println("Mounted!") state.Loop(*threaded) }