From 9370460df872b9d6c743d385bab7cecc45535be7 Mon Sep 17 00:00:00 2001 From: Cedric Staub Date: Mon, 15 Aug 2016 13:38:11 -0700 Subject: [PATCH] Update go-fuse to latest version --- glide.lock | 2 +- vendor/github.com/hanwen/go-fuse/AUTHORS | 15 ++ vendor/github.com/hanwen/go-fuse/CONTRIBUTING | 18 -- vendor/github.com/hanwen/go-fuse/LICENSE | 2 +- vendor/github.com/hanwen/go-fuse/README | 3 +- vendor/github.com/hanwen/go-fuse/all.bash | 22 ++- .../hanwen/go-fuse/benchmark/Makefile | 6 + .../hanwen/go-fuse/benchmark/benchmark.go | 106 +---------- .../hanwen/go-fuse/benchmark/bulkstat/main.go | 91 +++++++++ .../hanwen/go-fuse/benchmark/latencymap.go | 8 + .../go-fuse/benchmark/latencymap_test.go | 4 + .../hanwen/go-fuse/benchmark/stat_test.go | 154 ++++++++++----- .../hanwen/go-fuse/benchmark/statfs.go | 42 ++-- .../go-fuse/example/autounionfs/main.go | 16 +- .../hanwen/go-fuse/example/hello/main.go | 4 + .../hanwen/go-fuse/example/loopback/main.go | 42 ++-- .../hanwen/go-fuse/example/memfs/main.go | 9 +- .../hanwen/go-fuse/example/multizip/main.go | 9 +- .../hanwen/go-fuse/example/statfs/main.go | 6 +- .../hanwen/go-fuse/example/unionfs/main.go | 6 +- .../hanwen/go-fuse/example/zipfs/main.go | 6 +- vendor/github.com/hanwen/go-fuse/fuse/api.go | 11 ++ vendor/github.com/hanwen/go-fuse/fuse/attr.go | 4 + .../hanwen/go-fuse/fuse/attr_darwin.go | 4 + .../hanwen/go-fuse/fuse/attr_linux.go | 4 + .../hanwen/go-fuse/fuse/bufferpool.go | 4 + .../hanwen/go-fuse/fuse/bufferpool_test.go | 4 + .../hanwen/go-fuse/fuse/constants.go | 4 + .../hanwen/go-fuse/fuse/constants_freebsd.go | 4 + .../hanwen/go-fuse/fuse/constants_linux.go | 4 + .../hanwen/go-fuse/fuse/defaultraw.go | 6 +- .../hanwen/go-fuse/fuse/direntry.go | 4 + .../hanwen/go-fuse/fuse/lockingfs.go | 4 + vendor/github.com/hanwen/go-fuse/fuse/misc.go | 4 + .../hanwen/go-fuse/fuse/misc_test.go | 4 + .../hanwen/go-fuse/fuse/mount_darwin.go | 179 ++++++------------ .../hanwen/go-fuse/fuse/mount_linux.go | 20 +- .../hanwen/go-fuse/fuse/nodefs/api.go | 27 ++- .../hanwen/go-fuse/fuse/nodefs/defaultfile.go | 4 + .../hanwen/go-fuse/fuse/nodefs/defaultnode.go | 6 +- .../hanwen/go-fuse/fuse/nodefs/dir.go | 4 + .../go-fuse/fuse/nodefs/fileless_test.go | 42 ++-- .../hanwen/go-fuse/fuse/nodefs/files.go | 4 + .../go-fuse/fuse/nodefs/files_darwin.go | 4 + .../hanwen/go-fuse/fuse/nodefs/files_linux.go | 4 + .../hanwen/go-fuse/fuse/nodefs/fsconnector.go | 74 ++++---- .../hanwen/go-fuse/fuse/nodefs/fsmount.go | 9 +- .../hanwen/go-fuse/fuse/nodefs/fsops.go | 21 +- .../hanwen/go-fuse/fuse/nodefs/fuse.go | 11 +- .../hanwen/go-fuse/fuse/nodefs/handle.go | 4 + .../hanwen/go-fuse/fuse/nodefs/handle_test.go | 4 + .../hanwen/go-fuse/fuse/nodefs/inode.go | 32 +++- .../hanwen/go-fuse/fuse/nodefs/lockingfile.go | 4 + .../hanwen/go-fuse/fuse/nodefs/memnode.go | 4 + .../go-fuse/fuse/nodefs/memnode_test.go | 20 +- .../hanwen/go-fuse/fuse/nodefs/nodefs.go | 4 + .../hanwen/go-fuse/fuse/nodefs/print.go | 4 + .../go-fuse/fuse/nodefs/syscall_linux.go | 4 + .../go-fuse/fuse/nodefs/verbose_test.go | 4 + .../github.com/hanwen/go-fuse/fuse/opcode.go | 17 +- .../hanwen/go-fuse/fuse/pathfs/api.go | 7 + .../hanwen/go-fuse/fuse/pathfs/copy.go | 4 + .../hanwen/go-fuse/fuse/pathfs/copy_test.go | 18 +- .../hanwen/go-fuse/fuse/pathfs/default.go | 6 +- .../hanwen/go-fuse/fuse/pathfs/locking.go | 4 + .../hanwen/go-fuse/fuse/pathfs/loopback.go | 10 + .../go-fuse/fuse/pathfs/loopback_darwin.go | 9 +- .../go-fuse/fuse/pathfs/loopback_linux.go | 4 + .../hanwen/go-fuse/fuse/pathfs/owner_test.go | 11 +- .../hanwen/go-fuse/fuse/pathfs/pathfs.go | 165 ++++++---------- .../hanwen/go-fuse/fuse/pathfs/prefixfs.go | 4 + .../hanwen/go-fuse/fuse/pathfs/readonlyfs.go | 4 + .../go-fuse/fuse/pathfs/syscall_linux.go | 4 + .../go-fuse/fuse/pathfs/syscall_test.go | 8 +- .../go-fuse/fuse/pathfs/verbose_test.go | 4 + .../hanwen/go-fuse/fuse/pathfs/xattr_test.go | 31 ++- .../github.com/hanwen/go-fuse/fuse/print.go | 4 + .../hanwen/go-fuse/fuse/print_darwin.go | 6 +- .../hanwen/go-fuse/fuse/print_linux.go | 4 + vendor/github.com/hanwen/go-fuse/fuse/read.go | 4 + .../github.com/hanwen/go-fuse/fuse/request.go | 4 + .../hanwen/go-fuse/fuse/request_darwin.go | 4 + .../hanwen/go-fuse/fuse/request_linux.go | 4 + .../github.com/hanwen/go-fuse/fuse/server.go | 167 +++++++++++----- .../hanwen/go-fuse/fuse/server_darwin.go | 4 + .../hanwen/go-fuse/fuse/server_linux.go | 4 + .../hanwen/go-fuse/fuse/splice_darwin.go | 4 + .../hanwen/go-fuse/fuse/splice_linux.go | 4 + .../hanwen/go-fuse/fuse/syscall_darwin.go | 4 + .../hanwen/go-fuse/fuse/syscall_linux.go | 4 + .../hanwen/go-fuse/fuse/test/cache_test.go | 98 +++++----- .../go-fuse/fuse/test/defaultread_test.go | 21 +- .../go-fuse/fuse/test/delete_linux_test.go | 20 +- .../hanwen/go-fuse/fuse/test/fsetattr_test.go | 59 +++--- .../go-fuse/fuse/test/loopback_darwin_test.go | 20 +- .../go-fuse/fuse/test/loopback_linux_test.go | 21 ++ .../hanwen/go-fuse/fuse/test/loopback_test.go | 67 ++++--- .../hanwen/go-fuse/fuse/test/mount_test.go | 41 +++- .../go-fuse/fuse/test/notify_linux_test.go | 18 +- .../hanwen/go-fuse/fuse/test/xattr_test.go | 66 +++++++ .../hanwen/go-fuse/fuse/test/xfs_test.go | 6 + .../hanwen/go-fuse/fuse/typeprint.go | 4 + .../github.com/hanwen/go-fuse/fuse/types.go | 17 +- .../hanwen/go-fuse/fuse/types_darwin.go | 12 ++ .../hanwen/go-fuse/fuse/types_linux.go | 12 ++ .../github.com/hanwen/go-fuse/fuse/upgrade.go | 4 + .../go-fuse/internal/testutil/tempdir.go | 38 ++++ .../test => internal/testutil}/verbose.go | 6 +- .../github.com/hanwen/go-fuse/splice/copy.go | 4 + .../hanwen/go-fuse/splice/copy_test.go | 4 + .../github.com/hanwen/go-fuse/splice/pair.go | 4 + .../hanwen/go-fuse/splice/pair_darwin.go | 4 + .../hanwen/go-fuse/splice/pair_linux.go | 4 + .../github.com/hanwen/go-fuse/splice/pool.go | 4 + .../hanwen/go-fuse/splice/splice.go | 4 + .../hanwen/go-fuse/splice/splice_test.go | 4 + .../hanwen/go-fuse/unionfs/autounion.go | 89 +++------ .../go-fuse/unionfs/autounion_race_test.go | 35 ---- .../hanwen/go-fuse/unionfs/autounion_test.go | 54 ++++-- .../hanwen/go-fuse/unionfs/cachingfs.go | 4 + .../hanwen/go-fuse/unionfs/cachingfs_test.go | 8 +- .../hanwen/go-fuse/unionfs/create.go | 4 + .../hanwen/go-fuse/unionfs/dircache.go | 4 + .../hanwen/go-fuse/unionfs/timedcache.go | 4 + .../hanwen/go-fuse/unionfs/timedcache_test.go | 4 + .../hanwen/go-fuse/unionfs/unionfs.go | 8 +- .../hanwen/go-fuse/unionfs/unionfs_test.go | 72 ++----- .../go-fuse/unionfs/unionfs_test_linux.go | 45 +++++ .../go-fuse/unionfs/unionfs_xattr_test.go | 45 +++-- .../unionfs/unionfs_xattr_test_darwin.go | 37 ++++ .../unionfs/unionfs_xattr_test_linux.go | 14 ++ .../hanwen/go-fuse/unionfs/verbose_test.go | 9 - .../hanwen/go-fuse/zipfs/memtree.go | 7 +- .../hanwen/go-fuse/zipfs/multizip.go | 7 +- .../hanwen/go-fuse/zipfs/multizip_test.go | 46 +++-- .../github.com/hanwen/go-fuse/zipfs/tarfs.go | 4 + .../github.com/hanwen/go-fuse/zipfs/zipfs.go | 4 + .../hanwen/go-fuse/zipfs/zipfs_test.go | 13 +- 138 files changed, 1759 insertions(+), 980 deletions(-) create mode 100644 vendor/github.com/hanwen/go-fuse/AUTHORS create mode 100644 vendor/github.com/hanwen/go-fuse/benchmark/bulkstat/main.go create mode 100644 vendor/github.com/hanwen/go-fuse/fuse/test/xattr_test.go create mode 100644 vendor/github.com/hanwen/go-fuse/internal/testutil/tempdir.go rename vendor/github.com/hanwen/go-fuse/{fuse/test => internal/testutil}/verbose.go (51%) delete mode 100644 vendor/github.com/hanwen/go-fuse/unionfs/autounion_race_test.go create mode 100644 vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test_linux.go create mode 100644 vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_darwin.go create mode 100644 vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_linux.go delete mode 100644 vendor/github.com/hanwen/go-fuse/unionfs/verbose_test.go diff --git a/glide.lock b/glide.lock index 9afbcc9..81032ea 100644 --- a/glide.lock +++ b/glide.lock @@ -8,7 +8,7 @@ imports: - name: github.com/alecthomas/units version: 2efee857e7cfd4f3d0138cc3cbb1b4966962b93a - name: github.com/hanwen/go-fuse - version: ed841342e5daf4711f24106b93c1035864a8914b + version: e33e399b19f2fc4ad8922d005a1eff55ea9ffcbc subpackages: - fuse - fuse/nodefs diff --git a/vendor/github.com/hanwen/go-fuse/AUTHORS b/vendor/github.com/hanwen/go-fuse/AUTHORS new file mode 100644 index 0000000..4a4ab69 --- /dev/null +++ b/vendor/github.com/hanwen/go-fuse/AUTHORS @@ -0,0 +1,15 @@ +Adam H. Leventhal +Daniel Martí +Fazlul Shahriar +Frederick Akalin +Google Inc. +Haitao Li +Jakob Unterwurzacher +James D. Nurmi +Jeff +Logan Hanks +Nick Cooper +Patrick Crosby +Paul Jolly +Valient Gough +Yongwoo Park diff --git a/vendor/github.com/hanwen/go-fuse/CONTRIBUTING b/vendor/github.com/hanwen/go-fuse/CONTRIBUTING index 16a740c..a9a0ece 100644 --- a/vendor/github.com/hanwen/go-fuse/CONTRIBUTING +++ b/vendor/github.com/hanwen/go-fuse/CONTRIBUTING @@ -2,21 +2,3 @@ Before sending a patch or Pull request, please fill out a Google CLA, using the following form: https://cla.developers.google.com/clas - -For complex changes, please use Gerrit: - -* Connect your github account with gerrithub, at - - https://review.gerrithub.io/static/intro.html - -* The signup will setup your SSH keys from Github. - -* Create your change as a commit - -* Add an ID to the commit message: - - echo "Change-Id: I"$(head -c 20 /dev/urandom | sha1sum | awk '{print $1}') - -* Push the change for review, - - git push ssh://$USER@review.gerrithub.io:29418/hanwen/go-fuse HEAD:refs/for/master diff --git a/vendor/github.com/hanwen/go-fuse/LICENSE b/vendor/github.com/hanwen/go-fuse/LICENSE index d3fb206..962771c 100644 --- a/vendor/github.com/hanwen/go-fuse/LICENSE +++ b/vendor/github.com/hanwen/go-fuse/LICENSE @@ -1,6 +1,6 @@ // New BSD License // -// Copyright (c) 2010 Ivan Krasin (imkrasin@gmail.com). All rights reserved. +// Copyright (c) 2010 the Go-FUSE Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/hanwen/go-fuse/README b/vendor/github.com/hanwen/go-fuse/README index 2576329..d79c04d 100644 --- a/vendor/github.com/hanwen/go-fuse/README +++ b/vendor/github.com/hanwen/go-fuse/README @@ -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 diff --git a/vendor/github.com/hanwen/go-fuse/all.bash b/vendor/github.com/hanwen/go-fuse/all.bash index e7331f6..5e45f93 100755 --- a/vendor/github.com/hanwen/go-fuse/all.bash +++ b/vendor/github.com/hanwen/go-fuse/all.bash @@ -1,6 +1,18 @@ #!/bin/sh set -eu + +for d in fuse zipfs unionfs fuse/test +do + ( + cd $d + echo "go test github.com/hanwen/go-fuse/$d" + go test github.com/hanwen/go-fuse/$d + echo "go test -race github.com/hanwen/go-fuse/$d" + go test -race github.com/hanwen/go-fuse/$d + ) +done + for target in "clean" "install" ; do for d in fuse fuse/nodefs fuse/pathfs fuse/test zipfs unionfs \ example/hello example/loopback example/zipfs \ @@ -15,16 +27,6 @@ for target in "clean" "install" ; do done done -for d in fuse zipfs unionfs fuse/test -do - ( - cd $d - echo "go test github.com/hanwen/go-fuse/$d" - go test github.com/hanwen/go-fuse/$d - echo "go test -race github.com/hanwen/go-fuse/$d" - go test -race github.com/hanwen/go-fuse/$d - ) -done make -C benchmark for d in benchmark diff --git a/vendor/github.com/hanwen/go-fuse/benchmark/Makefile b/vendor/github.com/hanwen/go-fuse/benchmark/Makefile index c17bede..ab6418a 100644 --- a/vendor/github.com/hanwen/go-fuse/benchmark/Makefile +++ b/vendor/github.com/hanwen/go-fuse/benchmark/Makefile @@ -1,2 +1,8 @@ +all: cstatfs bulkstat.bin + cstatfs: statfs.cc g++ -O2 -Wall -std=c++0x $< `pkg-config fuse --cflags --libs` -o $@ + +bulkstat.bin: bulkstat/main.go + (cd bulkstat && go build main.go) + cp bulkstat/main $@ diff --git a/vendor/github.com/hanwen/go-fuse/benchmark/benchmark.go b/vendor/github.com/hanwen/go-fuse/benchmark/benchmark.go index adf5296..e397cc7 100644 --- a/vendor/github.com/hanwen/go-fuse/benchmark/benchmark.go +++ b/vendor/github.com/hanwen/go-fuse/benchmark/benchmark.go @@ -1,16 +1,15 @@ +// 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 benchmark // Routines for benchmarking fuse. import ( "bufio" - "fmt" "log" - "math" "os" - "regexp" - "sort" - "time" ) func ReadLines(name string) []string { @@ -34,103 +33,6 @@ func ReadLines(name string) []string { if len(l) == 0 { log.Fatal("no files added") } - log.Printf("Read %d file names", len(l)) return l } - -// Used for benchmarking. Returns milliseconds. -func BulkStat(parallelism int, files []string) float64 { - todo := make(chan string, len(files)) - dts := make(chan time.Duration, parallelism) - for i := 0; i < parallelism; i++ { - go func() { - t := time.Now() - for { - fn := <-todo - if fn == "" { - break - } - - _, err := os.Lstat(fn) - if err != nil { - log.Fatal("All stats should succeed:", err) - } - } - dts <- time.Now().Sub(t) - }() - } - - for _, v := range files { - todo <- v - } - close(todo) - - total := 0.0 - for i := 0; i < parallelism; i++ { - total += float64(<-dts) / float64(time.Millisecond) - } - - avg := total / float64(len(files)) - - return avg -} - -func AnalyzeBenchmarkRuns(label string, times []float64) { - sorted := times - sort.Float64s(sorted) - - tot := 0.0 - for _, v := range times { - tot += v - } - n := float64(len(times)) - - avg := tot / n - variance := 0.0 - for _, v := range times { - variance += (v - avg) * (v - avg) - } - variance /= n - - stddev := math.Sqrt(variance) - - median := sorted[len(times)/2] - perc90 := sorted[int(n*0.9)] - perc10 := sorted[int(n*0.1)] - - fmt.Printf( - "%s: %d samples\n"+ - "avg %.3fms +/- %.0f%% "+ - "median %.3fms, 10%%tiles: [-%.0f%%, +%.0f%%]\n", - label, - len(times), avg, 100.0*2*stddev/avg, - median, 100*(median-perc10)/median, 100*(perc90-median)/median) -} - -func RunBulkStat(runs int, threads int, sleepTime time.Duration, files []string) (results []float64) { - for j := 0; j < runs; j++ { - result := BulkStat(threads, files) - results = append(results, result) - - if j < runs-1 { - fmt.Printf("Sleeping %d seconds\n", sleepTime) - time.Sleep(sleepTime) - } - } - return results -} - -func CountCpus() int { - var contents [10240]byte - - f, err := os.Open("/proc/stat") - defer f.Close() - if err != nil { - return 1 - } - n, _ := f.Read(contents[:]) - re, _ := regexp.Compile("\ncpu[0-9]") - - return len(re.FindAllString(string(contents[:n]), 100)) -} diff --git a/vendor/github.com/hanwen/go-fuse/benchmark/bulkstat/main.go b/vendor/github.com/hanwen/go-fuse/benchmark/bulkstat/main.go new file mode 100644 index 0000000..06b5e88 --- /dev/null +++ b/vendor/github.com/hanwen/go-fuse/benchmark/bulkstat/main.go @@ -0,0 +1,91 @@ +// 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 main + +import ( + "bufio" + "flag" + "log" + "os" + "path/filepath" + "sync" +) + +func BulkStat(parallelism int, files []string) { + todo := make(chan string, len(files)) + var wg sync.WaitGroup + wg.Add(parallelism) + for i := 0; i < parallelism; i++ { + go func() { + for { + fn := <-todo + if fn == "" { + break + } + _, err := os.Lstat(fn) + if err != nil { + log.Fatal("All stats should succeed:", err) + } + } + wg.Done() + }() + } + + for _, v := range files { + todo <- v + } + close(todo) + wg.Wait() +} + +func ReadLines(name string) []string { + f, err := os.Open(name) + if err != nil { + log.Fatal("ReadLines: ", err) + } + defer f.Close() + r := bufio.NewReader(f) + + l := []string{} + for { + line, _, err := r.ReadLine() + if line == nil || err != nil { + break + } + + fn := string(line) + l = append(l, fn) + } + if len(l) == 0 { + log.Fatal("no files added") + } + + return l +} + +func main() { + N := flag.Int("N", 1000, "how many files to stat") + cpu := flag.Int("cpu", 1, "how many threads to use") + prefix := flag.String("prefix", "", "mount point") + quiet := flag.Bool("quiet", false, "be quiet") + flag.Parse() + + f := flag.Arg(0) + files := ReadLines(f) + for i, f := range files { + files[i] = filepath.Join(*prefix, f) + } + if !*quiet { + log.Printf("statting %d with %d threads; first file %s (%d names)", *N, *cpu, files[0], len(files)) + } + todo := *N + for todo > 0 { + if len(files) > todo { + files = files[:todo] + } + BulkStat(*cpu, files) + todo -= len(files) + } +} diff --git a/vendor/github.com/hanwen/go-fuse/benchmark/latencymap.go b/vendor/github.com/hanwen/go-fuse/benchmark/latencymap.go index 8e04d1f..e411d93 100644 --- a/vendor/github.com/hanwen/go-fuse/benchmark/latencymap.go +++ b/vendor/github.com/hanwen/go-fuse/benchmark/latencymap.go @@ -1,3 +1,7 @@ +// 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 benchmark import ( @@ -25,6 +29,10 @@ func (m *LatencyMap) Get(name string) (count int, dt time.Duration) { m.Mutex.Lock() l := m.stats[name] m.Mutex.Unlock() + if l == nil { + return 0, 0 + } + return l.count, l.dur } diff --git a/vendor/github.com/hanwen/go-fuse/benchmark/latencymap_test.go b/vendor/github.com/hanwen/go-fuse/benchmark/latencymap_test.go index bde6016..1f77d65 100644 --- a/vendor/github.com/hanwen/go-fuse/benchmark/latencymap_test.go +++ b/vendor/github.com/hanwen/go-fuse/benchmark/latencymap_test.go @@ -1,3 +1,7 @@ +// 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 benchmark import ( diff --git a/vendor/github.com/hanwen/go-fuse/benchmark/stat_test.go b/vendor/github.com/hanwen/go-fuse/benchmark/stat_test.go index a03d096..4cd9510 100644 --- a/vendor/github.com/hanwen/go-fuse/benchmark/stat_test.go +++ b/vendor/github.com/hanwen/go-fuse/benchmark/stat_test.go @@ -1,3 +1,7 @@ +// 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 benchmark import ( @@ -14,32 +18,42 @@ import ( "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) -func setupFs(fs pathfs.FileSystem) (string, func()) { +func setupFs(fs pathfs.FileSystem, N int) (string, func()) { opts := &nodefs.Options{ EntryTimeout: 0.0, AttrTimeout: 0.0, NegativeTimeout: 0.0, } - mountPoint, _ := ioutil.TempDir("", "stat_test") + 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() - - state.RecordLatencies(lmap) - // state.SetDebug(true) + if testutil.VerboseTest() { + state.RecordLatencies(lmap) + } go state.Serve() return mountPoint, func() { - lc, lns := lmap.Get("LOOKUP") - gc, gns := lmap.Get("GETATTR") - fmt.Printf("GETATTR %v/call n=%d, LOOKUP %v/call n=%d\n", - gns/time.Duration(gc), gc, - lns/time.Duration(lc), lc) + 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 { @@ -51,14 +65,14 @@ func setupFs(fs pathfs.FileSystem) (string, func()) { } func TestNewStatFs(t *testing.T) { - fs := NewStatFs() + fs := NewStatFS() for _, n := range []string{ "file.txt", "sub/dir/foo.txt", "sub/dir/bar.txt", "sub/marine.txt"} { fs.AddFile(n) } - wd, clean := setupFs(fs) + wd, clean := setupFs(fs, 1) defer clean() names, err := ioutil.ReadDir(wd) @@ -100,18 +114,18 @@ func TestNewStatFs(t *testing.T) { } } -func BenchmarkGoFuseThreadedStat(b *testing.B) { +func BenchmarkGoFuseStat(b *testing.B) { b.StopTimer() - fs := NewStatFs() - fs.delay = delay + fs := NewStatFS() wd, _ := os.Getwd() - files := ReadLines(wd + "/testpaths.txt") + fileList := wd + "/testpaths.txt" + files := ReadLines(fileList) for _, fn := range files { fs.AddFile(fn) } - wd, clean := setupFs(fs) + wd, clean := setupFs(fs, b.N) defer clean() for i, l := range files { @@ -119,31 +133,88 @@ func BenchmarkGoFuseThreadedStat(b *testing.B) { } threads := runtime.GOMAXPROCS(0) - results := TestingBOnePass(b, threads, files) - AnalyzeBenchmarkRuns(fmt.Sprintf("Go-FUSE %d CPUs", threads), results) + if err := TestingBOnePass(b, threads, fileList, wd); err != nil { + log.Fatalf("TestingBOnePass %v8", err) + } } -func TestingBOnePass(b *testing.B, threads int, files []string) (results []float64) { - runtime.GC() - var before, after runtime.MemStats - runtime.ReadMemStats(&before) +func readdir(d string) error { + f, err := os.Open(d) + if err != nil { + return err + } + if _, err := f.Readdirnames(-1); err != nil { + return err + } + return f.Close() +} + +func BenchmarkGoFuseReaddir(b *testing.B) { + b.StopTimer() + fs := NewStatFS() + + wd, _ := os.Getwd() + dirSet := map[string]struct{}{} + + for _, fn := range ReadLines(wd + "/testpaths.txt") { + fs.AddFile(fn) + dirSet[filepath.Dir(fn)] = struct{}{} + } + wd, clean := setupFs(fs, b.N) + defer clean() + + var dirs []string + for dir := range dirSet { + dirs = append(dirs, filepath.Join(wd, dir)) + } + b.StartTimer() todo := b.N for todo > 0 { - if len(files) > todo { - files = files[:todo] + if len(dirs) > todo { + dirs = dirs[:todo] } - b.StartTimer() - result := BulkStat(threads, files) - todo -= len(files) - b.StopTimer() - results = append(results, result) + for _, d := range dirs { + if err := readdir(d); err != nil { + b.Fatal(err) + } + } + todo -= len(dirs) } + b.StopTimer() +} + +func TestingBOnePass(b *testing.B, threads int, filelist, mountPoint string) error { + runtime.GC() + var before, after runtime.MemStats + runtime.ReadMemStats(&before) + + // We shell out to an external program, so the time spent by + // the part stat'ing doesn't interfere with the time spent by + // the FUSE server. + cmd := exec.Command("./bulkstat.bin", + fmt.Sprintf("-cpu=%d", threads), + fmt.Sprintf("-prefix=%s", mountPoint), + fmt.Sprintf("-N=%d", b.N), + fmt.Sprintf("-quiet=%v", !testutil.VerboseTest()), + filelist) + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + b.StartTimer() + err := cmd.Run() + b.StopTimer() runtime.ReadMemStats(&after) + if err != nil { + return err + } - fmt.Printf("GC count %d, total GC time: %d ns/file\n", - after.NumGC-before.NumGC, (after.PauseTotalNs-before.PauseTotalNs)/uint64(b.N)) - return results + if testutil.VerboseTest() { + fmt.Printf("GC count %d, total GC time: %d ns/file\n", + after.NumGC-before.NumGC, (after.PauseTotalNs-before.PauseTotalNs)/uint64(b.N)) + } + return nil } // Add this so we can estimate impact on latency numbers. @@ -157,7 +228,8 @@ func BenchmarkCFuseThreadedStat(b *testing.B) { b.StopTimer() wd, _ := os.Getwd() - lines := ReadLines(wd + "/testpaths.txt") + fileList := wd + "/testpaths.txt" + lines := ReadLines(fileList) unique := map[string]int{} for _, l := range lines { unique[l] = 1 @@ -184,14 +256,13 @@ func BenchmarkCFuseThreadedStat(b *testing.B) { } f.Close() - mountPoint, _ := ioutil.TempDir("", "stat_test") + 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()), - fmt.Sprintf("STATFS_DELAY_USEC=%d", delay/time.Microsecond)) + fmt.Sprintf("STATFS_INPUT=%s", f.Name())) cmd.Start() bin, err := exec.LookPath("fusermount") @@ -204,13 +275,10 @@ func BenchmarkCFuseThreadedStat(b *testing.B) { } defer stop.Run() - for i, l := range lines { - lines[i] = filepath.Join(mountPoint, l) - } - time.Sleep(100 * time.Millisecond) os.Lstat(mountPoint) threads := runtime.GOMAXPROCS(0) - results := TestingBOnePass(b, threads, lines) - AnalyzeBenchmarkRuns(fmt.Sprintf("CFuse on %d CPUS", threads), results) + if err := TestingBOnePass(b, threads, fileList, mountPoint); err != nil { + log.Fatalf("TestingBOnePass %v", err) + } } diff --git a/vendor/github.com/hanwen/go-fuse/benchmark/statfs.go b/vendor/github.com/hanwen/go-fuse/benchmark/statfs.go index e03db3f..9b5ab34 100644 --- a/vendor/github.com/hanwen/go-fuse/benchmark/statfs.go +++ b/vendor/github.com/hanwen/go-fuse/benchmark/statfs.go @@ -1,67 +1,67 @@ +// 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 benchmark import ( "path/filepath" "strings" - "time" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/pathfs" ) -var delay = 0 * time.Microsecond - -type StatFs struct { +type StatFS struct { pathfs.FileSystem entries map[string]*fuse.Attr dirs map[string][]fuse.DirEntry - delay time.Duration } -func (me *StatFs) Add(name string, a *fuse.Attr) { +func (fs *StatFS) Add(name string, a *fuse.Attr) { name = strings.TrimRight(name, "/") - _, ok := me.entries[name] + _, ok := fs.entries[name] if ok { return } - me.entries[name] = a + fs.entries[name] = a if name == "/" || name == "" { return } dir, base := filepath.Split(name) dir = strings.TrimRight(dir, "/") - me.dirs[dir] = append(me.dirs[dir], fuse.DirEntry{Name: base, Mode: a.Mode}) - me.Add(dir, &fuse.Attr{Mode: fuse.S_IFDIR | 0755}) + fs.dirs[dir] = append(fs.dirs[dir], fuse.DirEntry{Name: base, Mode: a.Mode}) + fs.Add(dir, &fuse.Attr{Mode: fuse.S_IFDIR | 0755}) } -func (me *StatFs) AddFile(name string) { - me.Add(name, &fuse.Attr{Mode: fuse.S_IFREG | 0644}) +func (fs *StatFS) AddFile(name string) { + fs.Add(name, &fuse.Attr{Mode: fuse.S_IFREG | 0644}) } -func (me *StatFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { - e := me.entries[name] +func (fs *StatFS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { + if d := fs.dirs[name]; d != nil { + return &fuse.Attr{Mode: 0755 | fuse.S_IFDIR}, fuse.OK + } + e := fs.entries[name] if e == nil { return nil, fuse.ENOENT } - if me.delay > 0 { - time.Sleep(me.delay) - } return e, fuse.OK } -func (me *StatFs) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) { - entries := me.dirs[name] +func (fs *StatFS) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) { + entries := fs.dirs[name] if entries == nil { return nil, fuse.ENOENT } return entries, fuse.OK } -func NewStatFs() *StatFs { - return &StatFs{ +func NewStatFS() *StatFS { + return &StatFS{ FileSystem: pathfs.NewDefaultFileSystem(), entries: make(map[string]*fuse.Attr), dirs: make(map[string][]fuse.DirEntry), diff --git a/vendor/github.com/hanwen/go-fuse/example/autounionfs/main.go b/vendor/github.com/hanwen/go-fuse/example/autounionfs/main.go index e6a460f..77312a1 100644 --- a/vendor/github.com/hanwen/go-fuse/example/autounionfs/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/autounionfs/main.go @@ -1,3 +1,7 @@ +// 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 main import ( @@ -43,6 +47,7 @@ func main() { AttrTimeout: time.Second, NegativeTimeout: time.Second, Owner: fuse.CurrentOwner(), + Debug: *debug, }, UpdateOnMount: true, PathNodeFsOptions: pathfs.PathNodeFsOptions{ @@ -52,19 +57,18 @@ func main() { } fsOpts := nodefs.Options{ PortableInodes: *portableInodes, + Debug: *debug, } gofs := unionfs.NewAutoUnionFs(flag.Arg(1), options) - pathfs := pathfs.NewPathNodeFs(gofs, nil) - state, conn, err := nodefs.MountRoot(flag.Arg(0), pathfs.Root(), &fsOpts) + pathfs := pathfs.NewPathNodeFs(gofs, &pathfs.PathNodeFsOptions{ + Debug: *debug, + }) + state, _, err := nodefs.MountRoot(flag.Arg(0), pathfs.Root(), &fsOpts) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } - pathfs.SetDebug(*debug) - conn.SetDebug(*debug) - state.SetDebug(*debug) - state.Serve() time.Sleep(1 * time.Second) } diff --git a/vendor/github.com/hanwen/go-fuse/example/hello/main.go b/vendor/github.com/hanwen/go-fuse/example/hello/main.go index 6a21f3e..794f6e2 100644 --- a/vendor/github.com/hanwen/go-fuse/example/hello/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/hello/main.go @@ -1,3 +1,7 @@ +// 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. + // A Go mirror of libfuse's hello.c package main diff --git a/vendor/github.com/hanwen/go-fuse/example/loopback/main.go b/vendor/github.com/hanwen/go-fuse/example/loopback/main.go index db8b714..df52221 100644 --- a/vendor/github.com/hanwen/go-fuse/example/loopback/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/loopback/main.go @@ -1,3 +1,7 @@ +// 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. + // Mounts another directory as loopback for testing and benchmarking // purposes. @@ -8,9 +12,11 @@ import ( "fmt" "log" "os" + "os/signal" "path" "path/filepath" "runtime/pprof" + "syscall" "time" "github.com/hanwen/go-fuse/fuse" @@ -18,6 +24,25 @@ import ( "github.com/hanwen/go-fuse/fuse/pathfs" ) +func writeMemProfile(fn string, sigs <-chan os.Signal) { + i := 0 + for range sigs { + fn := fmt.Sprintf("%s-%d.memprof", fn, i) + i++ + + log.Printf("Writing mem profile to %s\n", fn) + f, err := os.Create(fn) + if err != nil { + log.Printf("Create: %v", err) + continue + } + pprof.WriteHeapProfile(f) + if err := f.Close(); err != nil { + log.Printf("close %v", err) + } + } +} + func main() { log.SetFlags(log.Lmicroseconds) // Scans the arg list and sets up flags @@ -44,17 +69,10 @@ func main() { defer pprof.StopCPUProfile() } if *memprofile != "" { - fmt.Printf("Writing mem profile to %s\n", *memprofile) - f, err := os.Create(*memprofile) - if err != nil { - fmt.Println(err) - os.Exit(4) - } - defer func() { - pprof.WriteHeapProfile(f) - f.Close() - return - }() + 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") @@ -82,13 +100,13 @@ func main() { 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) } - state.SetDebug(*debug) fmt.Println("Mounted!") state.Serve() diff --git a/vendor/github.com/hanwen/go-fuse/example/memfs/main.go b/vendor/github.com/hanwen/go-fuse/example/memfs/main.go index d123918..2a67eab 100644 --- a/vendor/github.com/hanwen/go-fuse/example/memfs/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/memfs/main.go @@ -1,3 +1,7 @@ +// 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. + // Mounts MemNodeFs for testing purposes. package main @@ -25,12 +29,13 @@ func main() { prefix := flag.Arg(1) root := nodefs.NewMemNodeFSRoot(prefix) conn := nodefs.NewFileSystemConnector(root, nil) - server, err := fuse.NewServer(conn.RawFS(), mountPoint, nil) + server, err := fuse.NewServer(conn.RawFS(), mountPoint, &fuse.MountOptions{ + Debug: *debug, + }) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } - server.SetDebug(*debug) fmt.Println("Mounted!") server.Serve() } diff --git a/vendor/github.com/hanwen/go-fuse/example/multizip/main.go b/vendor/github.com/hanwen/go-fuse/example/multizip/main.go index 73fa829..0a0ae5e 100644 --- a/vendor/github.com/hanwen/go-fuse/example/multizip/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/multizip/main.go @@ -1,3 +1,7 @@ +// 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 main import ( @@ -23,12 +27,13 @@ func main() { fs := zipfs.NewMultiZipFs() nfs := pathfs.NewPathNodeFs(fs, nil) - state, _, err := nodefs.MountRoot(flag.Arg(0), nfs.Root(), nil) + opts := nodefs.NewOptions() + opts.Debug = *debug + state, _, err := nodefs.MountRoot(flag.Arg(0), nfs.Root(), opts) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } - state.SetDebug(*debug) state.Serve() } diff --git a/vendor/github.com/hanwen/go-fuse/example/statfs/main.go b/vendor/github.com/hanwen/go-fuse/example/statfs/main.go index edebc19..f5ac0d4 100644 --- a/vendor/github.com/hanwen/go-fuse/example/statfs/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/statfs/main.go @@ -1,3 +1,7 @@ +// 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 main import ( @@ -53,6 +57,7 @@ func main() { opts := &nodefs.Options{ AttrTimeout: time.Duration(*ttl * float64(time.Second)), EntryTimeout: time.Duration(*ttl * float64(time.Second)), + Debug: *debug, } state, _, err := nodefs.MountRoot(flag.Arg(0), nfs.Root(), opts) if err != nil { @@ -60,7 +65,6 @@ func main() { os.Exit(1) } - state.SetDebug(*debug) runtime.GC() if profFile != nil { pprof.StartCPUProfile(profFile) diff --git a/vendor/github.com/hanwen/go-fuse/example/unionfs/main.go b/vendor/github.com/hanwen/go-fuse/example/unionfs/main.go index b2b371c..d2be636 100644 --- a/vendor/github.com/hanwen/go-fuse/example/unionfs/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/unionfs/main.go @@ -1,3 +1,7 @@ +// 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 main import ( @@ -47,12 +51,12 @@ func main() { AttrTimeout: time.Duration(*entry_ttl * float64(time.Second)), NegativeTimeout: time.Duration(*negative_ttl * float64(time.Second)), PortableInodes: *portable, + Debug: *debug, } mountState, _, err := nodefs.MountRoot(flag.Arg(0), nodeFs.Root(), &mOpts) if err != nil { log.Fatal("Mount fail:", err) } - mountState.SetDebug(*debug) mountState.Serve() } diff --git a/vendor/github.com/hanwen/go-fuse/example/zipfs/main.go b/vendor/github.com/hanwen/go-fuse/example/zipfs/main.go index ac02b2c..e1dbed3 100644 --- a/vendor/github.com/hanwen/go-fuse/example/zipfs/main.go +++ b/vendor/github.com/hanwen/go-fuse/example/zipfs/main.go @@ -1,3 +1,7 @@ +// 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 main import ( @@ -53,6 +57,7 @@ func main() { opts := &nodefs.Options{ AttrTimeout: time.Duration(*ttl * float64(time.Second)), EntryTimeout: time.Duration(*ttl * float64(time.Second)), + Debug: *debug, } state, _, err := nodefs.MountRoot(flag.Arg(0), root, opts) if err != nil { @@ -60,7 +65,6 @@ func main() { os.Exit(1) } - state.SetDebug(*debug) runtime.GC() if profFile != nil { pprof.StartCPUProfile(profFile) diff --git a/vendor/github.com/hanwen/go-fuse/fuse/api.go b/vendor/github.com/hanwen/go-fuse/fuse/api.go index ea85093..240852a 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/api.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/api.go @@ -1,3 +1,7 @@ +// 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. + // The fuse package provides APIs to implement filesystems in // userspace. Typically, each call of the API happens in its own // goroutine, so take care to make the file system thread-safe. @@ -61,6 +65,13 @@ type MountOptions struct { // If set, wrap the file system in a single-threaded locking wrapper. SingleThreaded bool + + // If set, return ENOSYS for Getxattr calls, so the kernel does not issue any + // Xattr operations at all. + DisableXAttrs bool + + // If set, print debugging information. + Debug bool } // RawFileSystem is an interface close to the FUSE wire protocol. diff --git a/vendor/github.com/hanwen/go-fuse/fuse/attr.go b/vendor/github.com/hanwen/go-fuse/fuse/attr.go index 7211acc..99d1715 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/attr.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/attr.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/attr_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/attr_darwin.go index af3ef87..40cb072 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/attr_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/attr_darwin.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/attr_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/attr_linux.go index 3782369..223676a 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/attr_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/attr_linux.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/bufferpool.go b/vendor/github.com/hanwen/go-fuse/fuse/bufferpool.go index 7ed06c1..f517f07 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/bufferpool.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/bufferpool.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/bufferpool_test.go b/vendor/github.com/hanwen/go-fuse/fuse/bufferpool_test.go index 02106f7..9c94639 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/bufferpool_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/bufferpool_test.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/constants.go b/vendor/github.com/hanwen/go-fuse/fuse/constants.go index 5aeaf1f..9275e9f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/constants.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/constants.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/constants_freebsd.go b/vendor/github.com/hanwen/go-fuse/fuse/constants_freebsd.go index ae8233a..5fb6069 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/constants_freebsd.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/constants_freebsd.go @@ -1,3 +1,7 @@ +// 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 fuse // arbitrary values diff --git a/vendor/github.com/hanwen/go-fuse/fuse/constants_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/constants_linux.go index 6c51676..29455e4 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/constants_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/constants_linux.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/defaultraw.go b/vendor/github.com/hanwen/go-fuse/fuse/defaultraw.go index 8fc71cf..2be6e7e 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/defaultraw.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/defaultraw.go @@ -1,3 +1,7 @@ +// 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 fuse import ( @@ -82,7 +86,7 @@ func (fs *defaultRawFileSystem) GetXAttrSize(header *InHeader, attr string) (siz } func (fs *defaultRawFileSystem) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) { - return nil, ENOSYS + return nil, ENOATTR } func (fs *defaultRawFileSystem) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status { diff --git a/vendor/github.com/hanwen/go-fuse/fuse/direntry.go b/vendor/github.com/hanwen/go-fuse/fuse/direntry.go index 34fbacf..8136d4b 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/direntry.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/direntry.go @@ -1,3 +1,7 @@ +// 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 fuse // all of the code for DirEntryList. diff --git a/vendor/github.com/hanwen/go-fuse/fuse/lockingfs.go b/vendor/github.com/hanwen/go-fuse/fuse/lockingfs.go index 2c3bd8d..4ec665d 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/lockingfs.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/lockingfs.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/misc.go b/vendor/github.com/hanwen/go-fuse/fuse/misc.go index 4e2879b..44ad46a 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/misc.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/misc.go @@ -1,3 +1,7 @@ +// 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. + // Random odds and ends. package fuse diff --git a/vendor/github.com/hanwen/go-fuse/fuse/misc_test.go b/vendor/github.com/hanwen/go-fuse/fuse/misc_test.go index a6cd6df..5cffbbc 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/misc_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/misc_test.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/mount_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/mount_darwin.go index ceee372..13ea0e1 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/mount_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/mount_darwin.go @@ -1,145 +1,76 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// 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. -// TODO: Rewrite using package syscall not cgo - package fuse -/* - -// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c, -// which carries this notice: -// -// The files in this directory are subject to the following license. -// -// The author of this software is Russ Cox. -// -// Copyright (c) 2006 Russ Cox -// -// Permission to use, copy, modify, and distribute this software for any -// purpose without fee is hereby granted, provided that this entire notice -// is included in all copies of any software which is or includes a copy -// or modification of this software and in all copies of the supporting -// documentation for such software. -// -// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED -// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY -// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS -// FITNESS FOR ANY PARTICULAR PURPOSE. - -#include -#include -#include -#include -#include -#include -#include -#include - -#define nil ((void*)0) - -static int -mountfuse(char *mtpt, char **err) -{ - int i, pid, fd, r; - char buf[200]; - struct vfsconf vfs; - char *f; +import ( + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" +) - if(getvfsbyname("osxfusefs", &vfs) < 0){ - if(access(f="/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", 0) < 0){ - *err = strdup("cannot find load_fusefs"); - return -1; - } - if((r=system(f)) < 0){ - snprintf(buf, sizeof buf, "%s: %s", f, strerror(errno)); - *err = strdup(buf); - return -1; - } - if(r != 0){ - snprintf(buf, sizeof buf, "load_fusefs failed: exit %d", r); - *err = strdup(buf); - return -1; - } - if(getvfsbyname("osxfusefs", &vfs) < 0){ - snprintf(buf, sizeof buf, "getvfsbyname osxfusefs: %s", strerror(errno)); - *err = strdup(buf); - return -1; - } +func openFUSEDevice() (*os.File, error) { + fs, err := filepath.Glob("/dev/osxfuse*") + if err != nil { + return nil, err } - - // Look for available FUSE device. - for(i=0;; i++){ - snprintf(buf, sizeof buf, "/dev/osxfuse%d", i); - if(access(buf, 0) < 0){ - *err = strdup("no available fuse devices"); - return -1; + if len(fs) == 0 { + // TODO(hanwen): run the load_osxfuse command. + return nil, fmt.Errorf("no FUSE devices found") + } + for _, fn := range fs { + f, err := os.OpenFile(fn, os.O_RDWR, 0) + if err != nil { + continue } - if((fd = open(buf, O_RDWR)) >= 0) - break; + return f, nil } - pid = fork(); - if(pid < 0) - return -1; - if(pid == 0){ - snprintf(buf, sizeof buf, "%d", fd); - setenv("MOUNT_FUSEFS_CALL_BY_LIB", "", 1); - // Different versions of MacFUSE put the - // mount_fusefs binary in different places. - // Try all. - // Leopard location - setenv("MOUNT_FUSEFS_DAEMON_PATH", "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", 1); - execl("/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", "mount_osxfusefs", "-o", "iosize=4096", buf, mtpt, nil); - fprintf(stderr, "exec mount_osxfusefs: %s\n", strerror(errno)); - _exit(1); - } - return fd; + return nil, fmt.Errorf("all FUSE devices busy") } -*/ -import "C" -import ( - "fmt" - "log" - "os/exec" - "syscall" - "unsafe" -) +const bin = "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs" -func mount(dir string, options string) (int, error) { - errp := (**C.char)(C.malloc(16)) - *errp = nil - defer C.free(unsafe.Pointer(errp)) - cdir := C.CString(dir) - defer C.free(unsafe.Pointer(cdir)) - fd := C.mountfuse(cdir, errp) - if *errp != nil { - return -1, mountError(C.GoString(*errp)) +func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) { + f, err := openFUSEDevice() + if err != nil { + return 0, err } - return int(fd), nil -} -type mountError string + cmd := exec.Command(bin, "-o", strings.Join(opts.optionsStrings(), ","), "-o", fmt.Sprintf("iosize=%d", opts.MaxWrite), "3", mountPoint) + cmd.ExtraFiles = []*os.File{f} + cmd.Env = append(os.Environ(), "MOUNT_FUSEFS_CALL_BY_LIB=", "MOUNT_OSXFUSE_CALL_BY_LIB=", + "MOUNT_OSXFUSE_DAEMON_PATH="+os.Args[0], + "MOUNT_FUSEFS_DAEMON_PATH="+os.Args[0]) -func (m mountError) Error() string { - return string(m) -} + var out, errOut bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &errOut -func unmount(mountPoint string) error { - if err := syscall.Unmount(mountPoint, 0); err != nil { - return fmt.Errorf("umount(%q): %v", mountPoint, err) + if err := cmd.Start(); err != nil { + f.Close() + return 0, err } - return nil -} + go func() { + err := cmd.Wait() + if err != nil { + err = fmt.Errorf("mount_osxfusefs failed: %v. Stderr: %s, Stdout: %s", err, errOut.String(), out.String()) + } -var umountBinary string + ready <- err + close(ready) + }() -func init() { - var err error - umountBinary, err = exec.LookPath("umount") - if err != nil { - log.Fatalf("Could not find umount binary: %v", err) - } + // The finalizer for f will close its fd so we return a dup. + defer f.Close() + return syscall.Dup(int(f.Fd())) +} + +func unmount(dir string) error { + return syscall.Unmount(dir, 0) } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/mount_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/mount_linux.go index a33bb38..f90c5bb 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/mount_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/mount_linux.go @@ -1,3 +1,7 @@ +// 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 fuse import ( @@ -7,6 +11,7 @@ import ( "os/exec" "path" "path/filepath" + "strings" "syscall" "unsafe" ) @@ -24,7 +29,7 @@ func unixgramSocketpair() (l, r *os.File, err error) { // Create a FUSE FS on the specified mount point. The returned // mount point is always absolute. -func mount(mountPoint string, options string) (fd int, err error) { +func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) { local, remote, err := unixgramSocketpair() if err != nil { return @@ -39,9 +44,8 @@ func mount(mountPoint string, options string) (fd int, err error) { } cmd := []string{bin, mountPoint} - if options != "" { - cmd = append(cmd, "-o") - cmd = append(cmd, options) + if s := opts.optionsStrings(); len(s) > 0 { + cmd = append(cmd, "-o", strings.Join(s, ",")) } proc, err := os.StartProcess(bin, cmd, @@ -62,7 +66,13 @@ func mount(mountPoint string, options string) (fd int, err error) { return } - return getConnection(local) + fd, err = getConnection(local) + if err != nil { + return -1, err + } + + close(ready) + return fd, err } func privilegedUnmount(mountPoint string) error { diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/api.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/api.go index a6b0248..0a109ef 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/api.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/api.go @@ -1,3 +1,7 @@ +// 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. + // The nodefs package offers a high level API that resembles the // kernel's idea of what an FS looks like. File systems can have // multiple hard-links to one file, for example. It is also suited if @@ -37,14 +41,20 @@ type Node interface { // for directory Nodes. Lookup(out *fuse.Attr, name string, context *fuse.Context) (*Inode, fuse.Status) - // Deletable() should return true if this inode may be - // discarded from the children list. This will be called from - // within the treeLock critical section, so you cannot look at - // other inodes. + // Deletable() should return true if this node may be discarded once + // the kernel forgets its reference. + // If it returns false, OnForget will never get called for this node. This + // is appropriate if the filesystem has no persistent backing store + // (in-memory filesystems) where discarding the node loses the stored data. + // Deletable will be called from within the treeLock critical section, so you + // cannot look at other nodes. Deletable() bool - // OnForget is called when the reference to this inode is - // dropped from the tree. + // OnForget is called when the kernel forgets its reference to this node and + // sends a FORGET request. It should perform cleanup and free memory as + // appropriate for the filesystem. + // OnForget is not called if the node is a directory and has children. + // This is called from within a treeLock critical section. OnForget() // Misc. @@ -166,10 +176,13 @@ type Options struct { NegativeTimeout time.Duration // If set, replace all uids with given UID. - // NewFileSystemOptions() will set this to the daemon's + // NewOptions() will set this to the daemon's // uid/gid. *fuse.Owner // This option exists for compatibility and is ignored. PortableInodes bool + + // If set, print debug information. + Debug bool } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultfile.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultfile.go index c3b0b12..6002302 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultfile.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultfile.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultnode.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultnode.go index f4474dd..b3854eb 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultnode.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultnode.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( @@ -106,7 +110,7 @@ func (n *defaultNode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Stat } func (n *defaultNode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) { - return nil, fuse.ENOSYS + return nil, fuse.ENOATTR } func (n *defaultNode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status { diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/dir.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/dir.go index 2f12fb1..a1c2803 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/dir.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/dir.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fileless_test.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fileless_test.go index 5dc0457..37005e2 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fileless_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fileless_test.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( @@ -9,14 +13,16 @@ import ( type nodeReadNode struct { Node + dir bool noOpen bool data []byte } -func newNodeReadNode(noOpen bool, d []byte) *nodeReadNode { +func newNodeReadNode(noOpen, dir bool, d []byte) *nodeReadNode { return &nodeReadNode{ Node: NewDefaultNode(), noOpen: noOpen, + dir: dir, data: d, } } @@ -36,11 +42,19 @@ func (n *nodeReadNode) Read(file File, dest []byte, off int64, context *fuse.Con return fuse.ReadResultData(n.data[off:int(e)]), fuse.OK } +func (n *nodeReadNode) GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status) { + if n.dir { + out.Mode = fuse.S_IFDIR | 0755 + } else { + out.Mode = fuse.S_IFREG | 0644 + } + out.Size = uint64(len(n.data)) + return fuse.OK +} + func (n *nodeReadNode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (*Inode, fuse.Status) { - out.Mode = fuse.S_IFREG | 0644 - out.Size = uint64(len(name)) - ch := n.Inode().NewChild(name, false, newNodeReadNode(n.noOpen, []byte(name))) - return ch, fuse.OK + ch := n.Inode().NewChild(name, false, newNodeReadNode(n.noOpen, false, []byte(name))) + return ch, ch.Node().GetAttr(out, nil, context) } func TestNoOpen(t *testing.T) { @@ -49,16 +63,18 @@ func TestNoOpen(t *testing.T) { t.Fatalf("TempDir: %v", err) } - root := newNodeReadNode(true, nil) + root := newNodeReadNode(true, true, nil) root.noOpen = true - s, _, err := MountRoot(dir, root, nil) + s, _, err := MountRoot(dir, root, &Options{Debug: VerboseTest()}) if err != nil { t.Fatalf("MountRoot: %v", err) } - s.SetDebug(VerboseTest()) defer s.Unmount() go s.Serve() + if err := s.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } if s.KernelSettings().Minor < 23 { t.Skip("Kernel does not support open-less read/writes. Skipping test.") @@ -90,13 +106,18 @@ func TestNodeRead(t *testing.T) { t.Fatalf("TempDir: %v", err) } - root := newNodeReadNode(false, nil) - s, _, err := MountRoot(dir, root, nil) + root := newNodeReadNode(false, true, nil) + opts := NewOptions() + opts.Debug = VerboseTest() + s, _, err := MountRoot(dir, root, opts) if err != nil { t.Fatalf("MountRoot: %v", err) } defer s.Unmount() go s.Serve() + if err := s.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } content, err := ioutil.ReadFile(dir + "/file") if err != nil { t.Fatalf("ReadFile: %v", err) @@ -105,5 +126,4 @@ func TestNodeRead(t *testing.T) { if string(content) != want { t.Fatalf("got %q, want %q", content, want) } - } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files.go index 004406b..8e71339 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_darwin.go index 01ab0af..02e1495 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_darwin.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_linux.go index ac4f7c5..a4bf282 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_linux.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go index 3dddf01..e5c31a4 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go @@ -1,3 +1,7 @@ +// 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 nodefs // This file contains the internal logic of the @@ -60,6 +64,7 @@ func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) { // FUSE does not issue a LOOKUP for 1 (obviously), but it does // issue a forget. This lookupUpdate is to make the counts match. c.lookupUpdate(c.rootNode) + c.debug = opts.Debug return c } @@ -69,7 +74,8 @@ func (c *FileSystemConnector) Server() *fuse.Server { return c.server } -// SetDebug toggles printing of debug information. +// SetDebug toggles printing of debug information. This function is +// deprecated. Set the Debug option in the Options struct instead. func (c *FileSystemConnector) SetDebug(debug bool) { c.debug = debug } @@ -125,11 +131,33 @@ func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) { return } - if forgotten, handled := c.inodeMap.Forget(nodeID, forgetCount); forgotten { - node := (*Inode)(unsafe.Pointer(handled)) - node.mount.treeLock.Lock() - c.recursiveConsiderDropInode(node) - node.mount.treeLock.Unlock() + // Prevent concurrent modification of the tree while we are processing + // the FORGET + node := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeID))) + node.mount.treeLock.Lock() + defer node.mount.treeLock.Unlock() + + if forgotten, _ := c.inodeMap.Forget(nodeID, forgetCount); forgotten { + if len(node.children) > 0 || !node.Node().Deletable() || + node == c.rootNode || node.mountPoint != nil { + // We cannot forget a directory that still has children as these + // would become unreachable. + return + } + // We have to remove ourself from all parents. + // Create a copy of node.parents so we can safely iterate over it + // while modifying the original. + parents := make(map[parentData]struct{}, len(node.parents)) + for k, v := range node.parents { + parents[k] = v + } + + for p := range parents { + // This also modifies node.parents + p.parent.rmChild(p.name) + } + + node.fsInode.OnForget() } // TODO - try to drop children even forget was not successful. c.verify() @@ -140,39 +168,6 @@ func (c *FileSystemConnector) InodeHandleCount() int { return c.inodeMap.Count() } -// Must hold treeLock. - -func (c *FileSystemConnector) recursiveConsiderDropInode(n *Inode) (drop bool) { - delChildren := []string{} - for k, v := range n.children { - // Only consider children from the same mount, or - // already unmounted mountpoints. - if v.mountPoint == nil && c.recursiveConsiderDropInode(v) { - delChildren = append(delChildren, k) - } - } - for _, k := range delChildren { - ch := n.rmChild(k) - if ch == nil { - log.Panicf("trying to del child %q, but not present", k) - } - ch.fsInode.OnForget() - } - - if len(n.children) > 0 || !n.Node().Deletable() { - return false - } - if n == c.rootNode || n.mountPoint != nil { - return false - } - - n.openFilesMutex.Lock() - ok := len(n.openFiles) == 0 - n.openFilesMutex.Unlock() - - return ok -} - // Finds a node within the currently known inodes, returns the last // known node and the remaining unknown path components. If parent is // nil, start from FUSE mountpoint. @@ -241,7 +236,6 @@ func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode { func (c *FileSystemConnector) mountRoot(opts *Options) { c.rootNode.mountFs(opts) c.rootNode.mount.connector = c - c.rootNode.Node().OnMount(c) c.verify() } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsmount.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsmount.go index c79e175..fc830f6 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsmount.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsmount.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( @@ -27,8 +31,9 @@ type fileSystemMount struct { // Options for the mount. options *Options - // Protects Children hashmaps within the mount. treeLock - // should be acquired before openFilesLock. + // Protects the "children" and "parents" hashmaps of the inodes + // within the mount. + // treeLock should be acquired before openFilesLock. // // If multiple treeLocks must be acquired, the treeLocks // closer to the root must be acquired first. diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsops.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsops.go index 459b75c..e9c3cee 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsops.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsops.go @@ -1,3 +1,7 @@ +// 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 nodefs // This file contains FileSystemConnector's implementation of @@ -55,6 +59,7 @@ func (c *rawBridge) String() string { func (c *rawBridge) Init(s *fuse.Server) { c.server = s + c.rootNode.Node().OnMount((*FileSystemConnector)(c)) } func (c *FileSystemConnector) lookupMountUpdate(out *fuse.Attr, mount *fileSystemMount) (node *Inode, code fuse.Status) { @@ -139,6 +144,12 @@ func (c *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse return code } + if out.Nlink == 0 { + // With Nlink == 0, newer kernels will refuse link + // operations. + out.Nlink = 1 + } + node.mount.fillAttr(out, input.NodeId) return fuse.OK } @@ -201,7 +212,15 @@ func (c *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse code = node.fsInode.Chmod(f, permissions, &input.Context) } if code.Ok() && (input.Valid&(fuse.FATTR_UID|fuse.FATTR_GID) != 0) { - code = node.fsInode.Chown(f, uint32(input.Uid), uint32(input.Gid), &input.Context) + var uid uint32 = ^uint32(0) // means "do not change" in chown(2) + var gid uint32 = ^uint32(0) + if input.Valid&fuse.FATTR_UID != 0 { + uid = input.Uid + } + if input.Valid&fuse.FATTR_GID != 0 { + gid = input.Gid + } + code = node.fsInode.Chown(f, uid, gid, &input.Context) } if code.Ok() && input.Valid&fuse.FATTR_SIZE != 0 { code = node.fsInode.Truncate(f, input.Size, &input.Context) diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fuse.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fuse.go index 6845e8f..6a871c0 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fuse.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/fuse.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( @@ -7,7 +11,12 @@ import ( // Mounts a filesystem with the given root node on the given directory func MountRoot(mountpoint string, root Node, opts *Options) (*fuse.Server, *FileSystemConnector, error) { conn := NewFileSystemConnector(root, opts) - s, err := fuse.NewServer(conn.RawFS(), mountpoint, nil) + + mountOpts := fuse.MountOptions{} + if opts != nil && opts.Debug { + mountOpts.Debug = opts.Debug + } + s, err := fuse.NewServer(conn.RawFS(), mountpoint, &mountOpts) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle.go index 3f35c9e..8ceaa1c 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle_test.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle_test.go index b492973..d58402f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle_test.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/inode.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/inode.go index 3748253..2711725 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/inode.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/inode.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( @@ -7,6 +11,11 @@ import ( "github.com/hanwen/go-fuse/fuse" ) +type parentData struct { + parent *Inode + name string +} + // An Inode reflects the kernel's idea of the inode. Inodes have IDs // that are communicated to the kernel, and they have a tree // structure: a directory Inode may contain named children. Each @@ -32,6 +41,8 @@ type Inode struct { // All data below is protected by treeLock. children map[string]*Inode + // Due to hard links, an Inode can have many parents. + parents map[parentData]struct{} // Non-nil if this inode is a mountpoint, ie. the Root of a // NodeFileSystem. @@ -40,6 +51,7 @@ type Inode struct { func newInode(isDir bool, fsNode Node) *Inode { me := new(Inode) + me.parents = map[parentData]struct{}{} if isDir { me.children = make(map[string]*Inode, initDirSize) } @@ -75,6 +87,19 @@ func (n *Inode) Children() (out map[string]*Inode) { return out } +// Parent returns a random parent and the name this inode has under this parent. +// This function can be used to walk up the directory tree. It will not cross +// sub-mounts. +func (n *Inode) Parent() (parent *Inode, name string) { + if n.mountPoint != nil { + return nil, "" + } + for k := range n.parents { + return k.parent, k.name + } + return nil, "" +} + // FsChildren returns all the children from the same filesystem. It // will skip mountpoints. func (n *Inode) FsChildren() (out map[string]*Inode) { @@ -144,7 +169,7 @@ func (n *Inode) AddChild(name string, child *Inode) { // TreeWatcher is an additional interface that Nodes can implement. // If they do, the OnAdd and OnRemove are called for operations on the -// file system tree. The functions run under a lock, so they should +// file system tree. These functions run under a lock, so they should // not do blocking operations. type TreeWatcher interface { OnAdd(parent *Inode, name string) @@ -173,17 +198,20 @@ func (n *Inode) addChild(name string, child *Inode) { } } n.children[name] = child + child.parents[parentData{n, name}] = struct{}{} if w, ok := child.Node().(TreeWatcher); ok && child.mountPoint == nil { w.OnAdd(n, name) } } -// rmChild drops "name" from our children. +// rmChild throws out child "name". This means (1) deleting "name" from our +// "children" map and (2) deleting ourself from the child's "parents" map. // Must be called with treeLock for the mount held. func (n *Inode) rmChild(name string) *Inode { ch := n.children[name] if ch != nil { delete(n.children, name) + delete(ch.parents, parentData{n, name}) if w, ok := ch.Node().(TreeWatcher); ok && ch.mountPoint == nil { w.OnRemove(n, name) } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/lockingfile.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/lockingfile.go index 051188c..903486d 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/lockingfile.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/lockingfile.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode.go index 79f719f..e86794f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode_test.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode_test.go index a7edb38..54b5db3 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode_test.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( @@ -27,30 +31,30 @@ func setupMemNodeTest(t *testing.T) (wd string, root Node, clean func()) { EntryTimeout: testTtl, AttrTimeout: testTtl, NegativeTimeout: 0.0, + Debug: VerboseTest(), }) - connector.SetDebug(VerboseTest()) - state, err := fuse.NewServer(connector.RawFS(), mnt, nil) + state, err := fuse.NewServer(connector.RawFS(), mnt, &fuse.MountOptions{Debug: VerboseTest()}) if err != nil { t.Fatal("NewServer", err) } - //me.state.SetDebug(false) - state.SetDebug(VerboseTest()) - // Unthreaded, but in background. go state.Serve() + + if err := state.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } return mnt, root, func() { state.Unmount() os.RemoveAll(tmp) } - } func TestMemNodeFsWrite(t *testing.T) { wd, _, clean := setupMemNodeTest(t) defer clean() - want := "hello" + err := ioutil.WriteFile(wd+"/test", []byte(want), 0644) if err != nil { t.Fatalf("WriteFile failed: %v", err) @@ -62,7 +66,7 @@ func TestMemNodeFsWrite(t *testing.T) { } } -func TestMemNodeFs(t *testing.T) { +func TestMemNodeFsBasic(t *testing.T) { wd, _, clean := setupMemNodeTest(t) defer clean() diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/nodefs.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/nodefs.go index dfadb41..0be39d2 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/nodefs.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/nodefs.go @@ -1,3 +1,7 @@ +// 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 nodefs import "time" diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/print.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/print.go index 46f37e8..64538ca 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/print.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/print.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/syscall_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/syscall_linux.go index c2cca28..9433fe4 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/syscall_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/syscall_linux.go @@ -1,3 +1,7 @@ +// 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 nodefs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/verbose_test.go b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/verbose_test.go index 69e8a90..80eb0aa 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/nodefs/verbose_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/nodefs/verbose_test.go @@ -1,3 +1,7 @@ +// 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 nodefs import "flag" diff --git a/vendor/github.com/hanwen/go-fuse/fuse/opcode.go b/vendor/github.com/hanwen/go-fuse/fuse/opcode.go index d24730b..48a602f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/opcode.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/opcode.go @@ -1,3 +1,7 @@ +// 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 fuse import ( @@ -169,11 +173,16 @@ const _SECURITY_ACL = "system.posix_acl_access" const _SECURITY_ACL_DEFAULT = "system.posix_acl_default" func doGetXAttr(server *Server, req *request) { + if server.opts.DisableXAttrs { + req.status = ENOSYS + return + } + if server.opts.IgnoreSecurityLabels && req.inHeader.Opcode == _OP_GETXATTR { fn := req.filenames[0] if fn == _SECURITY_CAPABILITY || fn == _SECURITY_ACL_DEFAULT || fn == _SECURITY_ACL { - req.status = ENODATA + req.status = ENOATTR return } } @@ -184,6 +193,9 @@ func doGetXAttr(server *Server, req *request) { out := (*GetXAttrOut)(req.outData) switch req.inHeader.Opcode { case _OP_GETXATTR: + // TODO(hanwen): double check this. For getxattr, input.Size + // field refers to the size of the attribute, so it usually + // is not 0. sz, code := server.fileSystem.GetXAttrSize(req.inHeader, req.filenames[0]) if code.Ok() { out.Size = uint32(sz) @@ -254,7 +266,7 @@ func doBatchForget(server *Server, req *request) { forgets := *(*[]_ForgetOne)(unsafe.Pointer(h)) for i, f := range forgets { - if server.debug { + if server.opts.Debug { log.Printf("doBatchForget: forgetting %d of %d: NodeId: %d, Nlookup: %d", i+1, len(forgets), f.NodeId, f.Nlookup) } server.fileSystem.Forget(f.NodeId, f.Nlookup) @@ -583,6 +595,7 @@ func init() { _OP_NOTIFY_INODE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalInodeOut)(ptr) }, _OP_NOTIFY_DELETE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalDeleteOut)(ptr) }, _OP_STATFS: func(ptr unsafe.Pointer) interface{} { return (*StatfsOut)(ptr) }, + _OP_SYMLINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) }, } { operationHandlers[op].DecodeOut = f } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/api.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/api.go index 56184ba..d766785 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/api.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/api.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( @@ -75,4 +79,7 @@ type PathNodeFsOptions struct { // If ClientInodes is set, use Inode returned from GetAttr to // find hard-linked files. ClientInodes bool + + // Debug controls printing of debug information. + Debug bool } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy.go index 3897fee..fd7bdd5 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy_test.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy_test.go index 44993d0..f7e73b0 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy_test.go @@ -1,21 +1,21 @@ +// 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 pathfs import ( "io/ioutil" "os" "testing" + + "github.com/hanwen/go-fuse/internal/testutil" ) func TestCopyFile(t *testing.T) { - d1, err := ioutil.TempDir("", "go-fuse") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + d1 := testutil.TempDir() defer os.RemoveAll(d1) - d2, err := ioutil.TempDir("", "go-fuse") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + d2 := testutil.TempDir() defer os.RemoveAll(d2) fs1 := NewLoopbackFileSystem(d1) @@ -23,7 +23,7 @@ func TestCopyFile(t *testing.T) { content1 := "blabla" - err = ioutil.WriteFile(d1+"/file", []byte(content1), 0644) + err := ioutil.WriteFile(d1+"/file", []byte(content1), 0644) if err != nil { t.Fatalf("WriteFile failed: %v", err) } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/default.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/default.go index 62e1189..438ec6d 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/default.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/default.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( @@ -24,7 +28,7 @@ func (fs *defaultFileSystem) GetAttr(name string, context *fuse.Context) (*fuse. } func (fs *defaultFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) { - return nil, fuse.ENOSYS + return nil, fuse.ENOATTR } func (fs *defaultFileSystem) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status { diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/locking.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/locking.go index 93bef52..3da89f8 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/locking.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/locking.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback.go index 68fd9f1..825f42b 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( @@ -21,6 +25,12 @@ type loopbackFileSystem struct { // system. Its main purpose is to provide test coverage without // having to build a synthetic filesystem. func NewLoopbackFileSystem(root string) FileSystem { + // Make sure the Root path is absolute to avoid problems when the + // application changes working directory. + root, err := filepath.Abs(root) + if err != nil { + panic(err) + } return &loopbackFileSystem{ FileSystem: NewDefaultFileSystem(), Root: root, diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_darwin.go index 16e53b8..8e574f7 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_darwin.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( @@ -13,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 @@ -54,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) } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_linux.go index 20bab22..beb65a1 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_linux.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/owner_test.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/owner_test.go index 76edcb2..4acb552 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/owner_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/owner_test.go @@ -1,13 +1,17 @@ +// 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 pathfs import ( - "io/ioutil" "os" "syscall" "testing" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" + "github.com/hanwen/go-fuse/internal/testutil" ) type ownerFs struct { @@ -31,7 +35,7 @@ func (fs *ownerFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse } func setupOwnerTest(t *testing.T, opts *nodefs.Options) (workdir string, cleanup func()) { - wd, err := ioutil.TempDir("", "go-fuse-owner_test") + wd := testutil.TempDir() fs := &ownerFs{NewDefaultFileSystem()} nfs := NewPathNodeFs(fs, nil) @@ -40,6 +44,9 @@ func setupOwnerTest(t *testing.T, opts *nodefs.Options) (workdir string, cleanup 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) diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/pathfs.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/pathfs.go index 1720f07..4cca43b 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/pathfs.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/pathfs.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( @@ -12,11 +16,11 @@ import ( "github.com/hanwen/go-fuse/fuse/nodefs" ) -// A parent pointer: node should be reachable as parent.children[name] -type clientInodePath struct { - parent *pathInode - name string - node *pathInode +// refCountedInode is used in clientInodeMap. The reference count is used to decide +// if the entry in clientInodeMap can be dropped. +type refCountedInode struct { + node *pathInode + refCount int } // PathNodeFs is the file system that can translate an inode back to a @@ -32,12 +36,11 @@ type PathNodeFs struct { root *pathInode connector *nodefs.FileSystemConnector - // protects pathInode.parents + // protects clientInodeMap pathLock sync.RWMutex - // This map lists all the parent links known for a given - // nodeId. - clientInodeMap map[uint64][]*clientInodePath + // This map lists all the parent links known for a given inode number. + clientInodeMap map[uint64]*refCountedInode options *PathNodeFsOptions } @@ -68,7 +71,7 @@ func (fs *PathNodeFs) ForgetClientInodes() { return } fs.pathLock.Lock() - fs.clientInodeMap = map[uint64][]*clientInodePath{} + fs.clientInodeMap = map[uint64]*refCountedInode{} fs.root.forgetClientInodes() fs.pathLock.Unlock() } @@ -188,9 +191,7 @@ func (fs *PathNodeFs) AllFiles(name string, mask uint32) []nodefs.WithFlags { // NewPathNodeFs returns a file system that translates from inodes to // path names. func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs { - root := &pathInode{ - parents: map[parentData]struct{}{}, - } + root := &pathInode{} root.fs = fs if opts == nil { @@ -200,7 +201,7 @@ func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs { pfs := &PathNodeFs{ fs: fs, root: root, - clientInodeMap: map[uint64][]*clientInodePath{}, + clientInodeMap: map[uint64]*refCountedInode{}, options: opts, } root.pathFs = pfs @@ -212,11 +213,6 @@ func (fs *PathNodeFs) Root() nodefs.Node { return fs.root } -type parentData struct { - parent *pathInode - name string -} - // This is a combination of dentry (entry in the file/directory and // the inode). This structure is used to implement glue for FSes where // there is a one-to-one mapping of paths and inodes. @@ -224,8 +220,6 @@ type pathInode struct { pathFs *PathNodeFs fs FileSystem - parents map[parentData]struct{} - // This is to correctly resolve hardlinks of the underlying // real filesystem. clientInode uint64 @@ -256,14 +250,6 @@ func (n *pathInode) Inode() *nodefs.Inode { return n.inode } -// TODO - return *parentData? -func (n *pathInode) parent() parentData { - for k := range n.parents { - return k - } - return parentData{} -} - func (n *pathInode) SetInode(node *nodefs.Inode) { n.inode = node } @@ -291,24 +277,24 @@ func (n *pathInode) GetPath() string { // effort to avoid allocations. n.pathFs.pathLock.RLock() - walkUp := n + walkUp := n.Inode() - // TODO - guess depth? use *parentData? - parents := make([]parentData, 0, 10) + // TODO - guess depth? + segments := make([]string, 0, 10) for { - p := walkUp.parent() - if p.parent == nil { + parent, name := walkUp.Parent() + if parent == nil { break } - parents = append(parents, p) - pathLen += len(p.name) + 1 - walkUp = p.parent + segments = append(segments, name) + pathLen += len(name) + 1 + walkUp = parent } pathLen-- pathBytes := make([]byte, 0, pathLen) - for i := len(parents) - 1; i >= 0; i-- { - pathBytes = append(pathBytes, parents[i].name...) + for i := len(segments) - 1; i >= 0; i-- { + pathBytes = append(pathBytes, segments[i]...) if i > 0 { pathBytes = append(pathBytes, '/') } @@ -320,7 +306,7 @@ func (n *pathInode) GetPath() string { log.Printf("Inode = %q (%s)", path, n.fs.String()) } - if walkUp != n.pathFs.root { + if walkUp != n.pathFs.root.Inode() { // This might happen if the node has been removed from // the tree using unlink, but we are forced to run // some file system operation, because the file is @@ -334,20 +320,9 @@ func (n *pathInode) GetPath() string { } func (n *pathInode) OnAdd(parent *nodefs.Inode, name string) { - n.pathFs.pathLock.Lock() - defer n.pathFs.pathLock.Unlock() - - pathParent := parent.Node().(*pathInode) - n.parents[parentData{pathParent, name}] = struct{}{} - - if n.clientInode > 0 && n.pathFs.options.ClientInodes { - m := n.pathFs.clientInodeMap[n.clientInode] - e := &clientInodePath{ - pathParent, name, n, - } - m = append(m, e) - n.pathFs.clientInodeMap[n.clientInode] = m - } + // TODO it would be logical to increment the clientInodeMap reference count + // here. However, as the inode number is loaded lazily, we cannot do it + // yet. } func (n *pathInode) rmChild(name string) *pathInode { @@ -359,61 +334,35 @@ func (n *pathInode) rmChild(name string) *pathInode { } func (n *pathInode) OnRemove(parent *nodefs.Inode, name string) { - n.pathFs.pathLock.Lock() - defer n.pathFs.pathLock.Unlock() - - // TODO - paranoia: what if the cast fails? Can this happen? - parentPI := parent.Node().(*pathInode) - - delete(n.parents, parentData{parentPI, name}) - - if n.clientInode > 0 && n.pathFs.options.ClientInodes { - m := n.pathFs.clientInodeMap[n.clientInode] + if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() { + return + } - idx := -1 - // Find the right entry: both "parent" and "name" must match - for i, v := range m { - if v.parent == parentPI && v.name == name { - idx = i - break - } - } - if idx >= 0 { - // Delete the "idx" entry from the middle of the slice by moving the - // last element over it and truncating the slice - m[idx] = m[len(m)-1] - m = m[:len(m)-1] - n.pathFs.clientInodeMap[n.clientInode] = m - } - if len(m) == 0 { + n.pathFs.pathLock.Lock() + r := n.pathFs.clientInodeMap[n.clientInode] + if r != nil { + r.refCount-- + if r.refCount == 0 { delete(n.pathFs.clientInodeMap, n.clientInode) } } + n.pathFs.pathLock.Unlock() } -// Handle a change in clientInode number for an other wise unchanged -// pathInode. +// setClientInode sets the inode number if has not been set yet. +// This function exists to allow lazy-loading of the inode number. func (n *pathInode) setClientInode(ino uint64) { - if ino == n.clientInode || !n.pathFs.options.ClientInodes { + if ino == 0 || n.clientInode != 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() { return } n.pathFs.pathLock.Lock() defer n.pathFs.pathLock.Unlock() - if n.clientInode != 0 { - delete(n.pathFs.clientInodeMap, n.clientInode) - } - n.clientInode = ino - if p := n.parent(); p.parent != nil { - e := &clientInodePath{ - p.parent, p.name, n, - } - n.pathFs.clientInodeMap[ino] = append(n.pathFs.clientInodeMap[ino], e) - } + n.pathFs.clientInodeMap[ino] = &refCountedInode{node: n, refCount: 1} } func (n *pathInode) OnForget() { - if n.clientInode == 0 || !n.pathFs.options.ClientInodes { + if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() { return } n.pathFs.pathLock.Lock() @@ -520,8 +469,13 @@ func (n *pathInode) Rename(oldName string, newParent nodefs.Node, newName string newPath := filepath.Join(p.GetPath(), newName) code = n.fs.Rename(oldPath, newPath, context) if code.Ok() { + // The rename may have overwritten another file, remove it from the tree + p.Inode().RmChild(newName) ch := n.Inode().RmChild(oldName) - p.Inode().AddChild(newName, ch) + if ch != nil { + // oldName may have been forgotten in the meantime. + p.Inode().AddChild(newName, ch) + } } return code } @@ -568,13 +522,12 @@ func (n *pathInode) Create(name string, flags uint32, mode uint32, context *fuse func (n *pathInode) createChild(name string, isDir bool) *pathInode { i := &pathInode{ - parents: map[parentData]struct{}{}, + fs: n.fs, + pathFs: n.pathFs, } - i.fs = n.fs - i.pathFs = n.pathFs - n.Inode().NewChild(name, isDir, i) + return i } @@ -604,12 +557,12 @@ func (n *pathInode) Lookup(out *fuse.Attr, name string, context *fuse.Context) ( func (n *pathInode) findChild(fi *fuse.Attr, name string, fullPath string) (out *pathInode) { if fi.Ino > 0 { n.pathFs.pathLock.RLock() - v := n.pathFs.clientInodeMap[fi.Ino] - if len(v) > 0 { - out = v[0].node - + r := n.pathFs.clientInodeMap[fi.Ino] + if r != nil { + out = r.node + r.refCount++ if fi.Nlink == 1 { - log.Println("Found linked inode, but Nlink == 1", fullPath) + log.Printf("Found linked inode, but Nlink == 1, ino=%d, fullPath=%q", fi.Ino, fullPath) } } n.pathFs.pathLock.RUnlock() @@ -617,9 +570,9 @@ func (n *pathInode) findChild(fi *fuse.Attr, name string, fullPath string) (out if out == nil { out = n.createChild(name, fi.IsDir()) - out.clientInode = fi.Ino + out.setClientInode(fi.Ino) } else { - // should add 'out' as a child to n ? + n.Inode().AddChild(name, out.Inode()) } return out } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/prefixfs.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/prefixfs.go index 9b575c0..2cff9c6 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/prefixfs.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/prefixfs.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/readonlyfs.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/readonlyfs.go index ea3dddf..1f854b8 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/readonlyfs.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/readonlyfs.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_linux.go index 62a05ec..a6faa0b 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_linux.go @@ -1,3 +1,7 @@ +// 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 pathfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_test.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_test.go index e542c75..ab9322f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_test.go @@ -1,3 +1,9 @@ +// 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. + +// +build linux + package pathfs import ( @@ -8,7 +14,6 @@ import ( ) func TestSysUtimensat(t *testing.T) { - symlink := "/tmp/TestSysUtimensat" os.Remove(symlink) err := os.Symlink("/nonexisting/file", symlink) @@ -24,6 +29,7 @@ func TestSysUtimensat(t *testing.T) { ts[1].Nsec = 3333 ts[1].Sec = 4444 + // Linux specific. err = sysUtimensat(0, symlink, &ts, _AT_SYMLINK_NOFOLLOW) if err != nil { t.Fatal(err) diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/verbose_test.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/verbose_test.go index 01bb25f..29c6f28 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/verbose_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/verbose_test.go @@ -1,3 +1,7 @@ +// 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 pathfs import "flag" diff --git a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/xattr_test.go b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/xattr_test.go index 10d8a86..50ae4a7 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/pathfs/xattr_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/pathfs/xattr_test.go @@ -1,15 +1,20 @@ +// 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. + +// +build linux + package pathfs import ( "bytes" - "io/ioutil" "os" "path/filepath" - "syscall" "testing" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" + "github.com/hanwen/go-fuse/internal/testutil" ) var xattrGolden = map[string][]byte{ @@ -18,7 +23,6 @@ var xattrGolden = map[string][]byte{ var xattrFilename = "filename" type XAttrTestFs struct { - tester *testing.T filename string attrs map[string][]byte @@ -52,7 +56,6 @@ func (fs *XAttrTestFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, } func (fs *XAttrTestFs) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status { - fs.tester.Log("SetXAttr", name, attr, string(data), flags) if name != fs.filename { return fuse.ENOENT } @@ -68,9 +71,8 @@ func (fs *XAttrTestFs) GetXAttr(name string, attr string, context *fuse.Context) } v, ok := fs.attrs[attr] if !ok { - return nil, fuse.ENODATA + return nil, fuse.ENOATTR } - fs.tester.Log("GetXAttr", string(v)) return v, fuse.OK } @@ -90,9 +92,8 @@ func (fs *XAttrTestFs) RemoveXAttr(name string, attr string, context *fuse.Conte return fuse.ENOENT } _, ok := fs.attrs[attr] - fs.tester.Log("RemoveXAttr", name, attr, ok) if !ok { - return fuse.ENODATA + return fuse.ENOATTR } delete(fs.attrs, attr) return fuse.OK @@ -105,18 +106,14 @@ func readXAttr(p, a string) (val []byte, err error) { func xattrTestCase(t *testing.T, nm string, m map[string][]byte) (mountPoint string, cleanup func()) { xfs := NewXAttrFs(nm, m) - xfs.tester = t - mountPoint, err := ioutil.TempDir("", "go-fuse-xattr_test") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + mountPoint = testutil.TempDir() nfs := NewPathNodeFs(xfs, nil) - state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), nil) + state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), + &nodefs.Options{Debug: VerboseTest()}) if err != nil { - t.Fatalf("TempDir failed: %v", err) + t.Fatalf("MountRoot failed: %v", err) } - state.SetDebug(VerboseTest()) go state.Serve() return mountPoint, func() { @@ -199,7 +196,7 @@ func TestXAttrRead(t *testing.T) { sysRemovexattr(mounted, "third") val, err = readXAttr(mounted, "third") - if err != syscall.ENODATA { + if fuse.ToStatus(err) != fuse.ENOATTR { t.Error("Data not removed?", err, val) } } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/print.go b/vendor/github.com/hanwen/go-fuse/fuse/print.go index 29e12b0..e790b10 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/print.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/print.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/print_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/print_darwin.go index 16a9d6e..e065234 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/print_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/print_darwin.go @@ -1,3 +1,7 @@ +// 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 fuse import ( @@ -38,7 +42,7 @@ func (me *MknodIn) string() string { } func (me *ReadIn) string() string { - return fmt.Sprintf("{Fh %d off %d sz %d %s L %d %s}", + return fmt.Sprintf("{Fh %d off %d sz %d %s}", me.Fh, me.Offset, me.Size, FlagString(readFlagNames, int64(me.ReadFlags), "")) } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/print_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/print_linux.go index 2b9f26f..30fd1fa 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/print_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/print_linux.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/read.go b/vendor/github.com/hanwen/go-fuse/fuse/read.go index 60304b5..1612679 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/read.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/read.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/request.go b/vendor/github.com/hanwen/go-fuse/fuse/request.go index c9768ac..7b5a794 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/request.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/request.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/request_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/request_darwin.go index c920237..6344624 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/request_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/request_darwin.go @@ -1,3 +1,7 @@ +// 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 fuse const outputHeaderSize = 200 diff --git a/vendor/github.com/hanwen/go-fuse/fuse/request_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/request_linux.go index b7d6265..a29123b 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/request_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/request_linux.go @@ -1,3 +1,7 @@ +// 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 fuse const outputHeaderSize = 160 diff --git a/vendor/github.com/hanwen/go-fuse/fuse/server.go b/vendor/github.com/hanwen/go-fuse/fuse/server.go index cc597c3..0335e1d 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/server.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/server.go @@ -1,3 +1,7 @@ +// 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 fuse import ( @@ -31,15 +35,10 @@ type Server struct { // I/O with kernel and daemon. mountFd int - // Dump debug info onto stdout. - debug bool - latencies LatencyMap opts *MountOptions - started chan struct{} - // Pool for request structs. reqPool sync.Pool @@ -52,21 +51,25 @@ type Server struct { singleReader bool canSplice bool loops sync.WaitGroup + + ready chan error } +// SetDebug is deprecated. Use MountOptions.Debug instead. func (ms *Server) SetDebug(dbg bool) { - ms.debug = dbg + // This will typically trigger the race detector. + ms.opts.Debug = dbg } // KernelSettings returns the Init message from the kernel, so // filesystems can adapt to availability of features of the kernel -// driver. -func (ms *Server) KernelSettings() InitIn { +// driver. The message should not be altered. +func (ms *Server) KernelSettings() *InitIn { ms.reqMu.Lock() s := ms.kernelSettings ms.reqMu.Unlock() - return s + return &s } const _MAX_NAME_LEN = 20 @@ -136,10 +139,23 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server if o.MaxWrite > MAX_KERNEL_WRITE { o.MaxWrite = MAX_KERNEL_WRITE } - opts = &o + if o.Name == "" { + name := fs.String() + l := len(name) + if l > _MAX_NAME_LEN { + l = _MAX_NAME_LEN + } + o.Name = strings.Replace(name[:l], ",", ";", -1) + } + + for _, s := range o.optionsStrings() { + if strings.Contains(s, ",") { + return nil, fmt.Errorf("found ',' in option string %q", s) + } + } + ms := &Server{ fileSystem: fs, - started: make(chan struct{}), opts: &o, // OSX has races when multiple routines read from the // FUSE device: on unmount, sometime some reads do not @@ -148,26 +164,6 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server } ms.reqPool.New = func() interface{} { return new(request) } ms.readPool.New = func() interface{} { return make([]byte, o.MaxWrite+PAGESIZE) } - optStrs := opts.Options - if opts.AllowOther { - optStrs = append(optStrs, "allow_other") - } - - name := opts.Name - if name == "" { - name = ms.fileSystem.String() - l := len(name) - if l > _MAX_NAME_LEN { - l = _MAX_NAME_LEN - } - name = strings.Replace(name[:l], ",", ";", -1) - } - optStrs = append(optStrs, "subtype="+name) - - fsname := opts.FsName - if len(fsname) > 0 { - optStrs = append(optStrs, "fsname="+fsname) - } mountPoint = filepath.Clean(mountPoint) if !filepath.IsAbs(mountPoint) { @@ -177,17 +173,41 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server } mountPoint = filepath.Clean(filepath.Join(cwd, mountPoint)) } - fd, err := mount(mountPoint, strings.Join(optStrs, ",")) + ms.ready = make(chan error, 1) + fd, err := mount(mountPoint, &o, ms.ready) if err != nil { return nil, err } - ms.fileSystem.Init(ms) ms.mountPoint = mountPoint ms.mountFd = fd + + if code := ms.handleInit(); !code.Ok() { + syscall.Close(fd) + // TODO - unmount as well? + return nil, fmt.Errorf("init: %s", code) + } return ms, nil } +func (o *MountOptions) optionsStrings() []string { + var r []string + r = append(r, o.Options...) + + if o.AllowOther { + r = append(r, "allow_other") + } + + if o.FsName != "" { + r = append(r, "fsname="+o.FsName) + } + if o.Name != "" { + r = append(r, "subtype="+o.Name) + } + + return r +} + // DebugData returns internal status information for debugging // purposes. func (ms *Server) DebugData() string { @@ -279,13 +299,12 @@ func (ms *Server) returnRequest(req *request) { } req.clear() - ms.reqMu.Lock() - if req.bufferPoolInputBuf != nil { - ms.readPool.Put(req.bufferPoolInputBuf) + + if p := req.bufferPoolInputBuf; p != nil { req.bufferPoolInputBuf = nil + ms.readPool.Put(p) } ms.reqPool.Put(req) - ms.reqMu.Unlock() } func (ms *Server) recordStats(req *request) { @@ -311,6 +330,27 @@ func (ms *Server) Serve() { ms.writeMu.Unlock() } +func (ms *Server) handleInit() Status { + // The first request should be INIT; read it synchronously, + // and don't spawn new readers. + orig := ms.singleReader + ms.singleReader = true + req, errNo := ms.readRequest(false) + ms.singleReader = orig + + if errNo != OK || req == nil { + return errNo + } + if code := ms.handleRequest(req); !code.Ok() { + return code + } + + // INIT is handled. Init the file system, but don't accept + // incoming requests, so the file system can setup itself. + ms.fileSystem.Init(ms) + return OK +} + func (ms *Server) loop(exitIdle bool) { defer ms.loops.Done() exit: @@ -339,13 +379,13 @@ exit: } } -func (ms *Server) handleRequest(req *request) { +func (ms *Server) handleRequest(req *request) Status { req.parse() if req.handler == nil { req.status = ENOSYS } - if req.status.Ok() && ms.debug { + if req.status.Ok() && ms.opts.Debug { log.Println(req.InputDebug()) } @@ -364,6 +404,7 @@ func (ms *Server) handleRequest(req *request) { errNo, operationName(req.inHeader.Opcode)) } ms.returnRequest(req) + return Status(errNo) } func (ms *Server) allocOut(req *request, size uint32) []byte { @@ -385,7 +426,7 @@ func (ms *Server) write(req *request) Status { } header := req.serializeHeader(req.flatDataSize()) - if ms.debug { + if ms.opts.Debug { log.Println(req.OutputDebug()) } @@ -394,15 +435,16 @@ func (ms *Server) write(req *request) Status { } s := ms.systemWrite(req, header) - if req.inHeader.Opcode == _OP_INIT { - close(ms.started) - } return s } // InodeNotify invalidates the information associated with the inode // (ie. data cache, attributes, etc.) func (ms *Server) InodeNotify(node uint64, off int64, length int64) Status { + if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_INODE) { + return ENOSYS + } + entry := &NotifyInvalInodeOut{ Ino: node, Off: off, @@ -422,7 +464,7 @@ func (ms *Server) InodeNotify(node uint64, off int64, length int64) Status { result := ms.write(&req) ms.writeMu.Unlock() - if ms.debug { + if ms.opts.Debug { log.Println("Response: INODE_NOTIFY", result) } return result @@ -464,7 +506,7 @@ func (ms *Server) DeleteNotify(parent uint64, child uint64, name string) Status result := ms.write(&req) ms.writeMu.Unlock() - if ms.debug { + if ms.opts.Debug { log.Printf("Response: DELETE_NOTIFY: %v", result) } return result @@ -474,6 +516,9 @@ func (ms *Server) DeleteNotify(parent uint64, child uint64, name string) Status // within a directory changes. You should not hold any FUSE filesystem // locks, as that can lead to deadlock. func (ms *Server) EntryNotify(parent uint64, name string) Status { + if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_ENTRY) { + return ENOSYS + } req := request{ inHeader: &InHeader{ Opcode: _OP_NOTIFY_ENTRY, @@ -499,12 +544,32 @@ func (ms *Server) EntryNotify(parent uint64, name string) Status { result := ms.write(&req) ms.writeMu.Unlock() - if ms.debug { + if ms.opts.Debug { log.Printf("Response: ENTRY_NOTIFY: %v", result) } return result } +// SupportsVersion returns true if the kernel supports the given +// protocol version or newer. +func (in *InitIn) SupportsVersion(maj, min uint32) bool { + return in.Major >= maj && in.Minor >= min +} + +// SupportsNotify returns whether a certain notification type is +// supported. Pass any of the NOTIFY_INVAL_* types as argument. +func (in *InitIn) SupportsNotify(notifyType int) bool { + switch notifyType { + case NOTIFY_INVAL_ENTRY: + return in.SupportsVersion(7, 12) + case NOTIFY_INVAL_INODE: + return in.SupportsVersion(7, 12) + case NOTIFY_INVAL_DELETE: + return in.SupportsVersion(7, 18) + } + return false +} + var defaultBufferPool BufferPool func init() { @@ -512,8 +577,10 @@ func init() { } // WaitMount waits for the first request to be served. Use this to -// avoid racing between accessing the (empty) mountpoint, and the OS -// trying to setup the user-space mount. -func (ms *Server) WaitMount() { - <-ms.started +// avoid racing between accessing the (empty or not yet mounted) +// mountpoint, and the OS trying to setup the user-space mount. +// Currently, this call only necessary on OSX. +func (ms *Server) WaitMount() error { + err := <-ms.ready + return err } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/server_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/server_darwin.go index 043e51c..b4df8da 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/server_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/server_darwin.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/server_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/server_linux.go index b3fe8dd..f5058f6 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/server_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/server_linux.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/splice_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/splice_darwin.go index b5a8d5f..f9a0dd8 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/splice_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/splice_darwin.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/splice_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/splice_linux.go index 3fd6dd9..1bcb248 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/splice_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/splice_linux.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/syscall_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/syscall_darwin.go index aaf157f..71ede66 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/syscall_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/syscall_darwin.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/syscall_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/syscall_linux.go index 69aafb2..f1a8cac 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/syscall_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/syscall_linux.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/cache_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/cache_test.go index a8410b1..d97813f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/cache_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/cache_test.go @@ -1,15 +1,21 @@ +// 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 test import ( "bytes" "io/ioutil" "os" + "runtime" "sync" "testing" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) type cacheFs struct { @@ -29,26 +35,25 @@ func (fs *cacheFs) Open(name string, flags uint32, context *fuse.Context) (fuseF } 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) - } + dir := testutil.TempDir() 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) + 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) } - state.SetDebug(VerboseTest()) - conn.SetDebug(VerboseTest()) - pfs.SetDebug(VerboseTest()) go state.Serve() - + if err := state.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } return dir, pfs, func() { err := state.Unmount() if err == nil { @@ -57,51 +62,52 @@ func setupCacheTest(t *testing.T) (string, *pathfs.PathNodeFs, func()) { } } -func TestCacheFs(t *testing.T) { +func TestFopenKeepCache(t *testing.T) { + if runtime.GOOS == "darwin" { + t.Skip("FOPEN_KEEP_CACHE is broken on Darwin.") + } + wd, pathfs, clean := setupCacheTest(t) defer clean() - content1 := "hello" - content2 := "qqqq" - err := ioutil.WriteFile(wd+"/orig/file.txt", []byte(content1), 0644) - if err != nil { + before := "before" + after := "after" + if err := ioutil.WriteFile(wd+"/orig/file.txt", []byte(before), 0644); err != nil { t.Fatalf("WriteFile failed: %v", err) } c, err := ioutil.ReadFile(wd + "/mnt/file.txt") if err != nil { - t.Fatalf("ReadFile failed: %v", err) - } - - if string(c) != "hello" { - t.Fatalf("expect 'hello' %q", string(c)) + t.Fatalf("ReadFile: %v", err) + } else if string(c) != before { + t.Fatalf("ReadFile: got %q, want %q", c, before) } - err = ioutil.WriteFile(wd+"/orig/file.txt", []byte(content2), 0644) - if err != nil { - t.Fatalf("WriteFile failed: %v", err) + if err := ioutil.WriteFile(wd+"/orig/file.txt", []byte(after), 0644); err != nil { + t.Fatalf("WriteFile: %v", err) } c, err = ioutil.ReadFile(wd + "/mnt/file.txt") if err != nil { - t.Fatalf("ReadFile failed: %v", err) + t.Fatalf("ReadFile: %v", err) + } else if string(c) != before { + t.Fatalf("ReadFile: got %q, want cached %q", c, before) } - if string(c) != "hello" { - t.Fatalf("Page cache skipped: expect 'hello' %q", string(c)) + if minor := pathfs.Connector().Server().KernelSettings().Minor; minor < 12 { + t.Skip("protocol v%d has no notify support.", minor) } code := pathfs.EntryNotify("", "file.txt") if !code.Ok() { - t.Errorf("Entry notify failed: %v", code) + t.Errorf("EntryNotify: %v", code) } c, err = ioutil.ReadFile(wd + "/mnt/file.txt") if err != nil { - t.Fatalf("ReadFile failed: %v", err) - } - if string(c) != string(content2) { - t.Fatalf("Mismatch after notify expect '%s' %q", content2, string(c)) + t.Fatalf("ReadFile: %v", err) + } else if string(c) != after { + t.Fatalf("ReadFile: got %q after notify, want %q", c, after) } } @@ -134,20 +140,21 @@ func TestNonseekable(t *testing.T) { fs := &nonseekFs{FileSystem: pathfs.NewDefaultFileSystem()} fs.Length = 200 * 1024 - dir, err := ioutil.TempDir("", "go-fuse-cache_test") - if err != nil { - t.Fatalf("failed: %v", err) - } + dir := testutil.TempDir() defer os.RemoveAll(dir) nfs := pathfs.NewPathNodeFs(fs, nil) - state, _, err := nodefs.MountRoot(dir, nfs.Root(), nil) + opts := nodefs.NewOptions() + opts.Debug = testutil.VerboseTest() + state, _, err := nodefs.MountRoot(dir, nfs.Root(), opts) if err != nil { t.Fatalf("failed: %v", err) } - state.SetDebug(VerboseTest()) 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 { @@ -163,25 +170,22 @@ func TestNonseekable(t *testing.T) { } func TestGetAttrRace(t *testing.T) { - dir, err := ioutil.TempDir("", "go-fuse-cache_test") - if err != nil { - t.Fatalf("failed: %v", err) - } + 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, nil) - state, conn, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(), - &nodefs.Options{}) + 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) } - state.SetDebug(VerboseTest()) - conn.SetDebug(VerboseTest()) - pfs.SetDebug(VerboseTest()) go state.Serve() + if err := state.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } defer state.Unmount() diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/defaultread_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/defaultread_test.go index 83593b8..fc5a78f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/defaultread_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/defaultread_test.go @@ -1,3 +1,7 @@ +// 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 test import ( @@ -8,6 +12,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) type DefaultReadFS struct { @@ -33,21 +38,23 @@ func (fs *DefaultReadFS) Open(name string, f uint32, context *fuse.Context) (nod func defaultReadTest(t *testing.T) (root string, cleanup func()) { fs := &DefaultReadFS{ FileSystem: pathfs.NewDefaultFileSystem(), + size: 22, } var err error - dir, err := ioutil.TempDir("", "go-fuse") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + dir := testutil.TempDir() pathfs := pathfs.NewPathNodeFs(fs, nil) - state, _, err := nodefs.MountRoot(dir, pathfs.Root(), 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) } - state.SetDebug(VerboseTest()) go state.Serve() - + if err := state.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } return dir, func() { state.Unmount() os.Remove(dir) diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/delete_linux_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/delete_linux_test.go index d0524d1..557df6a 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/delete_linux_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/delete_linux_test.go @@ -1,3 +1,7 @@ +// 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 test import ( @@ -11,6 +15,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" + "github.com/hanwen/go-fuse/internal/testutil" ) type flipNode struct { @@ -29,27 +34,28 @@ func (f *flipNode) GetAttr(out *fuse.Attr, file nodefs.File, c *fuse.Context) fu } func TestDeleteNotify(t *testing.T) { - dir, err := ioutil.TempDir("", "go-fuse-delete_test") - if err != nil { - t.Fatalf("TempDir failed %v", err) - } + 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) + err := os.Mkdir(mnt, 0755) if err != nil { t.Fatal(err) } - state, err := fuse.NewServer(conn.RawFS(), mnt, nil) + state, err := fuse.NewServer(conn.RawFS(), mnt, &fuse.MountOptions{ + Debug: testutil.VerboseTest(), + }) if err != nil { t.Fatal(err) } - state.SetDebug(VerboseTest()) 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() { diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/fsetattr_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/fsetattr_test.go index 0476fa4..675c19c 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/fsetattr_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/fsetattr_test.go @@ -1,7 +1,10 @@ +// 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 test import ( - "io/ioutil" "os" "syscall" "testing" @@ -10,6 +13,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) type MutableDataFile struct { @@ -101,7 +105,7 @@ type FSetAttrFs struct { } func (fs *FSetAttrFs) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) { - return nil, fuse.ENODATA + return nil, fuse.ENOATTR } func (fs *FSetAttrFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { @@ -139,51 +143,35 @@ func NewFile() *MutableDataFile { } func setupFAttrTest(t *testing.T, fs pathfs.FileSystem) (dir string, clean func()) { - dir, err := ioutil.TempDir("", "go-fuse-fsetattr_test") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + dir = testutil.TempDir() nfs := pathfs.NewPathNodeFs(fs, nil) - state, _, err := nodefs.MountRoot(dir, nfs.Root(), 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) } - state.SetDebug(VerboseTest()) go state.Serve() - - // Trigger INIT. - os.Lstat(dir) - if state.KernelSettings().Flags&fuse.CAP_FILE_OPS == 0 { - t.Log("Mount does not support file operations") + if err := state.WaitMount(); err != nil { + t.Fatal("WaitMount", err) } - return dir, func() { - if state.Unmount() == nil { + clean = func() { + if err := state.Unmount(); err != nil { + t.Errorf("cleanup: Unmount: %v", err) + } else { os.RemoveAll(dir) } } -} - -func TestDataReadLarge(t *testing.T) { - fs := &FSetAttrFs{ - FileSystem: pathfs.NewDefaultFileSystem(), - } - dir, clean := setupFAttrTest(t, fs) - defer clean() - content := randomData(385 * 1023) - fn := dir + "/file" - err := ioutil.WriteFile(fn, []byte(content), 0644) - if err != nil { - t.Fatalf("WriteFile failed: %v", err) + if state.KernelSettings().Flags&fuse.CAP_FILE_OPS == 0 { + clean() + t.Skip("Mount does not support file operations") } - back, err := ioutil.ReadFile(fn) - if err != nil { - t.Fatalf("ReadFile failed: %v", err) - } - CompareSlices(t, back, content) + return dir, clean } func TestFSetAttr(t *testing.T) { @@ -261,12 +249,13 @@ func TestFSetAttr(t *testing.T) { t.Error("Fsync failed:", os.NewSyscallError("Fsync", code)) } + // Close the file, otherwise we can't unmount. + f.Close() + // Shutdown the FUSE FS so we can safely look at fSetAttrFs clean() clean = nil if !fSetAttrFs.file.FsyncCalled { t.Error("Fsync was not called") } - - // TODO - test chown if run as root. } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_darwin_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_darwin_test.go index 45a9985..2ec979b 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_darwin_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_darwin_test.go @@ -1,3 +1,7 @@ +// 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 test import ( @@ -6,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 } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_linux_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_linux_test.go index 048877b..5e6ec2f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_linux_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_linux_test.go @@ -1,3 +1,7 @@ +// 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 test import ( @@ -101,3 +105,20 @@ func TestFallocate(t *testing.T) { fi.Size()) } } + +// Check that "." and ".." exists. syscall.Getdents is linux specific. +func TestSpecialEntries(t *testing.T) { + tc := NewTestCase(t) + defer tc.Cleanup() + + d, err := os.Open(tc.mnt) + if err != nil { + t.Fatalf("Open failed: %v", err) + } + defer d.Close() + buf := make([]byte, 100) + n, err := syscall.Getdents(int(d.Fd()), buf) + if n == 0 { + t.Errorf("directory is empty, entries '.' and '..' are missing") + } +} diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_test.go index 39bb125..5dc0062 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/loopback_test.go @@ -1,3 +1,7 @@ +// 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 test import ( @@ -18,6 +22,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) const mode uint32 = 0757 @@ -70,10 +75,7 @@ func NewTestCase(t *testing.T) *testCase { const subdir string = "subdir" var err error - tc.tmpDir, err = ioutil.TempDir("", "go-fuse") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + tc.tmpDir = testutil.TempDir() tc.orig = tc.tmpDir + "/orig" tc.mnt = tc.tmpDir + "/mnt" @@ -96,20 +98,21 @@ func NewTestCase(t *testing.T) *testCase { EntryTimeout: testTtl, AttrTimeout: testTtl, NegativeTimeout: 0.0, + Debug: testutil.VerboseTest(), }) - tc.connector.SetDebug(VerboseTest()) tc.state, err = fuse.NewServer( - fuse.NewRawFileSystem(tc.connector.RawFS()), tc.mnt, &fuse.MountOptions{SingleThreaded: true}) + fuse.NewRawFileSystem(tc.connector.RawFS()), tc.mnt, &fuse.MountOptions{ + SingleThreaded: true, + Debug: testutil.VerboseTest(), + }) if err != nil { t.Fatal("NewServer:", err) } - tc.state.SetDebug(VerboseTest()) - - // Unthreaded, but in background. go tc.state.Serve() - - tc.state.WaitMount() + if err := tc.state.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } return tc } @@ -677,6 +680,20 @@ func TestReadLarge(t *testing.T) { CompareSlices(t, back, content) } +func TestWriteLarge(t *testing.T) { + tc := NewTestCase(t) + defer tc.Cleanup() + + content := randomData(385 * 1023) + tc.WriteFile(tc.mountFile, []byte(content), 0644) + + back, err := ioutil.ReadFile(tc.origFile) + if err != nil { + t.Fatalf("ReadFile failed: %v", err) + } + CompareSlices(t, back, content) +} + func randomLengthString(length int) string { r := rand.Intn(length) @@ -846,13 +863,10 @@ func TestFStatFs(t *testing.T) { } func TestOriginalIsSymlink(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "go-fuse-loopback_test") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + tmpDir := testutil.TempDir() defer os.RemoveAll(tmpDir) orig := tmpDir + "/orig" - err = os.Mkdir(orig, 0755) + err := os.Mkdir(orig, 0755) if err != nil { t.Fatalf("Mkdir failed: %v", err) } @@ -874,6 +888,9 @@ func TestOriginalIsSymlink(t *testing.T) { 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) @@ -924,19 +941,19 @@ func TestUmask(t *testing.T) { } } -// Check that "." and ".." exists -func TestSpecialEntries(t *testing.T) { +// Check that chgrp(1) works +func TestChgrp(t *testing.T) { tc := NewTestCase(t) defer tc.Cleanup() - d, err := os.Open(tc.mnt) + f, err := os.Create(tc.mnt + "/file") if err != nil { - t.Fatalf("Open failed: %v", err) + t.Fatalf("Create failed: %v", err) } - defer d.Close() - buf := make([]byte, 100) - n, err := syscall.Getdents(int(d.Fd()), buf) - if n == 0 { - t.Errorf("directory is empty, entries '.' and '..' are missing") + defer f.Close() + + err = f.Chown(-1, os.Getgid()) + if err != nil { + t.Errorf("Chown failed: %v", err) } } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/mount_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/mount_test.go index 0ca050e..e96714f 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/mount_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/mount_test.go @@ -1,15 +1,21 @@ +// 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 test import ( "io/ioutil" "os" "path/filepath" + "runtime" "testing" "time" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) func TestMountOnExisting(t *testing.T) { @@ -165,10 +171,7 @@ func TestDeletedUnmount(t *testing.T) { } func TestDefaultNodeMount(t *testing.T) { - dir, err := ioutil.TempDir("", "go-fuse") - if err != nil { - t.Fatalf("TempDir: %v", err) - } + dir := testutil.TempDir() defer os.RemoveAll(dir) root := nodefs.NewDefaultNode() s, conn, err := nodefs.MountRoot(dir, root, nil) @@ -176,6 +179,9 @@ func TestDefaultNodeMount(t *testing.T) { 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() { @@ -190,3 +196,30 @@ func TestDefaultNodeMount(t *testing.T) { t.Fatalf("got %q, want %q", entries[0].Name(), "sub") } } + +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) + } +} diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/notify_linux_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/notify_linux_test.go index 83c3a1f..a563fe3 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/notify_linux_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/notify_linux_test.go @@ -1,7 +1,10 @@ +// 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 test import ( - "io/ioutil" "os" "testing" "time" @@ -9,6 +12,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) type NotifyFs struct { @@ -74,25 +78,25 @@ type NotifyTest struct { func NewNotifyTest(t *testing.T) *NotifyTest { me := &NotifyTest{} me.fs = newNotifyFs() - var err error - me.dir, err = ioutil.TempDir("", "go-fuse-notify_test") - if err != nil { - t.Fatalf("TempDir failed: %v", err) - } + 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) } - me.state.SetDebug(VerboseTest()) go me.state.Serve() + if err := me.state.WaitMount(); err != nil { + t.Fatal("WaitMount", err) + } return me } diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/xattr_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/xattr_test.go new file mode 100644 index 0000000..03d6700 --- /dev/null +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/xattr_test.go @@ -0,0 +1,66 @@ +// 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. + +// +build linux + +package test + +import ( + "os" + "path/filepath" + "syscall" + "testing" + + "github.com/hanwen/go-fuse/fuse" + "github.com/hanwen/go-fuse/fuse/nodefs" + "github.com/hanwen/go-fuse/internal/testutil" +) + +// this file is linux-only, since it uses syscall.Getxattr. + +type xattrNode struct { + nodefs.Node +} + +func (n *xattrNode) OnMount(fsConn *nodefs.FileSystemConnector) { + n.Inode().NewChild("child", false, &xattrChildNode{nodefs.NewDefaultNode()}) +} + +type xattrChildNode struct { + nodefs.Node +} + +func (n *xattrChildNode) GetXAttr(attr string, context *fuse.Context) ([]byte, fuse.Status) { + return []byte("value"), fuse.OK +} + +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) + } +} diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/xfs_test.go b/vendor/github.com/hanwen/go-fuse/fuse/test/xfs_test.go index cd4e419..b62da74 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/xfs_test.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/test/xfs_test.go @@ -1,3 +1,9 @@ +// 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. + +// +build linux + package test import ( diff --git a/vendor/github.com/hanwen/go-fuse/fuse/typeprint.go b/vendor/github.com/hanwen/go-fuse/fuse/typeprint.go index fd68a65..b4a6177 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/typeprint.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/typeprint.go @@ -1,3 +1,7 @@ +// 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 fuse func (a *Attr) String() string { diff --git a/vendor/github.com/hanwen/go-fuse/fuse/types.go b/vendor/github.com/hanwen/go-fuse/fuse/types.go index 73b65ba..3438cc2 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/types.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/types.go @@ -1,3 +1,7 @@ +// 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 fuse import ( @@ -337,13 +341,14 @@ type NotifyInvalDeleteOut struct { } const ( - NOTIFY_POLL = -1 - NOTIFY_INVAL_INODE = -2 - NOTIFY_INVAL_ENTRY = -3 - NOTIFY_STORE = -4 - NOTIFY_RETRIEVE = -5 + // NOTIFY_POLL = -1 + NOTIFY_INVAL_INODE = -2 + NOTIFY_INVAL_ENTRY = -3 + // NOTIFY_STORE = -4 + // NOTIFY_RETRIEVE = -5 NOTIFY_INVAL_DELETE = -6 - NOTIFY_CODE_MAX = -6 + +// NOTIFY_CODE_MAX = -6 ) type FlushIn struct { diff --git a/vendor/github.com/hanwen/go-fuse/fuse/types_darwin.go b/vendor/github.com/hanwen/go-fuse/fuse/types_darwin.go index 162244f..e93dd23 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/types_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/types_darwin.go @@ -1,5 +1,17 @@ +// 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 fuse +import ( + "syscall" +) + +const ( + ENOATTR = Status(syscall.ENOATTR) // ENOATTR is not defined for all GOOS. +) + type Attr struct { Ino uint64 Size uint64 diff --git a/vendor/github.com/hanwen/go-fuse/fuse/types_linux.go b/vendor/github.com/hanwen/go-fuse/fuse/types_linux.go index 33e4252..d7a40e1 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/types_linux.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/types_linux.go @@ -1,5 +1,17 @@ +// 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 fuse +import ( + "syscall" +) + +const ( + ENOATTR = Status(syscall.ENODATA) // On Linux, ENOATTR is an alias for ENODATA. +) + type Attr struct { Ino uint64 Size uint64 diff --git a/vendor/github.com/hanwen/go-fuse/fuse/upgrade.go b/vendor/github.com/hanwen/go-fuse/fuse/upgrade.go index fce8ee9..c8d1898 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/upgrade.go +++ b/vendor/github.com/hanwen/go-fuse/fuse/upgrade.go @@ -1,3 +1,7 @@ +// 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 fuse import ( diff --git a/vendor/github.com/hanwen/go-fuse/internal/testutil/tempdir.go b/vendor/github.com/hanwen/go-fuse/internal/testutil/tempdir.go new file mode 100644 index 0000000..09787f4 --- /dev/null +++ b/vendor/github.com/hanwen/go-fuse/internal/testutil/tempdir.go @@ -0,0 +1,38 @@ +// 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 testutil + +import ( + "io/ioutil" + "log" + "runtime" + "strings" +) + +// TempDir creates a temporary directory that includes the name of the +// testcase. Panics if there was an I/O problem creating the directory. +func TempDir() string { + frames := make([]uintptr, 10) // at least 1 entry needed + n := runtime.Callers(1, frames) + + lastName := "" + for _, pc := range frames[:n] { + f := runtime.FuncForPC(pc) + name := f.Name() + i := strings.LastIndex(name, ".") + if i >= 0 { + name = name[i+1:] + } + if strings.HasPrefix(name, "Test") { + lastName = name + } + } + + dir, err := ioutil.TempDir("", lastName) + if err != nil { + log.Panicf("TempDir(%s): %v", lastName, err) + } + return dir +} diff --git a/vendor/github.com/hanwen/go-fuse/fuse/test/verbose.go b/vendor/github.com/hanwen/go-fuse/internal/testutil/verbose.go similarity index 51% rename from vendor/github.com/hanwen/go-fuse/fuse/test/verbose.go rename to vendor/github.com/hanwen/go-fuse/internal/testutil/verbose.go index 43c0775..406b6c5 100644 --- a/vendor/github.com/hanwen/go-fuse/fuse/test/verbose.go +++ b/vendor/github.com/hanwen/go-fuse/internal/testutil/verbose.go @@ -1,4 +1,8 @@ -package test +// 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 testutil import "flag" diff --git a/vendor/github.com/hanwen/go-fuse/splice/copy.go b/vendor/github.com/hanwen/go-fuse/splice/copy.go index b34e79b..e50ca0d 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/copy.go +++ b/vendor/github.com/hanwen/go-fuse/splice/copy.go @@ -1,3 +1,7 @@ +// 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 splice import ( diff --git a/vendor/github.com/hanwen/go-fuse/splice/copy_test.go b/vendor/github.com/hanwen/go-fuse/splice/copy_test.go index e9971e2..1492a2a 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/copy_test.go +++ b/vendor/github.com/hanwen/go-fuse/splice/copy_test.go @@ -1,3 +1,7 @@ +// 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 splice import ( diff --git a/vendor/github.com/hanwen/go-fuse/splice/pair.go b/vendor/github.com/hanwen/go-fuse/splice/pair.go index 1fb7b28..834a118 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/pair.go +++ b/vendor/github.com/hanwen/go-fuse/splice/pair.go @@ -1,3 +1,7 @@ +// 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 splice import ( diff --git a/vendor/github.com/hanwen/go-fuse/splice/pair_darwin.go b/vendor/github.com/hanwen/go-fuse/splice/pair_darwin.go index 8cd7f27..8780bb1 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/pair_darwin.go +++ b/vendor/github.com/hanwen/go-fuse/splice/pair_darwin.go @@ -1,3 +1,7 @@ +// 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 splice import () diff --git a/vendor/github.com/hanwen/go-fuse/splice/pair_linux.go b/vendor/github.com/hanwen/go-fuse/splice/pair_linux.go index 5e27d1f..9877239 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/pair_linux.go +++ b/vendor/github.com/hanwen/go-fuse/splice/pair_linux.go @@ -1,3 +1,7 @@ +// 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 splice import ( diff --git a/vendor/github.com/hanwen/go-fuse/splice/pool.go b/vendor/github.com/hanwen/go-fuse/splice/pool.go index 28c3911..b8bcaeb 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/pool.go +++ b/vendor/github.com/hanwen/go-fuse/splice/pool.go @@ -1,3 +1,7 @@ +// 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 splice import ( diff --git a/vendor/github.com/hanwen/go-fuse/splice/splice.go b/vendor/github.com/hanwen/go-fuse/splice/splice.go index f415e78..8e153e2 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/splice.go +++ b/vendor/github.com/hanwen/go-fuse/splice/splice.go @@ -1,3 +1,7 @@ +// 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 splice // Routines for efficient file to file copying. diff --git a/vendor/github.com/hanwen/go-fuse/splice/splice_test.go b/vendor/github.com/hanwen/go-fuse/splice/splice_test.go index cccfb4f..98b8344 100644 --- a/vendor/github.com/hanwen/go-fuse/splice/splice_test.go +++ b/vendor/github.com/hanwen/go-fuse/splice/splice_test.go @@ -1,3 +1,7 @@ +// 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 splice import ( diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/autounion.go b/vendor/github.com/hanwen/go-fuse/unionfs/autounion.go index e457e51..db1214d 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/autounion.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/autounion.go @@ -1,3 +1,7 @@ +// 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 ( @@ -57,14 +61,13 @@ type AutoUnionFsOptions struct { } const ( - _READONLY = "READONLY" - _STATUS = "status" - _CONFIG = "config" - _DEBUG = "debug" - _DEBUG_SETTING = "debug_setting" - _ROOT = "root" - _VERSION = "gounionfs_version" - _SCAN_CONFIG = ".scan_config" + _READONLY = "READONLY" + _STATUS = "status" + _CONFIG = "config" + _DEBUG = "debug" + _ROOT = "root" + _VERSION = "gounionfs_version" + _SCAN_CONFIG = ".scan_config" ) func NewAutoUnionFs(directory string, options AutoUnionFsOptions) pathfs.FileSystem { @@ -238,10 +241,6 @@ func (fs *autoUnionFs) Readlink(path string, context *fuse.Context) (out string, return fs.root, fuse.OK } - if comps[0] == _STATUS && comps[1] == _DEBUG_SETTING && fs.hasDebug() { - return "1", fuse.OK - } - if comps[0] != _CONFIG { return "", fuse.ENOENT } @@ -270,11 +269,6 @@ func (fs *autoUnionFs) Symlink(pointedTo string, linkName string, context *fuse. return fuse.EPERM } - if comps[0] == _STATUS && comps[1] == _DEBUG_SETTING { - fs.SetDebug(true) - return fuse.OK - } - if comps[0] == _CONFIG { roots := fs.getRoots(pointedTo) if roots == nil { @@ -287,31 +281,12 @@ func (fs *autoUnionFs) Symlink(pointedTo string, linkName string, context *fuse. return fuse.EPERM } -func (fs *autoUnionFs) SetDebug(b bool) { - // TODO(hanwen): this should use locking. - fs.debug = b - fs.nodeFs.SetDebug(b) - - conn := fs.nodeFs.Connector() - conn.SetDebug(b) - conn.Server().SetDebug(b) -} - -func (fs *autoUnionFs) hasDebug() bool { - return fs.debug -} - func (fs *autoUnionFs) Unlink(path string, context *fuse.Context) (code fuse.Status) { comps := strings.Split(path, "/") if len(comps) != 2 { return fuse.EPERM } - if comps[0] == _STATUS && comps[1] == _DEBUG_SETTING { - fs.SetDebug(false) - return fuse.OK - } - if comps[0] == _CONFIG && comps[1] != _SCAN_CONFIG { code = fs.rmFs(comps[1]) } else { @@ -322,50 +297,37 @@ func (fs *autoUnionFs) Unlink(path string, context *fuse.Context) (code fuse.Sta // Must define this, because ENOSYS will suspend all GetXAttr calls. func (fs *autoUnionFs) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) { - return nil, fuse.ENODATA + return nil, fuse.ENOATTR } 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, _DEBUG_SETTING) && fs.hasDebug() { - return &fuse.Attr{ - Mode: fuse.S_IFLNK | 0644, - }, 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)) @@ -377,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 } @@ -393,9 +353,6 @@ func (fs *autoUnionFs) StatusDir() (stream []fuse.DirEntry, status fuse.Status) {Name: _DEBUG, Mode: fuse.S_IFREG | 0644}, {Name: _ROOT, Mode: syscall.S_IFLNK | 0644}, } - if fs.hasDebug() { - stream = append(stream, fuse.DirEntry{Name: _DEBUG_SETTING, Mode: fuse.S_IFLNK | 0644}) - } return stream, fuse.OK } diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/autounion_race_test.go b/vendor/github.com/hanwen/go-fuse/unionfs/autounion_race_test.go deleted file mode 100644 index d3e07bb..0000000 --- a/vendor/github.com/hanwen/go-fuse/unionfs/autounion_race_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build !race - -package unionfs - -import ( - "os" - "testing" - "time" -) - -// Ideally, this should check inject a logger rather than a boolean, -// so we can check if the messages are as we expect. Right now, this -// test requires visual inspection. When run with -v, the UNLINK reply -// and SYMLINK request are not printed. -func TestToggleDebug(t *testing.T) { - wd, clean := setup(t) - defer clean() - - os.Remove(wd + "/mnt/status/debug_setting") - time.Sleep(10 * time.Millisecond) - - if err := os.Symlink("xyz", wd+"/mnt/status/debug_setting"); err != nil { - t.Fatalf("Symlink: %v", err) - } - - // now we should be in debug mode. - link, err := os.Readlink(wd + "/mnt/status/debug_setting") - if err != nil { - t.Fatalf("Readlink: %v", err) - } - if link != "1" { - t.Errorf("got %q want %q for debug_setting readlink", - link, "1") - } -} diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/autounion_test.go b/vendor/github.com/hanwen/go-fuse/unionfs/autounion_test.go index ce806c3..f2c554d 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/autounion_test.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/autounion_test.go @@ -1,3 +1,7 @@ +// 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 ( @@ -9,6 +13,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) const entryTtl = 100 * time.Millisecond @@ -19,11 +24,16 @@ var testAOpts = AutoUnionFsOptions{ EntryTimeout: entryTtl, AttrTimeout: entryTtl, NegativeTimeout: 0, + Debug: VerboseTest(), }, HideReadonly: true, Version: "version", } +func init() { + testAOpts.Options.Debug = testutil.VerboseTest() +} + func WriteFile(t *testing.T, name string, contents string) { err := ioutil.WriteFile(name, []byte(contents), 0644) if err != nil { @@ -31,8 +41,8 @@ func WriteFile(t *testing.T, name string, contents string) { } } -func setup(t *testing.T) (workdir string, cleanup func()) { - wd, _ := ioutil.TempDir("", "") +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) @@ -57,17 +67,17 @@ func setup(t *testing.T) (workdir string, cleanup func()) { if err != nil { t.Fatalf("MountNodeFileSystem failed: %v", err) } - fs.SetDebug(VerboseTest()) 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") @@ -80,7 +90,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") @@ -93,7 +103,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) @@ -135,14 +145,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") @@ -152,7 +170,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) @@ -183,7 +201,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) @@ -218,7 +236,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) diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs.go b/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs.go index f61a1cd..7f97e01 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs.go @@ -1,3 +1,7 @@ +// 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 ( diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs_test.go b/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs_test.go index d6511cd..18b47bb 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs_test.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/cachingfs_test.go @@ -1,13 +1,17 @@ +// 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" "os" "syscall" "testing" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) func modeMapEq(m1, m2 map[string]uint32) bool { @@ -25,7 +29,7 @@ func modeMapEq(m1, m2 map[string]uint32) bool { } func TestCachingFs(t *testing.T) { - wd, _ := ioutil.TempDir("", "") + wd := testutil.TempDir() defer os.RemoveAll(wd) fs := pathfs.NewLoopbackFileSystem(wd) diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/create.go b/vendor/github.com/hanwen/go-fuse/unionfs/create.go index a925d77..da65e83 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/create.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/create.go @@ -1,3 +1,7 @@ +// 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 ( diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/dircache.go b/vendor/github.com/hanwen/go-fuse/unionfs/dircache.go index 0f0c4aa..b37b78f 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/dircache.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/dircache.go @@ -1,3 +1,7 @@ +// 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 ( diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/timedcache.go b/vendor/github.com/hanwen/go-fuse/unionfs/timedcache.go index 56e52bf..ab64492 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/timedcache.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/timedcache.go @@ -1,3 +1,7 @@ +// 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 ( diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/timedcache_test.go b/vendor/github.com/hanwen/go-fuse/unionfs/timedcache_test.go index f97a8f1..a1c77d7 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/timedcache_test.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/timedcache_test.go @@ -1,3 +1,7 @@ +// 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 ( diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs.go b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs.go index c6d0200..a82ffff 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs.go @@ -1,3 +1,7 @@ +// 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 ( @@ -566,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) @@ -704,7 +708,7 @@ func (fs *unionFS) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, s func (fs *unionFS) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) { if name == _DROP_CACHE { - return nil, fuse.ENODATA + return nil, fuse.ENOATTR } r := fs.getBranch(name) if r.branch >= 0 { diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test.go b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test.go index 51e514b..4bfb766 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test.go @@ -1,3 +1,7 @@ +// 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 ( @@ -17,6 +21,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) func TestFilePathHash(t *testing.T) { @@ -59,7 +64,7 @@ func setupUfs(t *testing.T) (wd string, cleanup func()) { // Make sure system setting does not affect test. syscall.Umask(0) - wd, _ = ioutil.TempDir("", "unionfs") + wd = testutil.TempDir() err := os.Mkdir(wd+"/mnt", 0700) if err != nil { t.Fatalf("Mkdir failed: %v", err) @@ -90,18 +95,19 @@ func setupUfs(t *testing.T) (wd string, cleanup func()) { AttrTimeout: entryTtl / 2, NegativeTimeout: entryTtl / 2, PortableInodes: true, + Debug: testutil.VerboseTest(), } pathfs := pathfs.NewPathNodeFs(ufs, - &pathfs.PathNodeFsOptions{ClientInodes: true}) - state, conn, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts) + &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) } - conn.SetDebug(VerboseTest()) - state.SetDebug(VerboseTest()) - pathfs.SetDebug(VerboseTest()) go state.Serve() + state.WaitMount() return wd, func() { err := state.Unmount() @@ -239,8 +245,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) } } @@ -1123,7 +1130,7 @@ func newDisappearingFS(fs, nop pathfs.FileSystem) *disappearingFS { func TestUnionFsDisappearing(t *testing.T) { // This init is like setupUfs, but we want access to the // writable Fs. - wd, _ := ioutil.TempDir("", "") + wd := testutil.TempDir() defer os.RemoveAll(wd) err := os.Mkdir(wd+"/mnt", 0700) if err != nil { @@ -1154,6 +1161,7 @@ func TestUnionFsDisappearing(t *testing.T) { EntryTimeout: entryTtl, AttrTimeout: entryTtl, NegativeTimeout: entryTtl, + Debug: testutil.VerboseTest(), } nfs := pathfs.NewPathNodeFs(ufs, nil) @@ -1162,8 +1170,8 @@ func TestUnionFsDisappearing(t *testing.T) { t.Fatalf("MountNodeFileSystem failed: %v", err) } defer state.Unmount() - state.SetDebug(VerboseTest()) go state.Serve() + state.WaitMount() err = ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644) if err != nil { @@ -1189,9 +1197,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 { @@ -1277,41 +1285,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() @@ -1506,10 +1479,7 @@ func TestUnionFSBarf(t *testing.T) { if err := os.Rename(wd+"/rw/dir/file", wd+"/rw/file"); err != nil { t.Fatalf("os.Rename: %v", err) } - - err := os.Rename(wd+"/mnt/file", wd+"/mnt/dir2/file") - if fuse.ToStatus(err) != fuse.ENOENT { - // TODO - this should just succeed? + if err := os.Rename(wd+"/mnt/file", wd+"/mnt/dir2/file"); err != nil { t.Fatalf("os.Rename: %v", err) } } diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test_linux.go b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test_linux.go new file mode 100644 index 0000000..3c922b7 --- /dev/null +++ b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_test_linux.go @@ -0,0 +1,45 @@ +// 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" + "testing" +) + +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)) + } +} diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test.go b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test.go index 99c753d..0353ba2 100644 --- a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test.go +++ b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test.go @@ -1,15 +1,18 @@ +// 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" "os" - "syscall" "testing" "time" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) type TestFS struct { @@ -32,11 +35,11 @@ func (fs *TestFS) GetXAttr(path string, name string, context *fuse.Context) ([]b fs.xattrRead++ return []byte{42}, fuse.OK } - return nil, fuse.ENODATA + return nil, fuse.ENOATTR } func TestXAttrCaching(t *testing.T) { - wd, _ := ioutil.TempDir("", "unionfs") + wd := testutil.TempDir() defer os.RemoveAll(wd) os.Mkdir(wd+"/mnt", 0700) err := os.Mkdir(wd+"/rw", 0700) @@ -59,27 +62,27 @@ func TestXAttrCaching(t *testing.T) { EntryTimeout: entryTtl / 2, AttrTimeout: entryTtl / 2, NegativeTimeout: entryTtl / 2, + Debug: testutil.VerboseTest(), } pathfs := pathfs.NewPathNodeFs(ufs, - &pathfs.PathNodeFsOptions{ClientInodes: true}) + &pathfs.PathNodeFsOptions{ClientInodes: true, + Debug: testutil.VerboseTest()}) - server, conn, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts) + server, _, 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() + 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) } @@ -88,8 +91,24 @@ 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) + } + 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) } @@ -97,7 +116,7 @@ func TestXAttrCaching(t *testing.T) { 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) } diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_darwin.go b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_darwin.go new file mode 100644 index 0000000..e67133a --- /dev/null +++ b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_darwin.go @@ -0,0 +1,37 @@ +// 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 +} diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_linux.go b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_linux.go new file mode 100644 index 0000000..6b2568d --- /dev/null +++ b/vendor/github.com/hanwen/go-fuse/unionfs/unionfs_xattr_test_linux.go @@ -0,0 +1,14 @@ +// 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) +} diff --git a/vendor/github.com/hanwen/go-fuse/unionfs/verbose_test.go b/vendor/github.com/hanwen/go-fuse/unionfs/verbose_test.go deleted file mode 100644 index 1ac3b47..0000000 --- a/vendor/github.com/hanwen/go-fuse/unionfs/verbose_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package unionfs - -import "flag" - -// VerboseTest returns true if the testing framework is run with -v. -func VerboseTest() bool { - flag := flag.Lookup("test.v") - return flag != nil && flag.Value.String() == "true" -} diff --git a/vendor/github.com/hanwen/go-fuse/zipfs/memtree.go b/vendor/github.com/hanwen/go-fuse/zipfs/memtree.go index 9092f25..a2219f4 100644 --- a/vendor/github.com/hanwen/go-fuse/zipfs/memtree.go +++ b/vendor/github.com/hanwen/go-fuse/zipfs/memtree.go @@ -1,3 +1,7 @@ +// 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 zipfs import ( @@ -41,9 +45,6 @@ func (fs *MemTreeFs) String() string { return fs.Name } -func (fs *MemTreeFs) SetDebug(bool) { -} - func (fs *MemTreeFs) Root() nodefs.Node { return fs.root } diff --git a/vendor/github.com/hanwen/go-fuse/zipfs/multizip.go b/vendor/github.com/hanwen/go-fuse/zipfs/multizip.go index a5a945d..040c113 100644 --- a/vendor/github.com/hanwen/go-fuse/zipfs/multizip.go +++ b/vendor/github.com/hanwen/go-fuse/zipfs/multizip.go @@ -1,3 +1,7 @@ +// 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 zipfs /* @@ -83,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 @@ -133,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() diff --git a/vendor/github.com/hanwen/go-fuse/zipfs/multizip_test.go b/vendor/github.com/hanwen/go-fuse/zipfs/multizip_test.go index 71fb2ac..0023aa6 100644 --- a/vendor/github.com/hanwen/go-fuse/zipfs/multizip_test.go +++ b/vendor/github.com/hanwen/go-fuse/zipfs/multizip_test.go @@ -1,47 +1,47 @@ +// 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 zipfs import ( - "flag" "io/ioutil" "os" "testing" "time" + "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" + "github.com/hanwen/go-fuse/internal/testutil" ) -// VerboseTest returns true if the testing framework is run with -v. -func VerboseTest() bool { - flag := flag.Lookup("test.v") - return flag != nil && flag.Value.String() == "true" -} - 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("", "") + 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) } - state.SetDebug(VerboseTest()) 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") @@ -56,7 +56,7 @@ func TestMultiZipReadonly(t *testing.T) { } func TestMultiZipFs(t *testing.T) { - mountPoint, cleanup := setupMzfs(t) + mountPoint, server, cleanup := setupMzfs(t) defer cleanup() zipFile := testZipFile() @@ -118,8 +118,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) + } + } } + } diff --git a/vendor/github.com/hanwen/go-fuse/zipfs/tarfs.go b/vendor/github.com/hanwen/go-fuse/zipfs/tarfs.go index 22df910..1924036 100644 --- a/vendor/github.com/hanwen/go-fuse/zipfs/tarfs.go +++ b/vendor/github.com/hanwen/go-fuse/zipfs/tarfs.go @@ -1,3 +1,7 @@ +// 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 zipfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/zipfs/zipfs.go b/vendor/github.com/hanwen/go-fuse/zipfs/zipfs.go index cfc04dc..c2a13b2 100644 --- a/vendor/github.com/hanwen/go-fuse/zipfs/zipfs.go +++ b/vendor/github.com/hanwen/go-fuse/zipfs/zipfs.go @@ -1,3 +1,7 @@ +// 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 zipfs import ( diff --git a/vendor/github.com/hanwen/go-fuse/zipfs/zipfs_test.go b/vendor/github.com/hanwen/go-fuse/zipfs/zipfs_test.go index ee26d2a..8574619 100644 --- a/vendor/github.com/hanwen/go-fuse/zipfs/zipfs_test.go +++ b/vendor/github.com/hanwen/go-fuse/zipfs/zipfs_test.go @@ -1,3 +1,7 @@ +// 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 zipfs import ( @@ -9,6 +13,7 @@ import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" + "github.com/hanwen/go-fuse/internal/testutil" ) func testZipFile() string { @@ -26,11 +31,13 @@ func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) { t.Fatalf("NewArchiveFileSystem failed: %v", err) } - mountPoint, _ = ioutil.TempDir("", "") - state, _, err := nodefs.MountRoot(mountPoint, root, nil) + mountPoint = testutil.TempDir() + state, _, err := nodefs.MountRoot(mountPoint, root, &nodefs.Options{ + Debug: testutil.VerboseTest(), + }) - state.SetDebug(VerboseTest()) go state.Serve() + state.WaitMount() return mountPoint, func() { state.Unmount()