mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-15 18:19:12 -06:00
vendor github.com/hanwen/go-fuse (#2245)
This commit is contained in:
14
vendor/github.com/hanwen/go-fuse/.gitignore
generated
vendored
Normal file
14
vendor/github.com/hanwen/go-fuse/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
*~
|
||||
*.6
|
||||
8.out
|
||||
.nfs*
|
||||
_*
|
||||
6.out
|
||||
#*
|
||||
example/hello/hello
|
||||
example/unionfs/unionfs
|
||||
example/autounionfs/autounionfs
|
||||
example/loopback/loopback
|
||||
example/multizip/multizip
|
||||
example/bulkstat/bulkstat
|
||||
example/zipfs/zipfs
|
||||
2
vendor/github.com/hanwen/go-fuse/.version
generated
vendored
Normal file
2
vendor/github.com/hanwen/go-fuse/.version
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
https://github.com/hanwen/go-fuse
|
||||
ac3dcb75c6f00809911236629fe92af66b02dd35
|
||||
22
vendor/github.com/hanwen/go-fuse/CONTRIBUTING
generated
vendored
Normal file
22
vendor/github.com/hanwen/go-fuse/CONTRIBUTING
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
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
|
||||
30
vendor/github.com/hanwen/go-fuse/LICENSE
generated
vendored
Normal file
30
vendor/github.com/hanwen/go-fuse/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// New BSD License
|
||||
//
|
||||
// Copyright (c) 2010 Ivan Krasin (imkrasin@gmail.com). All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Ivan Krasin nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
150
vendor/github.com/hanwen/go-fuse/README
generated
vendored
Normal file
150
vendor/github.com/hanwen/go-fuse/README
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
|
||||
GO-FUSE: native bindings for the FUSE kernel module.
|
||||
|
||||
|
||||
HIGHLIGHTS
|
||||
|
||||
* High speed: as fast as libfuse using the gc compiler for single
|
||||
threaded loads.
|
||||
|
||||
* Supports in-process mounting of different FileSystems onto
|
||||
subdirectories of the FUSE mount.
|
||||
|
||||
* Supports 3 interfaces for writing filesystems:
|
||||
- PathFileSystem: define filesystems in terms path names.
|
||||
- NodeFileSystem: define filesystems in terms of inodes.
|
||||
- RawFileSystem: define filesystems in terms of FUSE's raw
|
||||
wire protocol.
|
||||
|
||||
* Both NodeFileSystem and PathFileSystem support manipulation of true
|
||||
hardlinks.
|
||||
|
||||
* Includes two fleshed out examples, zipfs and unionfs.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
|
||||
* examples/hello/hello.go contains a 60-line "hello world" filesystem
|
||||
|
||||
* zipfs/zipfs.go contains a small and simple read-only filesystem for
|
||||
zip and tar files. The corresponding command is in example/zipfs/
|
||||
For example,
|
||||
|
||||
mkdir /tmp/mountpoint
|
||||
example/zipfs/zipfs /tmp/mountpoint file.zip &
|
||||
ls /tmp/mountpoint
|
||||
fusermount -u /tmp/mountpoint
|
||||
|
||||
* zipfs/multizipfs.go shows how to use in-process mounts to
|
||||
combine multiple Go-FUSE filesystems into a larger filesystem.
|
||||
|
||||
* fuse/loopback.go mounts another piece of the filesystem.
|
||||
Functionally, it is similar to a symlink. A binary to run is in
|
||||
example/loopback/ . For example
|
||||
|
||||
mkdir /tmp/mountpoint
|
||||
example/loopback/loopback -debug /tmp/mountpoint /some/other/directory &
|
||||
ls /tmp/mountpoint
|
||||
fusermount -u /tmp/mountpoint
|
||||
|
||||
* unionfs/unionfs.go: implements a union mount using 1 R/W branch, and
|
||||
multiple R/O branches.
|
||||
|
||||
mkdir -p /tmp/mountpoint /tmp/writable
|
||||
example/unionfs/unionfs /tmp/mountpoint /tmp/writable /usr &
|
||||
ls /tmp/mountpoint
|
||||
ls -l /tmp/mountpoint/bin/vi
|
||||
rm /tmp/mountpoint/bin/vi
|
||||
ls -l /tmp/mountpoint/bin/vi
|
||||
cat /tmp/writable/*DELETION*/*
|
||||
|
||||
* union/autounionfs.go: creates UnionFs mounts automatically based on
|
||||
existence of READONLY symlinks.
|
||||
|
||||
|
||||
Tested on:
|
||||
|
||||
- x86 32bits (Fedora 14).
|
||||
- x86 64bits (Ubuntu Lucid).
|
||||
|
||||
|
||||
BENCHMARKS
|
||||
|
||||
We use threaded stats over a read-only filesystem for benchmarking.
|
||||
Automated code is under benchmark/ directory. A simple C version of
|
||||
the same FS gives a FUSE baseline
|
||||
|
||||
Data points (Go-FUSE version May 2012), 1000 files, high level
|
||||
interface, all kernel caching turned off, median stat time:
|
||||
|
||||
platform libfuse Go-FUSE difference (%)
|
||||
|
||||
Lenovo T60/Fedora16 (1cpu) 349us 355us 2% slower
|
||||
Lenovo T400/Lucid (1cpu) 138us 140us 5% slower
|
||||
Dell T3500/Lucid (1cpu) 72us 76us 5% slower
|
||||
|
||||
On T60, for each file we have
|
||||
- Client side latency is 360us
|
||||
- 106us of this is server side latency (4.5x lookup 23us, 1x getattr 4us)
|
||||
- 16.5us is due to latency measurements.
|
||||
- 3us is due to garbage collection.
|
||||
|
||||
|
||||
|
||||
MACOS SUPPORT
|
||||
|
||||
go-fuse works somewhat on OSX. Known limitations:
|
||||
|
||||
* All of the limitations of OSXFUSE, including lack of support for
|
||||
NOTIFY.
|
||||
|
||||
* OSX issues STATFS calls continuously (leading to performance
|
||||
concerns).
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
CREDITS
|
||||
|
||||
* Inspired by Taru Karttunen's package, https://bitbucket.org/taruti/go-extra.
|
||||
|
||||
* Originally based on Ivan Krasin's https://github.com/krasin/go-fuse-zip
|
||||
|
||||
|
||||
BUGS
|
||||
|
||||
Yes, probably. Report them through
|
||||
https://github.com/hanwen/go-fuse/issues
|
||||
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This is not an official Google product.
|
||||
|
||||
|
||||
KNOWN PROBLEMS
|
||||
|
||||
Grep source code for TODO. Major topics:
|
||||
|
||||
* Support for umask in Create
|
||||
|
||||
* Missing support for network FS file locking: FUSE_GETLK, FUSE_SETLK,
|
||||
FUSE_SETLKW
|
||||
|
||||
* Missing support for FUSE_INTERRUPT, CUSE, BMAP, POLL, IOCTL
|
||||
|
||||
* In the path API, renames are racy; See also:
|
||||
|
||||
http://sourceforge.net/mailarchive/message.php?msg_id=27550667
|
||||
|
||||
Don't use the path API if you care about correctness.
|
||||
|
||||
|
||||
LICENSE
|
||||
|
||||
Like Go, this library is distributed under the new BSD license. See
|
||||
accompanying LICENSE file.
|
||||
35
vendor/github.com/hanwen/go-fuse/all.bash
generated
vendored
Executable file
35
vendor/github.com/hanwen/go-fuse/all.bash
generated
vendored
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/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 \
|
||||
example/multizip example/unionfs example/memfs \
|
||||
example/autounionfs ; \
|
||||
do
|
||||
if test "${target}" = "install" && test "${d}" = "fuse/test"; then
|
||||
continue
|
||||
fi
|
||||
echo "go ${target} github.com/hanwen/go-fuse/${d}"
|
||||
go ${target} github.com/hanwen/go-fuse/${d}
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
make -C benchmark
|
||||
for d in benchmark
|
||||
do
|
||||
go test github.com/hanwen/go-fuse/benchmark -test.bench '.*' -test.cpu 1,2
|
||||
done
|
||||
2
vendor/github.com/hanwen/go-fuse/benchmark/Makefile
generated
vendored
Normal file
2
vendor/github.com/hanwen/go-fuse/benchmark/Makefile
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
cstatfs: statfs.cc
|
||||
g++ -O2 -Wall -std=c++0x $< `pkg-config fuse --cflags --libs` -o $@
|
||||
136
vendor/github.com/hanwen/go-fuse/benchmark/benchmark.go
generated
vendored
Normal file
136
vendor/github.com/hanwen/go-fuse/benchmark/benchmark.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
package benchmark
|
||||
|
||||
// Routines for benchmarking fuse.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
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))
|
||||
}
|
||||
52
vendor/github.com/hanwen/go-fuse/benchmark/latencymap.go
generated
vendored
Normal file
52
vendor/github.com/hanwen/go-fuse/benchmark/latencymap.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package benchmark
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type latencyMapEntry struct {
|
||||
count int
|
||||
dur time.Duration
|
||||
}
|
||||
|
||||
type LatencyMap struct {
|
||||
sync.Mutex
|
||||
stats map[string]*latencyMapEntry
|
||||
}
|
||||
|
||||
func NewLatencyMap() *LatencyMap {
|
||||
m := &LatencyMap{}
|
||||
m.stats = make(map[string]*latencyMapEntry)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *LatencyMap) Get(name string) (count int, dt time.Duration) {
|
||||
m.Mutex.Lock()
|
||||
l := m.stats[name]
|
||||
m.Mutex.Unlock()
|
||||
return l.count, l.dur
|
||||
}
|
||||
|
||||
func (m *LatencyMap) Add(name string, dt time.Duration) {
|
||||
m.Mutex.Lock()
|
||||
e := m.stats[name]
|
||||
if e == nil {
|
||||
e = new(latencyMapEntry)
|
||||
m.stats[name] = e
|
||||
}
|
||||
e.count++
|
||||
e.dur += dt
|
||||
m.Mutex.Unlock()
|
||||
}
|
||||
|
||||
func (m *LatencyMap) Counts() map[string]int {
|
||||
r := make(map[string]int)
|
||||
m.Mutex.Lock()
|
||||
for k, v := range m.stats {
|
||||
r[k] = v.count
|
||||
}
|
||||
m.Mutex.Unlock()
|
||||
|
||||
return r
|
||||
}
|
||||
16
vendor/github.com/hanwen/go-fuse/benchmark/latencymap_test.go
generated
vendored
Normal file
16
vendor/github.com/hanwen/go-fuse/benchmark/latencymap_test.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package benchmark
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLatencyMap(t *testing.T) {
|
||||
m := NewLatencyMap()
|
||||
m.Add("foo", 100*time.Millisecond)
|
||||
m.Add("foo", 200*time.Millisecond)
|
||||
c, d := m.Get("foo")
|
||||
if c != 2 || d != 300*time.Millisecond {
|
||||
t.Errorf("got %v, %d, want 2, 150ms", c, d)
|
||||
}
|
||||
}
|
||||
215
vendor/github.com/hanwen/go-fuse/benchmark/stat_test.go
generated
vendored
Normal file
215
vendor/github.com/hanwen/go-fuse/benchmark/stat_test.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
package benchmark
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||
)
|
||||
|
||||
func setupFs(fs pathfs.FileSystem) (string, func()) {
|
||||
opts := &nodefs.Options{
|
||||
EntryTimeout: 0.0,
|
||||
AttrTimeout: 0.0,
|
||||
NegativeTimeout: 0.0,
|
||||
}
|
||||
mountPoint, _ := ioutil.TempDir("", "stat_test")
|
||||
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)
|
||||
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)
|
||||
|
||||
err := state.Unmount()
|
||||
if err != nil {
|
||||
log.Println("error during unmount", err)
|
||||
} else {
|
||||
os.RemoveAll(mountPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStatFs(t *testing.T) {
|
||||
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)
|
||||
defer clean()
|
||||
|
||||
names, err := ioutil.ReadDir(wd)
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
if len(names) != 2 {
|
||||
t.Error("readdir /", names)
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(wd + "/sub")
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
t.Error("mode", fi)
|
||||
}
|
||||
names, err = ioutil.ReadDir(wd + "/sub")
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
if len(names) != 2 {
|
||||
t.Error("readdir /sub", names)
|
||||
}
|
||||
names, err = ioutil.ReadDir(wd + "/sub/dir")
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
if len(names) != 2 {
|
||||
t.Error("readdir /sub/dir", names)
|
||||
}
|
||||
|
||||
fi, err = os.Lstat(wd + "/sub/marine.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
if fi.Mode()&os.ModeType != 0 {
|
||||
t.Error("mode", fi)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGoFuseThreadedStat(b *testing.B) {
|
||||
b.StopTimer()
|
||||
fs := NewStatFs()
|
||||
fs.delay = delay
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
files := ReadLines(wd + "/testpaths.txt")
|
||||
for _, fn := range files {
|
||||
fs.AddFile(fn)
|
||||
}
|
||||
|
||||
wd, clean := setupFs(fs)
|
||||
defer clean()
|
||||
|
||||
for i, l := range files {
|
||||
files[i] = filepath.Join(wd, l)
|
||||
}
|
||||
|
||||
threads := runtime.GOMAXPROCS(0)
|
||||
results := TestingBOnePass(b, threads, files)
|
||||
AnalyzeBenchmarkRuns(fmt.Sprintf("Go-FUSE %d CPUs", threads), results)
|
||||
}
|
||||
|
||||
func TestingBOnePass(b *testing.B, threads int, files []string) (results []float64) {
|
||||
runtime.GC()
|
||||
var before, after runtime.MemStats
|
||||
runtime.ReadMemStats(&before)
|
||||
|
||||
todo := b.N
|
||||
for todo > 0 {
|
||||
if len(files) > todo {
|
||||
files = files[:todo]
|
||||
}
|
||||
b.StartTimer()
|
||||
result := BulkStat(threads, files)
|
||||
todo -= len(files)
|
||||
b.StopTimer()
|
||||
results = append(results, result)
|
||||
}
|
||||
runtime.ReadMemStats(&after)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Add this so we can estimate impact on latency numbers.
|
||||
func BenchmarkTimeNow(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCFuseThreadedStat(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
lines := ReadLines(wd + "/testpaths.txt")
|
||||
unique := map[string]int{}
|
||||
for _, l := range lines {
|
||||
unique[l] = 1
|
||||
dir, _ := filepath.Split(l)
|
||||
for dir != "/" && dir != "" {
|
||||
unique[dir] = 1
|
||||
dir = filepath.Clean(dir)
|
||||
dir, _ = filepath.Split(dir)
|
||||
}
|
||||
}
|
||||
|
||||
out := []string{}
|
||||
for k := range unique {
|
||||
out = append(out, k)
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
b.Fatalf("failed: %v", err)
|
||||
}
|
||||
sort.Strings(out)
|
||||
for _, k := range out {
|
||||
f.Write([]byte(fmt.Sprintf("/%s\n", k)))
|
||||
}
|
||||
f.Close()
|
||||
|
||||
mountPoint, _ := ioutil.TempDir("", "stat_test")
|
||||
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))
|
||||
cmd.Start()
|
||||
|
||||
bin, err := exec.LookPath("fusermount")
|
||||
if err != nil {
|
||||
b.Fatalf("failed: %v", err)
|
||||
}
|
||||
stop := exec.Command(bin, "-u", mountPoint)
|
||||
if err != nil {
|
||||
b.Fatalf("failed: %v", err)
|
||||
}
|
||||
defer stop.Run()
|
||||
|
||||
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)
|
||||
}
|
||||
99
vendor/github.com/hanwen/go-fuse/benchmark/statfs.cc
generated
vendored
Normal file
99
vendor/github.com/hanwen/go-fuse/benchmark/statfs.cc
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// +build !cgo
|
||||
// g++ -Wall `pkg-config fuse --cflags --libs` statfs.cc -o statfs
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
using std::unordered_map;
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
|
||||
extern "C" {
|
||||
#include <fuse.h>
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
useconds_t delay_usec;
|
||||
|
||||
class StatFs {
|
||||
public:
|
||||
void readFrom(const string& fn);
|
||||
unordered_map<string, bool> is_dir_;
|
||||
|
||||
int GetAttr(const char *name, struct stat *statbuf) {
|
||||
if (strcmp(name, "/") == 0) {
|
||||
statbuf->st_mode = S_IFDIR | 0777;
|
||||
return 0;
|
||||
}
|
||||
unordered_map<string, bool>::const_iterator it(is_dir_.find(name));
|
||||
if (it == is_dir_.end()) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (it->second) {
|
||||
statbuf->st_mode = S_IFDIR | 0777;
|
||||
} else {
|
||||
statbuf->st_nlink = 1;
|
||||
statbuf->st_mode = S_IFREG | 0666;
|
||||
}
|
||||
|
||||
if (delay_usec > 0) {
|
||||
usleep(delay_usec);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
StatFs *global;
|
||||
int global_getattr(const char *name, struct stat *statbuf) {
|
||||
return global->GetAttr(name, statbuf);
|
||||
}
|
||||
|
||||
void StatFs::readFrom(const string& fn) {
|
||||
FILE *f = fopen(fn.c_str(), "r");
|
||||
|
||||
char line[1024];
|
||||
while (char *s = fgets(line, sizeof(line), f)) {
|
||||
int l = strlen(s);
|
||||
if (line[l-1] == '\n') {
|
||||
line[l-1] = '\0';
|
||||
l--;
|
||||
}
|
||||
bool is_dir = line[l-1] == '/';
|
||||
if (is_dir) {
|
||||
line[l-1] = '\0';
|
||||
}
|
||||
is_dir_[line] = is_dir;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
global = new StatFs;
|
||||
// don't want to know about fuselib's option handling
|
||||
char *in = getenv("STATFS_INPUT");
|
||||
if (!in || !*in) {
|
||||
fprintf(stderr, "pass file in $STATFS_INPUT\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
global->readFrom(in);
|
||||
in = getenv("STATFS_DELAY_USEC");
|
||||
if (in != NULL) {
|
||||
delay_usec = atoi(in);
|
||||
}
|
||||
|
||||
struct fuse_operations statfs_oper = {0};
|
||||
statfs_oper.getattr = &global_getattr;
|
||||
return fuse_main(argc, argv, &statfs_oper, NULL);
|
||||
}
|
||||
69
vendor/github.com/hanwen/go-fuse/benchmark/statfs.go
generated
vendored
Normal file
69
vendor/github.com/hanwen/go-fuse/benchmark/statfs.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
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 {
|
||||
pathfs.FileSystem
|
||||
entries map[string]*fuse.Attr
|
||||
dirs map[string][]fuse.DirEntry
|
||||
delay time.Duration
|
||||
}
|
||||
|
||||
func (me *StatFs) Add(name string, a *fuse.Attr) {
|
||||
name = strings.TrimRight(name, "/")
|
||||
_, ok := me.entries[name]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
me.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})
|
||||
}
|
||||
|
||||
func (me *StatFs) AddFile(name string) {
|
||||
me.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]
|
||||
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]
|
||||
if entries == nil {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
return entries, fuse.OK
|
||||
}
|
||||
|
||||
func NewStatFs() *StatFs {
|
||||
return &StatFs{
|
||||
FileSystem: pathfs.NewDefaultFileSystem(),
|
||||
entries: make(map[string]*fuse.Attr),
|
||||
dirs: make(map[string][]fuse.DirEntry),
|
||||
}
|
||||
}
|
||||
1000
vendor/github.com/hanwen/go-fuse/benchmark/testpaths.txt
generated
vendored
Normal file
1000
vendor/github.com/hanwen/go-fuse/benchmark/testpaths.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
70
vendor/github.com/hanwen/go-fuse/example/autounionfs/main.go
generated
vendored
Normal file
70
vendor/github.com/hanwen/go-fuse/example/autounionfs/main.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"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/unionfs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
debug := flag.Bool("debug", false, "debug on")
|
||||
hardlinks := flag.Bool("hardlinks", false, "support hardlinks")
|
||||
delcache_ttl := flag.Float64("deletion_cache_ttl", 5.0, "Deletion cache TTL in seconds.")
|
||||
branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.")
|
||||
deldirname := flag.String(
|
||||
"deletion_dirname", "GOUNIONFS_DELETIONS", "Directory name to use for deletions.")
|
||||
hide_readonly_link := flag.Bool("hide_readonly_link", true,
|
||||
"Hides READONLY link from the top mountpoints. "+
|
||||
"Enabled by default.")
|
||||
portableInodes := flag.Bool("portable-inodes", false,
|
||||
"Use sequential 32-bit inode numbers.")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if len(flag.Args()) < 2 {
|
||||
fmt.Println("Usage:\n main MOUNTPOINT BASEDIR")
|
||||
os.Exit(2)
|
||||
}
|
||||
ufsOptions := unionfs.UnionFsOptions{
|
||||
DeletionCacheTTL: time.Duration(*delcache_ttl * float64(time.Second)),
|
||||
BranchCacheTTL: time.Duration(*branchcache_ttl * float64(time.Second)),
|
||||
DeletionDirName: *deldirname,
|
||||
}
|
||||
options := unionfs.AutoUnionFsOptions{
|
||||
UnionFsOptions: ufsOptions,
|
||||
Options: nodefs.Options{
|
||||
EntryTimeout: time.Second,
|
||||
AttrTimeout: time.Second,
|
||||
NegativeTimeout: time.Second,
|
||||
Owner: fuse.CurrentOwner(),
|
||||
Debug: *debug,
|
||||
},
|
||||
UpdateOnMount: true,
|
||||
PathNodeFsOptions: pathfs.PathNodeFsOptions{
|
||||
ClientInodes: *hardlinks,
|
||||
},
|
||||
HideReadonly: *hide_readonly_link,
|
||||
}
|
||||
fsOpts := nodefs.Options{
|
||||
PortableInodes: *portableInodes,
|
||||
Debug: *debug,
|
||||
}
|
||||
gofs := unionfs.NewAutoUnionFs(flag.Arg(1), options)
|
||||
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)
|
||||
}
|
||||
|
||||
state.Serve()
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
76
vendor/github.com/hanwen/go-fuse/example/benchmark.sh
generated
vendored
Normal file
76
vendor/github.com/hanwen/go-fuse/example/benchmark.sh
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Runtime is typically dominated by the costs of GetAttr (ie. Stat),
|
||||
# so let's time that. We use zipfs which runs from memory to minimize
|
||||
# noise due to the filesystem itself.
|
||||
|
||||
if [ "$1" == "" ] ; then
|
||||
echo "Usage: benchmark.sh ZIPFILE"
|
||||
echo "The zipfile should be large (> 10000 files)."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
set -eux
|
||||
|
||||
ZIPFILE=$1
|
||||
shift
|
||||
CPU_COUNT=$(grep '^processor' /proc/cpuinfo | wc -l)
|
||||
export GOMAXPROCS=${CPU_COUNT}
|
||||
|
||||
DELAY=5
|
||||
|
||||
gomake -C zipfs
|
||||
gomake -C bulkstat
|
||||
|
||||
MP=/tmp/zipbench
|
||||
fusermount -u ${MP} || true
|
||||
mkdir -p ${MP}
|
||||
|
||||
ZIPFS=$PWD/zipfs/zipfs
|
||||
BULKSTAT="$PWD/bulkstat/bulkstat -threads ${CPU_COUNT}"
|
||||
|
||||
cd /tmp
|
||||
|
||||
${ZIPFS} ${MP} ${ZIPFILE} >& zipfs.log &
|
||||
|
||||
|
||||
# Wait for FS to mount.
|
||||
sleep ${DELAY}
|
||||
find ${MP} > /tmp/zipfiles.txt
|
||||
fusermount -u ${MP}
|
||||
|
||||
# Run vanilla: block box measurement.
|
||||
${ZIPFS} ${MP} ${ZIPFILE} >& zipfs.log &
|
||||
|
||||
# Wait for zipfs to unpack and serve the file.
|
||||
sleep ${DELAY}
|
||||
|
||||
# Performance number without 6prof running
|
||||
echo -e "\n\n"
|
||||
${BULKSTAT} -runs 5 /tmp/zipfiles.txt
|
||||
echo -e "\n\n"
|
||||
|
||||
# Run 6prof
|
||||
6prof -p $! -d 20 -t 3 -hs -l -h -f >& /tmp/zipfs.6prof &
|
||||
sleep 0.1
|
||||
|
||||
# Feed data to 6prof
|
||||
${BULKSTAT} -runs 3 /tmp/zipfiles.txt
|
||||
|
||||
echo -e "\n\n"
|
||||
|
||||
fusermount -u ${MP}
|
||||
|
||||
# Now run with internal monitoring.
|
||||
${ZIPFS} -latencies ${MP} ${ZIPFILE} >& zipfs.log &
|
||||
|
||||
sleep ${DELAY}
|
||||
|
||||
# Measurements.
|
||||
${BULKSTAT} -runs 5 /tmp/zipfiles.txt
|
||||
|
||||
# Dump internal measurements.
|
||||
cat ${MP}/.debug/*
|
||||
|
||||
|
||||
|
||||
61
vendor/github.com/hanwen/go-fuse/example/hello/main.go
generated
vendored
Normal file
61
vendor/github.com/hanwen/go-fuse/example/hello/main.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// A Go mirror of libfuse's hello.c
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||
)
|
||||
|
||||
type HelloFs struct {
|
||||
pathfs.FileSystem
|
||||
}
|
||||
|
||||
func (me *HelloFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
switch name {
|
||||
case "file.txt":
|
||||
return &fuse.Attr{
|
||||
Mode: fuse.S_IFREG | 0644, Size: uint64(len(name)),
|
||||
}, fuse.OK
|
||||
case "":
|
||||
return &fuse.Attr{
|
||||
Mode: fuse.S_IFDIR | 0755,
|
||||
}, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (me *HelloFs) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
|
||||
if name == "" {
|
||||
c = []fuse.DirEntry{{Name: "file.txt", Mode: fuse.S_IFREG}}
|
||||
return c, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (me *HelloFs) Open(name string, flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
if name != "file.txt" {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
if flags&fuse.O_ANYWRITE != 0 {
|
||||
return nil, fuse.EPERM
|
||||
}
|
||||
return nodefs.NewDataFile([]byte(name)), fuse.OK
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if len(flag.Args()) < 1 {
|
||||
log.Fatal("Usage:\n hello MOUNTPOINT")
|
||||
}
|
||||
nfs := pathfs.NewPathNodeFs(&HelloFs{FileSystem: pathfs.NewDefaultFileSystem()}, nil)
|
||||
server, _, err := nodefs.MountRoot(flag.Arg(0), nfs.Root(), nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Mount fail: %v\n", err)
|
||||
}
|
||||
server.Serve()
|
||||
}
|
||||
109
vendor/github.com/hanwen/go-fuse/example/loopback/main.go
generated
vendored
Normal file
109
vendor/github.com/hanwen/go-fuse/example/loopback/main.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Mounts another directory as loopback for testing and benchmarking
|
||||
// purposes.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime/pprof"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"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
|
||||
debug := flag.Bool("debug", false, "print debugging messages.")
|
||||
other := flag.Bool("allow-other", false, "mount with -o allowother.")
|
||||
enableLinks := flag.Bool("l", false, "Enable hard link support")
|
||||
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to this file")
|
||||
memprofile := flag.String("memprofile", "", "write memory profile to this file")
|
||||
flag.Parse()
|
||||
if flag.NArg() < 2 {
|
||||
fmt.Printf("usage: %s MOUNTPOINT ORIGINAL\n", path.Base(os.Args[0]))
|
||||
fmt.Printf("\noptions:\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
if *cpuprofile != "" {
|
||||
fmt.Printf("Writing cpu profile to %s\n", *cpuprofile)
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(3)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
if *memprofile != "" {
|
||||
log.Printf("send SIGUSR1 to %d to dump memory profile", os.Getpid())
|
||||
profSig := make(chan os.Signal, 1)
|
||||
signal.Notify(profSig, syscall.SIGUSR1)
|
||||
go writeMemProfile(*memprofile, profSig)
|
||||
}
|
||||
if *cpuprofile != "" || *memprofile != "" {
|
||||
fmt.Printf("Note: You must unmount gracefully, otherwise the profile file(s) will stay empty!\n")
|
||||
}
|
||||
|
||||
var finalFs pathfs.FileSystem
|
||||
orig := flag.Arg(1)
|
||||
loopbackfs := pathfs.NewLoopbackFileSystem(orig)
|
||||
finalFs = loopbackfs
|
||||
|
||||
opts := &nodefs.Options{
|
||||
// These options are to be compatible with libfuse defaults,
|
||||
// making benchmarking easier.
|
||||
NegativeTimeout: time.Second,
|
||||
AttrTimeout: time.Second,
|
||||
EntryTimeout: time.Second,
|
||||
}
|
||||
// Enable ClientInodes so hard links work
|
||||
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: *enableLinks}
|
||||
pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts)
|
||||
conn := nodefs.NewFileSystemConnector(pathFs.Root(), opts)
|
||||
mountPoint := flag.Arg(0)
|
||||
origAbs, _ := filepath.Abs(orig)
|
||||
mOpts := &fuse.MountOptions{
|
||||
AllowOther: *other,
|
||||
Name: "loopbackfs",
|
||||
FsName: origAbs,
|
||||
Debug: *debug,
|
||||
}
|
||||
state, err := fuse.NewServer(conn.RawFS(), mountPoint, mOpts)
|
||||
if err != nil {
|
||||
fmt.Printf("Mount fail: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Mounted!")
|
||||
state.Serve()
|
||||
}
|
||||
37
vendor/github.com/hanwen/go-fuse/example/memfs/main.go
generated
vendored
Normal file
37
vendor/github.com/hanwen/go-fuse/example/memfs/main.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Mounts MemNodeFs for testing purposes.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Scans the arg list and sets up flags
|
||||
debug := flag.Bool("debug", false, "print debugging messages.")
|
||||
flag.Parse()
|
||||
if flag.NArg() < 2 {
|
||||
// TODO - where to get program name?
|
||||
fmt.Println("usage: main MOUNTPOINT BACKING-PREFIX")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
mountPoint := flag.Arg(0)
|
||||
prefix := flag.Arg(1)
|
||||
root := nodefs.NewMemNodeFSRoot(prefix)
|
||||
conn := nodefs.NewFileSystemConnector(root, 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)
|
||||
}
|
||||
fmt.Println("Mounted!")
|
||||
server.Serve()
|
||||
}
|
||||
35
vendor/github.com/hanwen/go-fuse/example/multizip/main.go
generated
vendored
Normal file
35
vendor/github.com/hanwen/go-fuse/example/multizip/main.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||
"github.com/hanwen/go-fuse/zipfs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Scans the arg list and sets up flags
|
||||
debug := flag.Bool("debug", false, "debug on")
|
||||
flag.Parse()
|
||||
if flag.NArg() < 1 {
|
||||
_, prog := filepath.Split(os.Args[0])
|
||||
fmt.Printf("usage: %s MOUNTPOINT\n", prog)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
fs := zipfs.NewMultiZipFs()
|
||||
nfs := pathfs.NewPathNodeFs(fs, 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.Serve()
|
||||
}
|
||||
81
vendor/github.com/hanwen/go-fuse/example/statfs/main.go
generated
vendored
Normal file
81
vendor/github.com/hanwen/go-fuse/example/statfs/main.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/benchmark"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Scans the arg list and sets up flags
|
||||
debug := flag.Bool("debug", false, "print debugging messages.")
|
||||
profile := flag.String("profile", "", "record cpu profile.")
|
||||
mem_profile := flag.String("mem-profile", "", "record memory profile.")
|
||||
command := flag.String("run", "", "run this command after mounting.")
|
||||
ttl := flag.Float64("ttl", 1.0, "attribute/entry cache TTL.")
|
||||
flag.Parse()
|
||||
if flag.NArg() < 2 {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s MOUNTPOINT FILENAMES-FILE\n", os.Args[0])
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var profFile, memProfFile io.Writer
|
||||
var err error
|
||||
if *profile != "" {
|
||||
profFile, err = os.Create(*profile)
|
||||
if err != nil {
|
||||
log.Fatalf("os.Create: %v", err)
|
||||
}
|
||||
}
|
||||
if *mem_profile != "" {
|
||||
memProfFile, err = os.Create(*mem_profile)
|
||||
if err != nil {
|
||||
log.Fatalf("os.Create: %v", err)
|
||||
}
|
||||
}
|
||||
fs := benchmark.NewStatFs()
|
||||
lines := benchmark.ReadLines(flag.Arg(1))
|
||||
for _, l := range lines {
|
||||
fs.AddFile(l)
|
||||
}
|
||||
nfs := pathfs.NewPathNodeFs(fs, nil)
|
||||
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 {
|
||||
fmt.Printf("Mount fail: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
if profFile != nil {
|
||||
pprof.StartCPUProfile(profFile)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
if *command != "" {
|
||||
args := strings.Split(*command, " ")
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Start()
|
||||
}
|
||||
|
||||
state.Serve()
|
||||
if memProfFile != nil {
|
||||
pprof.WriteHeapProfile(memProfFile)
|
||||
}
|
||||
}
|
||||
58
vendor/github.com/hanwen/go-fuse/example/unionfs/main.go
generated
vendored
Normal file
58
vendor/github.com/hanwen/go-fuse/example/unionfs/main.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||
"github.com/hanwen/go-fuse/unionfs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
debug := flag.Bool("debug", false, "debug on")
|
||||
portable := flag.Bool("portable", false, "use 32 bit inodes")
|
||||
|
||||
entry_ttl := flag.Float64("entry_ttl", 1.0, "fuse entry cache TTL.")
|
||||
negative_ttl := flag.Float64("negative_ttl", 1.0, "fuse negative entry cache TTL.")
|
||||
|
||||
delcache_ttl := flag.Float64("deletion_cache_ttl", 5.0, "Deletion cache TTL in seconds.")
|
||||
branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.")
|
||||
deldirname := flag.String(
|
||||
"deletion_dirname", "GOUNIONFS_DELETIONS", "Directory name to use for deletions.")
|
||||
|
||||
flag.Parse()
|
||||
if len(flag.Args()) < 2 {
|
||||
fmt.Println("Usage:\n unionfs MOUNTPOINT RW-DIRECTORY RO-DIRECTORY ...")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
ufsOptions := unionfs.UnionFsOptions{
|
||||
DeletionCacheTTL: time.Duration(*delcache_ttl * float64(time.Second)),
|
||||
BranchCacheTTL: time.Duration(*branchcache_ttl * float64(time.Second)),
|
||||
DeletionDirName: *deldirname,
|
||||
}
|
||||
|
||||
ufs, err := unionfs.NewUnionFsFromRoots(flag.Args()[1:], &ufsOptions, true)
|
||||
if err != nil {
|
||||
log.Fatal("Cannot create UnionFs", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
nodeFs := pathfs.NewPathNodeFs(ufs, &pathfs.PathNodeFsOptions{ClientInodes: true})
|
||||
mOpts := nodefs.Options{
|
||||
EntryTimeout: time.Duration(*entry_ttl * float64(time.Second)),
|
||||
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.Serve()
|
||||
}
|
||||
81
vendor/github.com/hanwen/go-fuse/example/zipfs/main.go
generated
vendored
Normal file
81
vendor/github.com/hanwen/go-fuse/example/zipfs/main.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/zipfs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Scans the arg list and sets up flags
|
||||
debug := flag.Bool("debug", false, "print debugging messages.")
|
||||
profile := flag.String("profile", "", "record cpu profile.")
|
||||
mem_profile := flag.String("mem-profile", "", "record memory profile.")
|
||||
command := flag.String("run", "", "run this command after mounting.")
|
||||
ttl := flag.Float64("ttl", 1.0, "attribute/entry cache TTL.")
|
||||
flag.Parse()
|
||||
if flag.NArg() < 2 {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s MOUNTPOINT ZIP-FILE\n", os.Args[0])
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var profFile, memProfFile io.Writer
|
||||
var err error
|
||||
if *profile != "" {
|
||||
profFile, err = os.Create(*profile)
|
||||
if err != nil {
|
||||
log.Fatalf("os.Create: %v", err)
|
||||
}
|
||||
}
|
||||
if *mem_profile != "" {
|
||||
memProfFile, err = os.Create(*mem_profile)
|
||||
if err != nil {
|
||||
log.Fatalf("os.Create: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
root, err := zipfs.NewArchiveFileSystem(flag.Arg(1))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NewArchiveFileSystem failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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 {
|
||||
fmt.Printf("Mount fail: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
if profFile != nil {
|
||||
pprof.StartCPUProfile(profFile)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
if *command != "" {
|
||||
args := strings.Split(*command, " ")
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Start()
|
||||
}
|
||||
|
||||
state.Serve()
|
||||
if memProfFile != nil {
|
||||
pprof.WriteHeapProfile(memProfFile)
|
||||
}
|
||||
}
|
||||
1
vendor/github.com/hanwen/go-fuse/fuse/.gitignore
generated
vendored
Normal file
1
vendor/github.com/hanwen/go-fuse/fuse/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
version.gen.go
|
||||
138
vendor/github.com/hanwen/go-fuse/fuse/api.go
generated
vendored
Normal file
138
vendor/github.com/hanwen/go-fuse/fuse/api.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
// 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.
|
||||
|
||||
package fuse
|
||||
|
||||
// Types for users to implement.
|
||||
|
||||
// The result of Read is an array of bytes, but for performance
|
||||
// reasons, we can also return data as a file-descriptor/offset/size
|
||||
// tuple. If the backing store for a file is another filesystem, this
|
||||
// reduces the amount of copying between the kernel and the FUSE
|
||||
// server. The ReadResult interface captures both cases.
|
||||
type ReadResult interface {
|
||||
// Returns the raw bytes for the read, possibly using the
|
||||
// passed buffer. The buffer should be larger than the return
|
||||
// value from Size.
|
||||
Bytes(buf []byte) ([]byte, Status)
|
||||
|
||||
// Size returns how many bytes this return value takes at most.
|
||||
Size() int
|
||||
|
||||
// Done() is called after sending the data to the kernel.
|
||||
Done()
|
||||
}
|
||||
|
||||
type MountOptions struct {
|
||||
AllowOther bool
|
||||
|
||||
// Options are passed as -o string to fusermount.
|
||||
Options []string
|
||||
|
||||
// Default is _DEFAULT_BACKGROUND_TASKS, 12. This numbers
|
||||
// controls the allowed number of requests that relate to
|
||||
// async I/O. Concurrency for synchronous I/O is not limited.
|
||||
MaxBackground int
|
||||
|
||||
// Write size to use. If 0, use default. This number is
|
||||
// capped at the kernel maximum.
|
||||
MaxWrite int
|
||||
|
||||
// If IgnoreSecurityLabels is set, all security related xattr
|
||||
// requests will return NO_DATA without passing through the
|
||||
// user defined filesystem. You should only set this if you
|
||||
// file system implements extended attributes, and you are not
|
||||
// interested in security labels.
|
||||
IgnoreSecurityLabels bool // ignoring labels should be provided as a fusermount mount option.
|
||||
|
||||
// If given, use this buffer pool instead of the global one.
|
||||
Buffers BufferPool
|
||||
|
||||
// If RememberInodes is set, we will never forget inodes.
|
||||
// This may be useful for NFS.
|
||||
RememberInodes bool
|
||||
|
||||
// Values shown in "df -T" and friends
|
||||
// First column, "Filesystem"
|
||||
FsName string
|
||||
// Second column, "Type", will be shown as "fuse." + Name
|
||||
Name string
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Unless you really know what you are doing, you should not implement
|
||||
// this, but rather the FileSystem interface; the details of getting
|
||||
// interactions with open files, renames, and threading right etc. are
|
||||
// somewhat tricky and not very interesting.
|
||||
//
|
||||
// A null implementation is provided by NewDefaultRawFileSystem.
|
||||
type RawFileSystem interface {
|
||||
String() string
|
||||
|
||||
// If called, provide debug output through the log package.
|
||||
SetDebug(debug bool)
|
||||
|
||||
Lookup(header *InHeader, name string, out *EntryOut) (status Status)
|
||||
Forget(nodeid, nlookup uint64)
|
||||
|
||||
// Attributes.
|
||||
GetAttr(input *GetAttrIn, out *AttrOut) (code Status)
|
||||
SetAttr(input *SetAttrIn, out *AttrOut) (code Status)
|
||||
|
||||
// Modifying structure.
|
||||
Mknod(input *MknodIn, name string, out *EntryOut) (code Status)
|
||||
Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status)
|
||||
Unlink(header *InHeader, name string) (code Status)
|
||||
Rmdir(header *InHeader, name string) (code Status)
|
||||
Rename(input *RenameIn, oldName string, newName string) (code Status)
|
||||
Link(input *LinkIn, filename string, out *EntryOut) (code Status)
|
||||
|
||||
Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status)
|
||||
Readlink(header *InHeader) (out []byte, code Status)
|
||||
Access(input *AccessIn) (code Status)
|
||||
|
||||
// Extended attributes.
|
||||
GetXAttrSize(header *InHeader, attr string) (sz int, code Status)
|
||||
GetXAttrData(header *InHeader, attr string) (data []byte, code Status)
|
||||
ListXAttr(header *InHeader) (attributes []byte, code Status)
|
||||
SetXAttr(input *SetXAttrIn, attr string, data []byte) Status
|
||||
RemoveXAttr(header *InHeader, attr string) (code Status)
|
||||
|
||||
// File handling.
|
||||
Create(input *CreateIn, name string, out *CreateOut) (code Status)
|
||||
Open(input *OpenIn, out *OpenOut) (status Status)
|
||||
Read(input *ReadIn, buf []byte) (ReadResult, Status)
|
||||
|
||||
Release(input *ReleaseIn)
|
||||
Write(input *WriteIn, data []byte) (written uint32, code Status)
|
||||
Flush(input *FlushIn) Status
|
||||
Fsync(input *FsyncIn) (code Status)
|
||||
Fallocate(input *FallocateIn) (code Status)
|
||||
|
||||
// Directory handling
|
||||
OpenDir(input *OpenIn, out *OpenOut) (status Status)
|
||||
ReadDir(input *ReadIn, out *DirEntryList) Status
|
||||
ReadDirPlus(input *ReadIn, out *DirEntryList) Status
|
||||
ReleaseDir(input *ReleaseIn)
|
||||
FsyncDir(input *FsyncIn) (code Status)
|
||||
|
||||
//
|
||||
StatFs(input *InHeader, out *StatfsOut) (code Status)
|
||||
|
||||
// This is called on processing the first request. The
|
||||
// filesystem implementation can use the server argument to
|
||||
// talk back to the kernel (through notify methods).
|
||||
Init(*Server)
|
||||
}
|
||||
75
vendor/github.com/hanwen/go-fuse/fuse/attr.go
generated
vendored
Normal file
75
vendor/github.com/hanwen/go-fuse/fuse/attr.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (a *Attr) IsFifo() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFIFO }
|
||||
|
||||
// IsChar reports whether the FileInfo describes a character special file.
|
||||
func (a *Attr) IsChar() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFCHR }
|
||||
|
||||
// IsDir reports whether the FileInfo describes a directory.
|
||||
func (a *Attr) IsDir() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFDIR }
|
||||
|
||||
// IsBlock reports whether the FileInfo describes a block special file.
|
||||
func (a *Attr) IsBlock() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFBLK }
|
||||
|
||||
// IsRegular reports whether the FileInfo describes a regular file.
|
||||
func (a *Attr) IsRegular() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFREG }
|
||||
|
||||
// IsSymlink reports whether the FileInfo describes a symbolic link.
|
||||
func (a *Attr) IsSymlink() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFLNK }
|
||||
|
||||
// IsSocket reports whether the FileInfo describes a socket.
|
||||
func (a *Attr) IsSocket() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFSOCK }
|
||||
|
||||
func (a *Attr) SetTimes(access *time.Time, mod *time.Time, chstatus *time.Time) {
|
||||
if access != nil {
|
||||
a.Atime = uint64(access.Unix())
|
||||
a.Atimensec = uint32(access.Nanosecond())
|
||||
}
|
||||
if mod != nil {
|
||||
a.Mtime = uint64(mod.Unix())
|
||||
a.Mtimensec = uint32(mod.Nanosecond())
|
||||
}
|
||||
if chstatus != nil {
|
||||
a.Ctime = uint64(chstatus.Unix())
|
||||
a.Ctimensec = uint32(chstatus.Nanosecond())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Attr) ChangeTime() time.Time {
|
||||
return time.Unix(int64(a.Ctime), int64(a.Ctimensec))
|
||||
}
|
||||
|
||||
func (a *Attr) AccessTime() time.Time {
|
||||
return time.Unix(int64(a.Atime), int64(a.Atimensec))
|
||||
}
|
||||
|
||||
func (a *Attr) ModTime() time.Time {
|
||||
return time.Unix(int64(a.Mtime), int64(a.Mtimensec))
|
||||
}
|
||||
|
||||
func ToStatT(f os.FileInfo) *syscall.Stat_t {
|
||||
s, _ := f.Sys().(*syscall.Stat_t)
|
||||
if s != nil {
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ToAttr(f os.FileInfo) *Attr {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
s := ToStatT(f)
|
||||
if s != nil {
|
||||
a := &Attr{}
|
||||
a.FromStat(s)
|
||||
return a
|
||||
}
|
||||
return nil
|
||||
}
|
||||
22
vendor/github.com/hanwen/go-fuse/fuse/attr_darwin.go
generated
vendored
Normal file
22
vendor/github.com/hanwen/go-fuse/fuse/attr_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (a *Attr) FromStat(s *syscall.Stat_t) {
|
||||
a.Ino = uint64(s.Ino)
|
||||
a.Size = uint64(s.Size)
|
||||
a.Blocks = uint64(s.Blocks)
|
||||
a.Atime = uint64(s.Atimespec.Sec)
|
||||
a.Atimensec = uint32(s.Atimespec.Nsec)
|
||||
a.Mtime = uint64(s.Mtimespec.Sec)
|
||||
a.Mtimensec = uint32(s.Mtimespec.Nsec)
|
||||
a.Ctime = uint64(s.Ctimespec.Sec)
|
||||
a.Ctimensec = uint32(s.Ctimespec.Nsec)
|
||||
a.Mode = uint32(s.Mode)
|
||||
a.Nlink = uint32(s.Nlink)
|
||||
a.Uid = uint32(s.Uid)
|
||||
a.Gid = uint32(s.Gid)
|
||||
a.Rdev = uint32(s.Rdev)
|
||||
}
|
||||
23
vendor/github.com/hanwen/go-fuse/fuse/attr_linux.go
generated
vendored
Normal file
23
vendor/github.com/hanwen/go-fuse/fuse/attr_linux.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (a *Attr) FromStat(s *syscall.Stat_t) {
|
||||
a.Ino = uint64(s.Ino)
|
||||
a.Size = uint64(s.Size)
|
||||
a.Blocks = uint64(s.Blocks)
|
||||
a.Atime = uint64(s.Atim.Sec)
|
||||
a.Atimensec = uint32(s.Atim.Nsec)
|
||||
a.Mtime = uint64(s.Mtim.Sec)
|
||||
a.Mtimensec = uint32(s.Mtim.Nsec)
|
||||
a.Ctime = uint64(s.Ctim.Sec)
|
||||
a.Ctimensec = uint32(s.Ctim.Nsec)
|
||||
a.Mode = s.Mode
|
||||
a.Nlink = uint32(s.Nlink)
|
||||
a.Uid = uint32(s.Uid)
|
||||
a.Gid = uint32(s.Gid)
|
||||
a.Rdev = uint32(s.Rdev)
|
||||
a.Blksize = uint32(s.Blksize)
|
||||
}
|
||||
95
vendor/github.com/hanwen/go-fuse/fuse/bufferpool.go
generated
vendored
Normal file
95
vendor/github.com/hanwen/go-fuse/fuse/bufferpool.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var paranoia bool
|
||||
|
||||
// BufferPool implements explicit memory management. It is used for
|
||||
// minimizing the GC overhead of communicating with the kernel.
|
||||
type BufferPool interface {
|
||||
// AllocBuffer creates a buffer of at least the given size. After use,
|
||||
// it should be deallocated with FreeBuffer().
|
||||
AllocBuffer(size uint32) []byte
|
||||
|
||||
// FreeBuffer takes back a buffer if it was allocated through
|
||||
// AllocBuffer. It is not an error to call FreeBuffer() on a slice
|
||||
// obtained elsewhere.
|
||||
FreeBuffer(slice []byte)
|
||||
}
|
||||
|
||||
type gcBufferPool struct {
|
||||
}
|
||||
|
||||
// NewGcBufferPool is a fallback to the standard allocation routines.
|
||||
func NewGcBufferPool() BufferPool {
|
||||
return &gcBufferPool{}
|
||||
}
|
||||
|
||||
func (p *gcBufferPool) AllocBuffer(size uint32) []byte {
|
||||
return make([]byte, size)
|
||||
}
|
||||
|
||||
func (p *gcBufferPool) FreeBuffer(slice []byte) {
|
||||
}
|
||||
|
||||
type bufferPoolImpl struct {
|
||||
lock sync.Mutex
|
||||
|
||||
// For each page size multiple a list of slice pointers.
|
||||
buffersBySize []*sync.Pool
|
||||
}
|
||||
|
||||
// NewBufferPool returns a BufferPool implementation that that returns
|
||||
// slices with capacity of a multiple of PAGESIZE, which have possibly
|
||||
// been used, and may contain random contents. When using
|
||||
// NewBufferPool, file system handlers may not hang on to passed-in
|
||||
// buffers beyond the handler's return.
|
||||
func NewBufferPool() BufferPool {
|
||||
bp := new(bufferPoolImpl)
|
||||
return bp
|
||||
}
|
||||
|
||||
func (p *bufferPoolImpl) getPool(pageCount int) *sync.Pool {
|
||||
p.lock.Lock()
|
||||
for len(p.buffersBySize) < pageCount+1 {
|
||||
p.buffersBySize = append(p.buffersBySize, nil)
|
||||
}
|
||||
if p.buffersBySize[pageCount] == nil {
|
||||
p.buffersBySize[pageCount] = &sync.Pool{
|
||||
New: func() interface{} { return make([]byte, PAGESIZE*pageCount) },
|
||||
}
|
||||
}
|
||||
pool := p.buffersBySize[pageCount]
|
||||
p.lock.Unlock()
|
||||
return pool
|
||||
}
|
||||
|
||||
func (p *bufferPoolImpl) AllocBuffer(size uint32) []byte {
|
||||
sz := int(size)
|
||||
if sz < PAGESIZE {
|
||||
sz = PAGESIZE
|
||||
}
|
||||
|
||||
if sz%PAGESIZE != 0 {
|
||||
sz += PAGESIZE
|
||||
}
|
||||
pages := sz / PAGESIZE
|
||||
|
||||
b := p.getPool(pages).Get().([]byte)
|
||||
return b[:size]
|
||||
}
|
||||
|
||||
func (p *bufferPoolImpl) FreeBuffer(slice []byte) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if cap(slice)%PAGESIZE != 0 || cap(slice) == 0 {
|
||||
return
|
||||
}
|
||||
pages := cap(slice) / PAGESIZE
|
||||
slice = slice[:cap(slice)]
|
||||
|
||||
p.getPool(pages).Put(slice)
|
||||
}
|
||||
15
vendor/github.com/hanwen/go-fuse/fuse/bufferpool_test.go
generated
vendored
Normal file
15
vendor/github.com/hanwen/go-fuse/fuse/bufferpool_test.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBufferPool(t *testing.T) {
|
||||
bp := NewBufferPool()
|
||||
size := 1500
|
||||
buf := bp.AllocBuffer(uint32(size))
|
||||
if len(buf) != size {
|
||||
t.Errorf("Expected buffer of %d bytes, got %d bytes", size, len(buf))
|
||||
}
|
||||
bp.FreeBuffer(buf)
|
||||
}
|
||||
31
vendor/github.com/hanwen/go-fuse/fuse/constants.go
generated
vendored
Normal file
31
vendor/github.com/hanwen/go-fuse/fuse/constants.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
FUSE_ROOT_ID = 1
|
||||
|
||||
FUSE_UNKNOWN_INO = 0xffffffff
|
||||
|
||||
CUSE_UNRESTRICTED_IOCTL = (1 << 0)
|
||||
|
||||
FUSE_LK_FLOCK = (1 << 0)
|
||||
|
||||
FUSE_IOCTL_MAX_IOV = 256
|
||||
|
||||
FUSE_POLL_SCHEDULE_NOTIFY = (1 << 0)
|
||||
|
||||
CUSE_INIT_INFO_MAX = 4096
|
||||
|
||||
S_IFDIR = syscall.S_IFDIR
|
||||
S_IFREG = syscall.S_IFREG
|
||||
S_IFLNK = syscall.S_IFLNK
|
||||
S_IFIFO = syscall.S_IFIFO
|
||||
|
||||
CUSE_INIT = 4096
|
||||
|
||||
O_ANYWRITE = uint32(os.O_WRONLY | os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_TRUNC)
|
||||
)
|
||||
5
vendor/github.com/hanwen/go-fuse/fuse/constants_freebsd.go
generated
vendored
Normal file
5
vendor/github.com/hanwen/go-fuse/fuse/constants_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package fuse
|
||||
|
||||
// arbitrary values
|
||||
const syscall_O_LARGEFILE = 1 << 29
|
||||
const syscall_O_NOATIME = 1 << 30
|
||||
8
vendor/github.com/hanwen/go-fuse/fuse/constants_linux.go
generated
vendored
Normal file
8
vendor/github.com/hanwen/go-fuse/fuse/constants_linux.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const syscall_O_LARGEFILE = syscall.O_LARGEFILE
|
||||
const syscall_O_NOATIME = syscall.O_NOATIME
|
||||
148
vendor/github.com/hanwen/go-fuse/fuse/defaultraw.go
generated
vendored
Normal file
148
vendor/github.com/hanwen/go-fuse/fuse/defaultraw.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewDefaultRawFileSystem returns ENOSYS (not implemented) for all
|
||||
// operations.
|
||||
func NewDefaultRawFileSystem() RawFileSystem {
|
||||
return (*defaultRawFileSystem)(nil)
|
||||
}
|
||||
|
||||
type defaultRawFileSystem struct{}
|
||||
|
||||
func (fs *defaultRawFileSystem) Init(*Server) {
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) String() string {
|
||||
return os.Args[0]
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) SetDebug(dbg bool) {
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) StatFs(header *InHeader, out *StatfsOut) Status {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Lookup(header *InHeader, name string, out *EntryOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Forget(nodeID, nlookup uint64) {
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) GetAttr(input *GetAttrIn, out *AttrOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Open(input *OpenIn, out *OpenOut) (status Status) {
|
||||
return OK
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) SetAttr(input *SetAttrIn, out *AttrOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Readlink(header *InHeader) (out []byte, code Status) {
|
||||
return nil, ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Mknod(input *MknodIn, name string, out *EntryOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Unlink(header *InHeader, name string) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Rmdir(header *InHeader, name string) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Rename(input *RenameIn, oldName string, newName string) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Link(input *LinkIn, name string, out *EntryOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) GetXAttrSize(header *InHeader, attr string) (size int, code Status) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) {
|
||||
return nil, ENODATA
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) ListXAttr(header *InHeader) (data []byte, code Status) {
|
||||
return nil, ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) RemoveXAttr(header *InHeader, attr string) Status {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Access(input *AccessIn) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Create(input *CreateIn, name string, out *CreateOut) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) OpenDir(input *OpenIn, out *OpenOut) (status Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Status) {
|
||||
return nil, ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Release(input *ReleaseIn) {
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Flush(input *FlushIn) Status {
|
||||
return OK
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Fsync(input *FsyncIn) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) ReadDir(input *ReadIn, l *DirEntryList) Status {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) ReadDirPlus(input *ReadIn, l *DirEntryList) Status {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) ReleaseDir(input *ReleaseIn) {
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) FsyncDir(input *FsyncIn) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultRawFileSystem) Fallocate(in *FallocateIn) (code Status) {
|
||||
return ENOSYS
|
||||
}
|
||||
98
vendor/github.com/hanwen/go-fuse/fuse/direntry.go
generated
vendored
Normal file
98
vendor/github.com/hanwen/go-fuse/fuse/direntry.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package fuse
|
||||
|
||||
// all of the code for DirEntryList.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var eightPadding [8]byte
|
||||
|
||||
const direntSize = int(unsafe.Sizeof(_Dirent{}))
|
||||
|
||||
// DirEntry is a type for PathFileSystem and NodeFileSystem to return
|
||||
// directory contents in.
|
||||
type DirEntry struct {
|
||||
// Mode is the file's mode. Only the high bits (eg. S_IFDIR)
|
||||
// are considered.
|
||||
Mode uint32
|
||||
|
||||
// Name is the basename of the file in the directory.
|
||||
Name string
|
||||
}
|
||||
|
||||
func (d DirEntry) String() string {
|
||||
return fmt.Sprintf("%o: %q", d.Mode, d.Name)
|
||||
}
|
||||
|
||||
// DirEntryList holds the return value for READDIR and READDIRPLUS
|
||||
// opcodes.
|
||||
type DirEntryList struct {
|
||||
buf []byte
|
||||
size int
|
||||
offset uint64
|
||||
}
|
||||
|
||||
// NewDirEntryList creates a DirEntryList with the given data buffer
|
||||
// and offset.
|
||||
func NewDirEntryList(data []byte, off uint64) *DirEntryList {
|
||||
return &DirEntryList{
|
||||
buf: data[:0],
|
||||
size: len(data),
|
||||
offset: off,
|
||||
}
|
||||
}
|
||||
|
||||
// AddDirEntry tries to add an entry, and reports whether it
|
||||
// succeeded.
|
||||
func (l *DirEntryList) AddDirEntry(e DirEntry) (bool, uint64) {
|
||||
return l.Add(0, e.Name, uint64(FUSE_UNKNOWN_INO), e.Mode)
|
||||
}
|
||||
|
||||
// Add adds a direntry to the DirEntryList, returning whether it
|
||||
// succeeded.
|
||||
func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) (bool, uint64) {
|
||||
padding := (8 - len(name)&7) & 7
|
||||
delta := padding + direntSize + len(name) + prefix
|
||||
oldLen := len(l.buf)
|
||||
newLen := delta + oldLen
|
||||
|
||||
if newLen > l.size {
|
||||
return false, l.offset
|
||||
}
|
||||
l.buf = l.buf[:newLen]
|
||||
oldLen += prefix
|
||||
dirent := (*_Dirent)(unsafe.Pointer(&l.buf[oldLen]))
|
||||
dirent.Off = l.offset + 1
|
||||
dirent.Ino = inode
|
||||
dirent.NameLen = uint32(len(name))
|
||||
dirent.Typ = (mode & 0170000) >> 12
|
||||
oldLen += direntSize
|
||||
copy(l.buf[oldLen:], name)
|
||||
oldLen += len(name)
|
||||
|
||||
if padding > 0 {
|
||||
copy(l.buf[oldLen:], eightPadding[:padding])
|
||||
}
|
||||
|
||||
l.offset = dirent.Off
|
||||
return true, l.offset
|
||||
}
|
||||
|
||||
// AddDirLookupEntry is used for ReadDirPlus. It serializes a DirEntry
|
||||
// and returns the space for entry. If no space is left, returns a nil
|
||||
// pointer.
|
||||
func (l *DirEntryList) AddDirLookupEntry(e DirEntry) (*EntryOut, uint64) {
|
||||
lastStart := len(l.buf)
|
||||
ok, off := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name,
|
||||
uint64(FUSE_UNKNOWN_INO), e.Mode)
|
||||
if !ok {
|
||||
return nil, off
|
||||
}
|
||||
return (*EntryOut)(unsafe.Pointer(&l.buf[lastStart])), off
|
||||
}
|
||||
|
||||
func (l *DirEntryList) bytes() []byte {
|
||||
return l.buf
|
||||
}
|
||||
206
vendor/github.com/hanwen/go-fuse/fuse/lockingfs.go
generated
vendored
Normal file
206
vendor/github.com/hanwen/go-fuse/fuse/lockingfs.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Locking raw FS.
|
||||
|
||||
type lockingRawFileSystem struct {
|
||||
RawFS RawFileSystem
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Returns a Wrap
|
||||
func NewLockingRawFileSystem(fs RawFileSystem) RawFileSystem {
|
||||
return &lockingRawFileSystem{
|
||||
RawFS: fs,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) FS() RawFileSystem {
|
||||
return fs.RawFS
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) locked() func() {
|
||||
fs.lock.Lock()
|
||||
return func() { fs.lock.Unlock() }
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Lookup(header *InHeader, name string, out *EntryOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Lookup(header, name, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) SetDebug(dbg bool) {
|
||||
defer fs.locked()()
|
||||
fs.RawFS.SetDebug(dbg)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Forget(nodeID uint64, nlookup uint64) {
|
||||
defer fs.locked()()
|
||||
fs.RawFS.Forget(nodeID, nlookup)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) GetAttr(input *GetAttrIn, out *AttrOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.GetAttr(input, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Open(input *OpenIn, out *OpenOut) (status Status) {
|
||||
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Open(input, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) SetAttr(input *SetAttrIn, out *AttrOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.SetAttr(input, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Readlink(header *InHeader) (out []byte, code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Readlink(header)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Mknod(input *MknodIn, name string, out *EntryOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Mknod(input, name, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Mkdir(input, name, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Unlink(header *InHeader, name string) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Unlink(header, name)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Rmdir(header *InHeader, name string) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Rmdir(header, name)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Symlink(header, pointedTo, linkName, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Rename(input *RenameIn, oldName string, newName string) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Rename(input, oldName, newName)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Link(input *LinkIn, name string, out *EntryOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Link(input, name, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.SetXAttr(input, attr, data)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.GetXAttrData(header, attr)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) GetXAttrSize(header *InHeader, attr string) (sz int, code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.GetXAttrSize(header, attr)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) ListXAttr(header *InHeader) (data []byte, code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.ListXAttr(header)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) RemoveXAttr(header *InHeader, attr string) Status {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.RemoveXAttr(header, attr)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Access(input *AccessIn) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Access(input)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Create(input *CreateIn, name string, out *CreateOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Create(input, name, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) OpenDir(input *OpenIn, out *OpenOut) (status Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.OpenDir(input, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Release(input *ReleaseIn) {
|
||||
defer fs.locked()()
|
||||
fs.RawFS.Release(input)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) ReleaseDir(input *ReleaseIn) {
|
||||
defer fs.locked()()
|
||||
fs.RawFS.ReleaseDir(input)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Read(input, buf)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Write(input, data)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Flush(input *FlushIn) Status {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Flush(input)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Fsync(input *FsyncIn) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Fsync(input)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) ReadDir(input *ReadIn, out *DirEntryList) Status {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.ReadDir(input, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) ReadDirPlus(input *ReadIn, out *DirEntryList) Status {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.ReadDirPlus(input, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) FsyncDir(input *FsyncIn) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.FsyncDir(input)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Init(s *Server) {
|
||||
defer fs.locked()()
|
||||
fs.RawFS.Init(s)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) StatFs(header *InHeader, out *StatfsOut) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.StatFs(header, out)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) Fallocate(in *FallocateIn) (code Status) {
|
||||
defer fs.locked()()
|
||||
return fs.RawFS.Fallocate(in)
|
||||
}
|
||||
|
||||
func (fs *lockingRawFileSystem) String() string {
|
||||
defer fs.locked()()
|
||||
return fmt.Sprintf("Locked(%s)", fs.RawFS.String())
|
||||
}
|
||||
84
vendor/github.com/hanwen/go-fuse/fuse/misc.go
generated
vendored
Normal file
84
vendor/github.com/hanwen/go-fuse/fuse/misc.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Random odds and ends.
|
||||
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (code Status) String() string {
|
||||
if code <= 0 {
|
||||
return []string{
|
||||
"OK",
|
||||
"NOTIFY_POLL",
|
||||
"NOTIFY_INVAL_INODE",
|
||||
"NOTIFY_INVAL_ENTRY",
|
||||
"NOTIFY_INVAL_STORE",
|
||||
"NOTIFY_INVAL_RETRIEVE",
|
||||
"NOTIFY_INVAL_DELETE",
|
||||
}[-code]
|
||||
}
|
||||
return fmt.Sprintf("%d=%v", int(code), syscall.Errno(code))
|
||||
}
|
||||
|
||||
func (code Status) Ok() bool {
|
||||
return code == OK
|
||||
}
|
||||
|
||||
// ToStatus extracts an errno number from Go error objects. If it
|
||||
// fails, it logs an error and returns ENOSYS.
|
||||
func ToStatus(err error) Status {
|
||||
switch err {
|
||||
case nil:
|
||||
return OK
|
||||
case os.ErrPermission:
|
||||
return EPERM
|
||||
case os.ErrExist:
|
||||
return Status(syscall.EEXIST)
|
||||
case os.ErrNotExist:
|
||||
return ENOENT
|
||||
case os.ErrInvalid:
|
||||
return EINVAL
|
||||
}
|
||||
|
||||
switch t := err.(type) {
|
||||
case syscall.Errno:
|
||||
return Status(t)
|
||||
case *os.SyscallError:
|
||||
return Status(t.Err.(syscall.Errno))
|
||||
case *os.PathError:
|
||||
return ToStatus(t.Err)
|
||||
case *os.LinkError:
|
||||
return ToStatus(t.Err)
|
||||
}
|
||||
log.Println("can't convert error type:", err)
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func toSlice(dest *[]byte, ptr unsafe.Pointer, byteCount uintptr) {
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(dest))
|
||||
*h = reflect.SliceHeader{
|
||||
Data: uintptr(ptr),
|
||||
Len: int(byteCount),
|
||||
Cap: int(byteCount),
|
||||
}
|
||||
}
|
||||
|
||||
func CurrentOwner() *Owner {
|
||||
return &Owner{
|
||||
Uid: uint32(os.Getuid()),
|
||||
Gid: uint32(os.Getgid()),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
p := syscall.Getpagesize()
|
||||
if p != PAGESIZE {
|
||||
log.Panicf("page size incorrect: %d", p)
|
||||
}
|
||||
}
|
||||
26
vendor/github.com/hanwen/go-fuse/fuse/misc_test.go
generated
vendored
Normal file
26
vendor/github.com/hanwen/go-fuse/fuse/misc_test.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToStatus(t *testing.T) {
|
||||
errNo := ToStatus(os.ErrPermission)
|
||||
if errNo != EPERM {
|
||||
t.Errorf("Wrong conversion %v != %v", errNo, syscall.EPERM)
|
||||
}
|
||||
|
||||
e := os.NewSyscallError("syscall", syscall.EPERM)
|
||||
errNo = ToStatus(e)
|
||||
if errNo != EPERM {
|
||||
t.Errorf("Wrong conversion %v != %v", errNo, syscall.EPERM)
|
||||
}
|
||||
|
||||
e = os.Remove("this-file-surely-does-not-exist")
|
||||
errNo = ToStatus(e)
|
||||
if errNo != ENOENT {
|
||||
t.Errorf("Wrong conversion %v != %v", errNo, syscall.ENOENT)
|
||||
}
|
||||
}
|
||||
72
vendor/github.com/hanwen/go-fuse/fuse/mount_darwin.go
generated
vendored
Normal file
72
vendor/github.com/hanwen/go-fuse/fuse/mount_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func openFUSEDevice() (*os.File, error) {
|
||||
fs, err := filepath.Glob("/dev/osxfuse*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("all FUSE devices busy")
|
||||
}
|
||||
|
||||
const bin = "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
|
||||
|
||||
func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
|
||||
f, err := openFUSEDevice()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
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])
|
||||
|
||||
var out, errOut bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &errOut
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
f.Close()
|
||||
return 0, err
|
||||
}
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("mount_osxfusefs failed: %v. Stderr: %s, Stdout: %s", err, errOut.String(), out.String())
|
||||
}
|
||||
|
||||
ready <- err
|
||||
close(ready)
|
||||
}()
|
||||
|
||||
// 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)
|
||||
}
|
||||
158
vendor/github.com/hanwen/go-fuse/fuse/mount_linux.go
generated
vendored
Normal file
158
vendor/github.com/hanwen/go-fuse/fuse/mount_linux.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func unixgramSocketpair() (l, r *os.File, err error) {
|
||||
fd, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
|
||||
if err != nil {
|
||||
return nil, nil, os.NewSyscallError("socketpair",
|
||||
err.(syscall.Errno))
|
||||
}
|
||||
l = os.NewFile(uintptr(fd[0]), "socketpair-half1")
|
||||
r = os.NewFile(uintptr(fd[1]), "socketpair-half2")
|
||||
return
|
||||
}
|
||||
|
||||
// Create a FUSE FS on the specified mount point. The returned
|
||||
// mount point is always absolute.
|
||||
func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
|
||||
local, remote, err := unixgramSocketpair()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer local.Close()
|
||||
defer remote.Close()
|
||||
|
||||
bin, err := fusermountBinary()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
cmd := []string{bin, mountPoint}
|
||||
if s := opts.optionsStrings(); len(s) > 0 {
|
||||
cmd = append(cmd, "-o", strings.Join(s, ","))
|
||||
}
|
||||
proc, err := os.StartProcess(bin,
|
||||
cmd,
|
||||
&os.ProcAttr{
|
||||
Env: []string{"_FUSE_COMMFD=3"},
|
||||
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr, remote}})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
w, err := proc.Wait()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !w.Success() {
|
||||
err = fmt.Errorf("fusermount exited with code %v\n", w.Sys())
|
||||
return
|
||||
}
|
||||
|
||||
fd, err = getConnection(local)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
close(ready)
|
||||
return fd, err
|
||||
}
|
||||
|
||||
func privilegedUnmount(mountPoint string) error {
|
||||
dir, _ := filepath.Split(mountPoint)
|
||||
bin, err := umountBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
proc, err := os.StartProcess(bin,
|
||||
[]string{bin, mountPoint},
|
||||
&os.ProcAttr{Dir: dir, Files: []*os.File{nil, nil, os.Stderr}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w, err := proc.Wait()
|
||||
if !w.Success() {
|
||||
return fmt.Errorf("umount exited with code %v\n", w.Sys())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func unmount(mountPoint string) (err error) {
|
||||
if os.Geteuid() == 0 {
|
||||
return privilegedUnmount(mountPoint)
|
||||
}
|
||||
bin, err := fusermountBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errBuf := bytes.Buffer{}
|
||||
cmd := exec.Command(bin, "-u", mountPoint)
|
||||
cmd.Stderr = &errBuf
|
||||
err = cmd.Run()
|
||||
if errBuf.Len() > 0 {
|
||||
return fmt.Errorf("%s (code %v)\n",
|
||||
errBuf.String(), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getConnection(local *os.File) (int, error) {
|
||||
var data [4]byte
|
||||
control := make([]byte, 4*256)
|
||||
|
||||
// n, oobn, recvflags, from, errno - todo: error checking.
|
||||
_, oobn, _, _,
|
||||
err := syscall.Recvmsg(
|
||||
int(local.Fd()), data[:], control[:], 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
message := *(*syscall.Cmsghdr)(unsafe.Pointer(&control[0]))
|
||||
fd := *(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&control[0])) + syscall.SizeofCmsghdr))
|
||||
|
||||
if message.Type != 1 {
|
||||
return 0, fmt.Errorf("getConnection: recvmsg returned wrong control type: %d", message.Type)
|
||||
}
|
||||
if oobn <= syscall.SizeofCmsghdr {
|
||||
return 0, fmt.Errorf("getConnection: too short control message. Length: %d", oobn)
|
||||
}
|
||||
if fd < 0 {
|
||||
return 0, fmt.Errorf("getConnection: fd < 0: %d", fd)
|
||||
}
|
||||
return int(fd), nil
|
||||
}
|
||||
|
||||
// lookPathFallback - search binary in PATH and, if that fails,
|
||||
// in fallbackDir. This is useful if PATH is possible empty.
|
||||
func lookPathFallback(file string, fallbackDir string) (string, error) {
|
||||
binPath, err := exec.LookPath(file)
|
||||
if err == nil {
|
||||
return binPath, nil
|
||||
}
|
||||
|
||||
abs := path.Join(fallbackDir, file)
|
||||
return exec.LookPath(abs)
|
||||
}
|
||||
|
||||
func fusermountBinary() (string, error) {
|
||||
return lookPathFallback("fusermount", "/bin")
|
||||
}
|
||||
|
||||
func umountBinary() (string, error) {
|
||||
return lookPathFallback("umount", "/bin")
|
||||
}
|
||||
184
vendor/github.com/hanwen/go-fuse/fuse/nodefs/api.go
generated
vendored
Normal file
184
vendor/github.com/hanwen/go-fuse/fuse/nodefs/api.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
// 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
|
||||
// the data to represent fits in memory: you can construct the
|
||||
// complete file system tree at mount time
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// The Node interface implements the user-defined file system
|
||||
// functionality
|
||||
type Node interface {
|
||||
// Inode and SetInode are basic getter/setters. They are
|
||||
// called by the FileSystemConnector. You get them for free by
|
||||
// embedding the result of NewDefaultNode() in your node
|
||||
// struct.
|
||||
Inode() *Inode
|
||||
SetInode(node *Inode)
|
||||
|
||||
// OnMount is called on the root node just after a mount is
|
||||
// executed, either when the actual root is mounted, or when a
|
||||
// filesystem is mounted in-process. The passed-in
|
||||
// FileSystemConnector gives access to Notify methods and
|
||||
// Debug settings.
|
||||
OnMount(conn *FileSystemConnector)
|
||||
|
||||
// OnUnmount is executed just before a submount is removed,
|
||||
// and when the process receives a forget for the FUSE root
|
||||
// node.
|
||||
OnUnmount()
|
||||
|
||||
// Lookup finds a child node to this node; it is only called
|
||||
// for directory Nodes.
|
||||
Lookup(out *fuse.Attr, name string, context *fuse.Context) (*Inode, fuse.Status)
|
||||
|
||||
// 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 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.
|
||||
Access(mode uint32, context *fuse.Context) (code fuse.Status)
|
||||
Readlink(c *fuse.Context) ([]byte, fuse.Status)
|
||||
|
||||
// Namespace operations; these are only called on directory Nodes.
|
||||
|
||||
// Mknod should create the node, add it to the receiver's
|
||||
// inode, and return it
|
||||
Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)
|
||||
|
||||
// Mkdir should create the directory Inode, add it to the
|
||||
// receiver's Inode, and return it
|
||||
Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)
|
||||
Unlink(name string, context *fuse.Context) (code fuse.Status)
|
||||
Rmdir(name string, context *fuse.Context) (code fuse.Status)
|
||||
|
||||
// Symlink should create a child inode to the receiver, and
|
||||
// return it.
|
||||
Symlink(name string, content string, context *fuse.Context) (*Inode, fuse.Status)
|
||||
Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status)
|
||||
|
||||
// Link should return the Inode of the resulting link. In
|
||||
// a POSIX conformant file system, this should add 'existing'
|
||||
// to the receiver, and return the Inode corresponding to
|
||||
// 'existing'.
|
||||
Link(name string, existing Node, context *fuse.Context) (newNode *Inode, code fuse.Status)
|
||||
|
||||
// Create should return an open file, and the Inode for that file.
|
||||
Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, child *Inode, code fuse.Status)
|
||||
|
||||
// Open opens a file, and returns a File which is associated
|
||||
// with a file handle. It is OK to return (nil, OK) here. In
|
||||
// that case, the Node should implement Read or Write
|
||||
// directly.
|
||||
Open(flags uint32, context *fuse.Context) (file File, code fuse.Status)
|
||||
OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status)
|
||||
Read(file File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status)
|
||||
Write(file File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status)
|
||||
|
||||
// XAttrs
|
||||
GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status)
|
||||
RemoveXAttr(attr string, context *fuse.Context) fuse.Status
|
||||
SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status
|
||||
ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status)
|
||||
|
||||
// Attributes
|
||||
GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status)
|
||||
Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status)
|
||||
Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status)
|
||||
Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status)
|
||||
Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status)
|
||||
Fallocate(file File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status)
|
||||
|
||||
StatFs() *fuse.StatfsOut
|
||||
}
|
||||
|
||||
// A File object is returned from FileSystem.Open and
|
||||
// FileSystem.Create. Include the NewDefaultFile return value into
|
||||
// the struct to inherit a null implementation.
|
||||
type File interface {
|
||||
// Called upon registering the filehandle in the inode.
|
||||
SetInode(*Inode)
|
||||
|
||||
// The String method is for debug printing.
|
||||
String() string
|
||||
|
||||
// Wrappers around other File implementations, should return
|
||||
// the inner file here.
|
||||
InnerFile() File
|
||||
|
||||
Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status)
|
||||
Write(data []byte, off int64) (written uint32, code fuse.Status)
|
||||
|
||||
// Flush is called for close() call on a file descriptor. In
|
||||
// case of duplicated descriptor, it may be called more than
|
||||
// once for a file.
|
||||
Flush() fuse.Status
|
||||
|
||||
// This is called to before the file handle is forgotten. This
|
||||
// method has no return value, so nothing can synchronizes on
|
||||
// the call. Any cleanup that requires specific synchronization or
|
||||
// could fail with I/O errors should happen in Flush instead.
|
||||
Release()
|
||||
Fsync(flags int) (code fuse.Status)
|
||||
|
||||
// The methods below may be called on closed files, due to
|
||||
// concurrency. In that case, you should return EBADF.
|
||||
Truncate(size uint64) fuse.Status
|
||||
GetAttr(out *fuse.Attr) fuse.Status
|
||||
Chown(uid uint32, gid uint32) fuse.Status
|
||||
Chmod(perms uint32) fuse.Status
|
||||
Utimens(atime *time.Time, mtime *time.Time) fuse.Status
|
||||
Allocate(off uint64, size uint64, mode uint32) (code fuse.Status)
|
||||
}
|
||||
|
||||
// Wrap a File return in this to set FUSE flags. Also used internally
|
||||
// to store open file data.
|
||||
type WithFlags struct {
|
||||
File
|
||||
|
||||
// For debugging.
|
||||
Description string
|
||||
|
||||
// Put FOPEN_* flags here.
|
||||
FuseFlags uint32
|
||||
|
||||
// O_RDWR, O_TRUNCATE, etc.
|
||||
OpenFlags uint32
|
||||
}
|
||||
|
||||
// Options contains time out options for a node FileSystem. The
|
||||
// default copied from libfuse and set in NewMountOptions() is
|
||||
// (1s,1s,0s).
|
||||
type Options struct {
|
||||
EntryTimeout time.Duration
|
||||
AttrTimeout time.Duration
|
||||
NegativeTimeout time.Duration
|
||||
|
||||
// If set, replace all uids with given UID.
|
||||
// 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
|
||||
}
|
||||
70
vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultfile.go
generated
vendored
Normal file
70
vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultfile.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
type defaultFile struct{}
|
||||
|
||||
// NewDefaultFile returns a File instance that returns ENOSYS for
|
||||
// every operation.
|
||||
func NewDefaultFile() File {
|
||||
return (*defaultFile)(nil)
|
||||
}
|
||||
|
||||
func (f *defaultFile) SetInode(*Inode) {
|
||||
}
|
||||
|
||||
func (f *defaultFile) InnerFile() File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *defaultFile) String() string {
|
||||
return "defaultFile"
|
||||
}
|
||||
|
||||
func (f *defaultFile) Read(buf []byte, off int64) (fuse.ReadResult, fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Write(data []byte, off int64) (uint32, fuse.Status) {
|
||||
return 0, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Flush() fuse.Status {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *defaultFile) Release() {
|
||||
|
||||
}
|
||||
|
||||
func (f *defaultFile) GetAttr(*fuse.Attr) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Fsync(flags int) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Utimens(atime *time.Time, mtime *time.Time) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Truncate(size uint64) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Chown(uid uint32, gid uint32) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Chmod(perms uint32) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (f *defaultFile) Allocate(off uint64, size uint64, mode uint32) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
165
vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultnode.go
generated
vendored
Normal file
165
vendor/github.com/hanwen/go-fuse/fuse/nodefs/defaultnode.go
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// NewDefaultNode returns an implementation of Node that returns
|
||||
// ENOSYS for all operations.
|
||||
func NewDefaultNode() Node {
|
||||
return &defaultNode{}
|
||||
}
|
||||
|
||||
type defaultNode struct {
|
||||
inode *Inode
|
||||
}
|
||||
|
||||
func (fs *defaultNode) OnUnmount() {
|
||||
}
|
||||
|
||||
func (fs *defaultNode) OnMount(conn *FileSystemConnector) {
|
||||
}
|
||||
|
||||
func (n *defaultNode) StatFs() *fuse.StatfsOut {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *defaultNode) SetInode(node *Inode) {
|
||||
n.inode = node
|
||||
}
|
||||
|
||||
func (n *defaultNode) Deletable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *defaultNode) Inode() *Inode {
|
||||
return n.inode
|
||||
}
|
||||
|
||||
func (n *defaultNode) OnForget() {
|
||||
}
|
||||
|
||||
func (n *defaultNode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (node *Inode, code fuse.Status) {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (n *defaultNode) Access(mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (newNode *Inode, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
func (n *defaultNode) Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
func (n *defaultNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
func (n *defaultNode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
func (n *defaultNode) Symlink(name string, content string, context *fuse.Context) (newNode *Inode, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Link(name string, existing Node, context *fuse.Context) (newNode *Inode, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, newNode *Inode, code fuse.Status) {
|
||||
return nil, nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Open(flags uint32, context *fuse.Context) (file File, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Flush(file File, openFlags uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
|
||||
ch := n.Inode().Children()
|
||||
s := make([]fuse.DirEntry, 0, len(ch))
|
||||
for name, child := range ch {
|
||||
if child.mountPoint != nil {
|
||||
continue
|
||||
}
|
||||
var a fuse.Attr
|
||||
code := child.Node().GetAttr(&a, nil, context)
|
||||
if code.Ok() {
|
||||
s = append(s, fuse.DirEntry{Name: name, Mode: a.Mode})
|
||||
}
|
||||
}
|
||||
return s, fuse.OK
|
||||
}
|
||||
|
||||
func (n *defaultNode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
|
||||
return nil, fuse.ENODATA
|
||||
}
|
||||
|
||||
func (n *defaultNode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status) {
|
||||
if n.Inode().IsDir() {
|
||||
out.Mode = fuse.S_IFDIR | 0755
|
||||
} else {
|
||||
out.Mode = fuse.S_IFREG | 0644
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (n *defaultNode) Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Fallocate(file File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Read(file File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) {
|
||||
if file != nil {
|
||||
return file.Read(dest, off)
|
||||
}
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *defaultNode) Write(file File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status) {
|
||||
if file != nil {
|
||||
return file.Write(data, off)
|
||||
}
|
||||
return 0, fuse.ENOSYS
|
||||
}
|
||||
114
vendor/github.com/hanwen/go-fuse/fuse/nodefs/dir.go
generated
vendored
Normal file
114
vendor/github.com/hanwen/go-fuse/fuse/nodefs/dir.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
type connectorDir struct {
|
||||
node Node
|
||||
rawFS fuse.RawFileSystem
|
||||
|
||||
// Protect stream and lastOffset. These are written in case
|
||||
// there is a seek on the directory.
|
||||
mu sync.Mutex
|
||||
stream []fuse.DirEntry
|
||||
|
||||
// lastOffset stores the last offset for a readdir. This lets
|
||||
// readdir pick up changes to the directory made after opening
|
||||
// it.
|
||||
lastOffset uint64
|
||||
}
|
||||
|
||||
func (d *connectorDir) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if d.stream == nil {
|
||||
return fuse.OK
|
||||
}
|
||||
// rewinddir() should be as if reopening directory.
|
||||
// TODO - test this.
|
||||
if d.lastOffset > 0 && input.Offset == 0 {
|
||||
d.stream, code = d.node.OpenDir((*fuse.Context)(&input.Context))
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
if input.Offset > uint64(len(d.stream)) {
|
||||
// This shouldn't happen, but let's not crash.
|
||||
return fuse.EINVAL
|
||||
}
|
||||
|
||||
todo := d.stream[input.Offset:]
|
||||
for _, e := range todo {
|
||||
if e.Name == "" {
|
||||
log.Printf("got empty directory entry, mode %o.", e.Mode)
|
||||
continue
|
||||
}
|
||||
ok, off := out.AddDirEntry(e)
|
||||
d.lastOffset = off
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (d *connectorDir) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if d.stream == nil {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
// rewinddir() should be as if reopening directory.
|
||||
if d.lastOffset > 0 && input.Offset == 0 {
|
||||
d.stream, code = d.node.OpenDir((*fuse.Context)(&input.Context))
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
if input.Offset > uint64(len(d.stream)) {
|
||||
// This shouldn't happen, but let's not crash.
|
||||
return fuse.EINVAL
|
||||
}
|
||||
todo := d.stream[input.Offset:]
|
||||
for _, e := range todo {
|
||||
if e.Name == "" {
|
||||
log.Printf("got empty directory entry, mode %o.", e.Mode)
|
||||
continue
|
||||
}
|
||||
|
||||
// we have to be sure entry will fit if we try to add
|
||||
// it, or we'll mess up the lookup counts.
|
||||
entryDest, off := out.AddDirLookupEntry(e)
|
||||
if entryDest == nil {
|
||||
break
|
||||
}
|
||||
entryDest.Ino = uint64(fuse.FUSE_UNKNOWN_INO)
|
||||
|
||||
// No need to fill attributes for . and ..
|
||||
if e.Name == "." || e.Name == ".." {
|
||||
continue
|
||||
}
|
||||
|
||||
// Clear entryDest before use it, some fields can be corrupted if does not set all fields in rawFS.Lookup
|
||||
*entryDest = fuse.EntryOut{}
|
||||
|
||||
d.rawFS.Lookup(&input.InHeader, e.Name, entryDest)
|
||||
d.lastOffset = off
|
||||
}
|
||||
return fuse.OK
|
||||
|
||||
}
|
||||
|
||||
type rawDir interface {
|
||||
ReadDir(out *fuse.DirEntryList, input *fuse.ReadIn, c *fuse.Context) fuse.Status
|
||||
ReadDirPlus(out *fuse.DirEntryList, input *fuse.ReadIn, c *fuse.Context) fuse.Status
|
||||
}
|
||||
125
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fileless_test.go
generated
vendored
Normal file
125
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fileless_test.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
type nodeReadNode struct {
|
||||
Node
|
||||
dir bool
|
||||
noOpen bool
|
||||
data []byte
|
||||
}
|
||||
|
||||
func newNodeReadNode(noOpen, dir bool, d []byte) *nodeReadNode {
|
||||
return &nodeReadNode{
|
||||
Node: NewDefaultNode(),
|
||||
noOpen: noOpen,
|
||||
dir: dir,
|
||||
data: d,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nodeReadNode) Open(flags uint32, context *fuse.Context) (file File, code fuse.Status) {
|
||||
if n.noOpen {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
return nil, fuse.OK
|
||||
}
|
||||
|
||||
func (n *nodeReadNode) Read(file File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) {
|
||||
e := off + int64(len(dest))
|
||||
if int(e) > len(n.data) {
|
||||
e = int64(len(n.data))
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
dir, err := ioutil.TempDir("", "nodefs")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir: %v", err)
|
||||
}
|
||||
|
||||
root := newNodeReadNode(true, true, nil)
|
||||
root.noOpen = true
|
||||
|
||||
s, _, err := MountRoot(dir, root, &Options{Debug: VerboseTest()})
|
||||
if err != nil {
|
||||
t.Fatalf("MountRoot: %v", err)
|
||||
}
|
||||
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.")
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(dir + "/file")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
want := "file"
|
||||
if string(content) != want {
|
||||
t.Fatalf("got %q, want %q", content, want)
|
||||
}
|
||||
|
||||
content, err = ioutil.ReadFile(dir + "/file2")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
|
||||
want = "file2"
|
||||
if string(content) != want {
|
||||
t.Fatalf("got %q, want %q", content, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeRead(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "nodefs")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir: %v", err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
want := "file"
|
||||
if string(content) != want {
|
||||
t.Fatalf("got %q, want %q", content, want)
|
||||
}
|
||||
}
|
||||
249
vendor/github.com/hanwen/go-fuse/fuse/nodefs/files.go
generated
vendored
Normal file
249
vendor/github.com/hanwen/go-fuse/fuse/nodefs/files.go
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// DataFile is for implementing read-only filesystems. This
|
||||
// assumes we already have the data in memory.
|
||||
type dataFile struct {
|
||||
data []byte
|
||||
|
||||
File
|
||||
}
|
||||
|
||||
func (f *dataFile) String() string {
|
||||
l := len(f.data)
|
||||
if l > 10 {
|
||||
l = 10
|
||||
}
|
||||
|
||||
return fmt.Sprintf("dataFile(%x)", f.data[:l])
|
||||
}
|
||||
|
||||
func (f *dataFile) GetAttr(out *fuse.Attr) fuse.Status {
|
||||
out.Mode = fuse.S_IFREG | 0644
|
||||
out.Size = uint64(len(f.data))
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func NewDataFile(data []byte) File {
|
||||
f := new(dataFile)
|
||||
f.data = data
|
||||
f.File = NewDefaultFile()
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *dataFile) Read(buf []byte, off int64) (res fuse.ReadResult, code fuse.Status) {
|
||||
end := int(off) + int(len(buf))
|
||||
if end > len(f.data) {
|
||||
end = len(f.data)
|
||||
}
|
||||
|
||||
return fuse.ReadResultData(f.data[off:end]), fuse.OK
|
||||
}
|
||||
|
||||
type devNullFile struct {
|
||||
File
|
||||
}
|
||||
|
||||
// NewDevNullFile returns a file that accepts any write, and always
|
||||
// returns EOF for reads.
|
||||
func NewDevNullFile() File {
|
||||
return &devNullFile{
|
||||
File: NewDefaultFile(),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *devNullFile) Allocate(off uint64, size uint64, mode uint32) (code fuse.Status) {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *devNullFile) String() string {
|
||||
return "devNullFile"
|
||||
}
|
||||
|
||||
func (f *devNullFile) Read(buf []byte, off int64) (fuse.ReadResult, fuse.Status) {
|
||||
return fuse.ReadResultData(nil), fuse.OK
|
||||
}
|
||||
|
||||
func (f *devNullFile) Write(content []byte, off int64) (uint32, fuse.Status) {
|
||||
return uint32(len(content)), fuse.OK
|
||||
}
|
||||
|
||||
func (f *devNullFile) Flush() fuse.Status {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *devNullFile) Fsync(flags int) (code fuse.Status) {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *devNullFile) Truncate(size uint64) (code fuse.Status) {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
||||
// LoopbackFile delegates all operations back to an underlying os.File.
|
||||
func NewLoopbackFile(f *os.File) File {
|
||||
return &loopbackFile{File: f}
|
||||
}
|
||||
|
||||
type loopbackFile struct {
|
||||
File *os.File
|
||||
|
||||
// os.File is not threadsafe. Although fd themselves are
|
||||
// constant during the lifetime of an open file, the OS may
|
||||
// reuse the fd number after it is closed. When open races
|
||||
// with another close, they may lead to confusion as which
|
||||
// file gets written in the end.
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (f *loopbackFile) InnerFile() File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *loopbackFile) SetInode(n *Inode) {
|
||||
}
|
||||
|
||||
func (f *loopbackFile) String() string {
|
||||
return fmt.Sprintf("loopbackFile(%s)", f.File.Name())
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Read(buf []byte, off int64) (res fuse.ReadResult, code fuse.Status) {
|
||||
f.lock.Lock()
|
||||
// This is not racy by virtue of the kernel properly
|
||||
// synchronizing the open/write/close.
|
||||
r := fuse.ReadResultFd(f.File.Fd(), off, len(buf))
|
||||
f.lock.Unlock()
|
||||
return r, fuse.OK
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Write(data []byte, off int64) (uint32, fuse.Status) {
|
||||
f.lock.Lock()
|
||||
n, err := f.File.WriteAt(data, off)
|
||||
f.lock.Unlock()
|
||||
return uint32(n), fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Release() {
|
||||
f.lock.Lock()
|
||||
f.File.Close()
|
||||
f.lock.Unlock()
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Flush() fuse.Status {
|
||||
f.lock.Lock()
|
||||
|
||||
// Since Flush() may be called for each dup'd fd, we don't
|
||||
// want to really close the file, we just want to flush. This
|
||||
// is achieved by closing a dup'd fd.
|
||||
newFd, err := syscall.Dup(int(f.File.Fd()))
|
||||
f.lock.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
err = syscall.Close(newFd)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Fsync(flags int) (code fuse.Status) {
|
||||
f.lock.Lock()
|
||||
r := fuse.ToStatus(syscall.Fsync(int(f.File.Fd())))
|
||||
f.lock.Unlock()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Truncate(size uint64) fuse.Status {
|
||||
f.lock.Lock()
|
||||
r := fuse.ToStatus(syscall.Ftruncate(int(f.File.Fd()), int64(size)))
|
||||
f.lock.Unlock()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Chmod(mode uint32) fuse.Status {
|
||||
f.lock.Lock()
|
||||
r := fuse.ToStatus(f.File.Chmod(os.FileMode(mode)))
|
||||
f.lock.Unlock()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *loopbackFile) Chown(uid uint32, gid uint32) fuse.Status {
|
||||
f.lock.Lock()
|
||||
r := fuse.ToStatus(f.File.Chown(int(uid), int(gid)))
|
||||
f.lock.Unlock()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *loopbackFile) GetAttr(a *fuse.Attr) fuse.Status {
|
||||
st := syscall.Stat_t{}
|
||||
f.lock.Lock()
|
||||
err := syscall.Fstat(int(f.File.Fd()), &st)
|
||||
f.lock.Unlock()
|
||||
if err != nil {
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
a.FromStat(&st)
|
||||
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
// Utimens implemented in files_linux.go
|
||||
|
||||
// Allocate implemented in files_linux.go
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// NewReadOnlyFile wraps a File so all read/write operations are
|
||||
// denied.
|
||||
func NewReadOnlyFile(f File) File {
|
||||
return &readOnlyFile{File: f}
|
||||
}
|
||||
|
||||
type readOnlyFile struct {
|
||||
File
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) InnerFile() File {
|
||||
return f.File
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) String() string {
|
||||
return fmt.Sprintf("readOnlyFile(%s)", f.File.String())
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) Write(data []byte, off int64) (uint32, fuse.Status) {
|
||||
return 0, fuse.EPERM
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) Fsync(flag int) (code fuse.Status) {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) Truncate(size uint64) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) Chmod(mode uint32) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) Chown(uid uint32, gid uint32) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (f *readOnlyFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
99
vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_darwin.go
generated
vendored
Normal file
99
vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
||||
// TODO: Handle `mode` parameter.
|
||||
|
||||
// From `man fcntl` on OSX:
|
||||
// The F_PREALLOCATE command operates on the following structure:
|
||||
//
|
||||
// typedef struct fstore {
|
||||
// u_int32_t fst_flags; /* IN: flags word */
|
||||
// int fst_posmode; /* IN: indicates offset field */
|
||||
// off_t fst_offset; /* IN: start of the region */
|
||||
// off_t fst_length; /* IN: size of the region */
|
||||
// off_t fst_bytesalloc; /* OUT: number of bytes allocated */
|
||||
// } fstore_t;
|
||||
//
|
||||
// The flags (fst_flags) for the F_PREALLOCATE command are as follows:
|
||||
//
|
||||
// F_ALLOCATECONTIG Allocate contiguous space.
|
||||
//
|
||||
// F_ALLOCATEALL Allocate all requested space or no space at all.
|
||||
//
|
||||
// The position modes (fst_posmode) for the F_PREALLOCATE command indicate how to use the offset field. The modes are as fol-
|
||||
// lows:
|
||||
//
|
||||
// F_PEOFPOSMODE Allocate from the physical end of file.
|
||||
//
|
||||
// F_VOLPOSMODE Allocate from the volume offset.
|
||||
|
||||
k := struct {
|
||||
Flags uint32 // u_int32_t
|
||||
Posmode int64 // int
|
||||
Offset int64 // off_t
|
||||
Length int64 // off_t
|
||||
Bytesalloc int64 // off_t
|
||||
}{
|
||||
0,
|
||||
0,
|
||||
int64(off),
|
||||
int64(sz),
|
||||
0,
|
||||
}
|
||||
|
||||
// Linux version for reference:
|
||||
// err := syscall.Fallocate(int(f.File.Fd()), mode, int64(off), int64(sz))
|
||||
|
||||
f.lock.Lock()
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.File.Fd(), uintptr(syscall.F_PREALLOCATE), uintptr(unsafe.Pointer(&k)))
|
||||
f.lock.Unlock()
|
||||
if errno != 0 {
|
||||
return fuse.ToStatus(errno)
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
const _UTIME_NOW = ((1 << 30) - 1)
|
||||
const _UTIME_OMIT = ((1 << 30) - 2)
|
||||
|
||||
// timeToTimeval - Convert time.Time to syscall.Timeval
|
||||
//
|
||||
// Note: This does not use syscall.NsecToTimespec because
|
||||
// that does not work properly for times before 1970,
|
||||
// see https://github.com/golang/go/issues/12777
|
||||
func timeToTimeval(t *time.Time) syscall.Timeval {
|
||||
var tv syscall.Timeval
|
||||
tv.Usec = int32(t.Nanosecond() / 1000)
|
||||
tv.Sec = t.Unix()
|
||||
return tv
|
||||
}
|
||||
|
||||
// OSX does not have the utimensat syscall neded to implement this properly.
|
||||
// We do our best to emulate it using futimes.
|
||||
func (f *loopbackFile) Utimens(a *time.Time, m *time.Time) fuse.Status {
|
||||
tv := make([]syscall.Timeval, 2)
|
||||
if a == nil {
|
||||
tv[0].Usec = _UTIME_OMIT
|
||||
} else {
|
||||
tv[0] = timeToTimeval(a)
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
tv[1].Usec = _UTIME_OMIT
|
||||
} else {
|
||||
tv[1] = timeToTimeval(m)
|
||||
}
|
||||
|
||||
f.lock.Lock()
|
||||
err := syscall.Futimes(int(f.File.Fd()), tv)
|
||||
f.lock.Unlock()
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
45
vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_linux.go
generated
vendored
Normal file
45
vendor/github.com/hanwen/go-fuse/fuse/nodefs/files_linux.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
||||
f.lock.Lock()
|
||||
err := syscall.Fallocate(int(f.File.Fd()), mode, int64(off), int64(sz))
|
||||
f.lock.Unlock()
|
||||
if err != nil {
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
const _UTIME_NOW = ((1 << 30) - 1)
|
||||
const _UTIME_OMIT = ((1 << 30) - 2)
|
||||
|
||||
// Utimens - file handle based version of loopbackFileSystem.Utimens()
|
||||
func (f *loopbackFile) Utimens(a *time.Time, m *time.Time) fuse.Status {
|
||||
var ts [2]syscall.Timespec
|
||||
|
||||
if a == nil {
|
||||
ts[0].Nsec = _UTIME_OMIT
|
||||
} else {
|
||||
ts[0] = syscall.NsecToTimespec(a.UnixNano())
|
||||
ts[0].Nsec = 0
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
ts[1].Nsec = _UTIME_OMIT
|
||||
} else {
|
||||
ts[1] = syscall.NsecToTimespec(a.UnixNano())
|
||||
ts[1].Nsec = 0
|
||||
}
|
||||
|
||||
f.lock.Lock()
|
||||
err := futimens(int(f.File.Fd()), &ts)
|
||||
f.lock.Unlock()
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
420
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go
generated
vendored
Normal file
420
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go
generated
vendored
Normal file
@@ -0,0 +1,420 @@
|
||||
package nodefs
|
||||
|
||||
// This file contains the internal logic of the
|
||||
// FileSystemConnector. The functions for satisfying the raw interface
|
||||
// are in fsops.go
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// Tests should set to true.
|
||||
var paranoia = false
|
||||
|
||||
// FilesystemConnector translates the raw FUSE protocol (serialized
|
||||
// structs of uint32/uint64) to operations on Go objects representing
|
||||
// files and directories.
|
||||
type FileSystemConnector struct {
|
||||
debug bool
|
||||
|
||||
// Callbacks for talking back to the kernel.
|
||||
server *fuse.Server
|
||||
|
||||
// Translate between uint64 handles and *Inode.
|
||||
inodeMap handleMap
|
||||
|
||||
// The root of the FUSE file system.
|
||||
rootNode *Inode
|
||||
}
|
||||
|
||||
// NewOptions generates FUSE options that correspond to libfuse's
|
||||
// defaults.
|
||||
func NewOptions() *Options {
|
||||
return &Options{
|
||||
NegativeTimeout: 0,
|
||||
AttrTimeout: time.Second,
|
||||
EntryTimeout: time.Second,
|
||||
Owner: fuse.CurrentOwner(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFileSystemConnector creates a FileSystemConnector with the given
|
||||
// options.
|
||||
func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) {
|
||||
c = new(FileSystemConnector)
|
||||
if opts == nil {
|
||||
opts = NewOptions()
|
||||
}
|
||||
c.inodeMap = newPortableHandleMap()
|
||||
c.rootNode = newInode(true, root)
|
||||
|
||||
c.verify()
|
||||
c.mountRoot(opts)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Server returns the fuse.Server that talking to the kernel.
|
||||
func (c *FileSystemConnector) Server() *fuse.Server {
|
||||
return c.server
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// This verifies invariants of the data structure. This routine
|
||||
// acquires tree locks as it walks the inode tree.
|
||||
func (c *FileSystemConnector) verify() {
|
||||
if !paranoia {
|
||||
return
|
||||
}
|
||||
root := c.rootNode
|
||||
root.verify(c.rootNode.mountPoint)
|
||||
}
|
||||
|
||||
// childLookup fills entry information for a newly created child inode
|
||||
func (c *rawBridge) childLookup(out *fuse.EntryOut, n *Inode, context *fuse.Context) {
|
||||
n.Node().GetAttr((*fuse.Attr)(&out.Attr), nil, context)
|
||||
n.mount.fillEntry(out)
|
||||
out.NodeId, out.Generation = c.fsConn().lookupUpdate(n)
|
||||
if out.Ino == 0 {
|
||||
out.Ino = out.NodeId
|
||||
}
|
||||
if out.Nlink == 0 {
|
||||
// With Nlink == 0, newer kernels will refuse link
|
||||
// operations.
|
||||
out.Nlink = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (c *rawBridge) toInode(nodeid uint64) *Inode {
|
||||
if nodeid == fuse.FUSE_ROOT_ID {
|
||||
return c.rootNode
|
||||
}
|
||||
i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid)))
|
||||
return i
|
||||
}
|
||||
|
||||
// Must run outside treeLock. Returns the nodeId and generation.
|
||||
func (c *FileSystemConnector) lookupUpdate(node *Inode) (id, generation uint64) {
|
||||
id, generation = c.inodeMap.Register(&node.handled)
|
||||
c.verify()
|
||||
return
|
||||
}
|
||||
|
||||
// forgetUpdate decrements the reference counter for "nodeID" by "forgetCount".
|
||||
// Must run outside treeLock.
|
||||
func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) {
|
||||
if nodeID == fuse.FUSE_ROOT_ID {
|
||||
c.rootNode.Node().OnUnmount()
|
||||
|
||||
// We never got a lookup for root, so don't try to
|
||||
// forget root.
|
||||
return
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// InodeCount returns the number of inodes registered with the kernel.
|
||||
func (c *FileSystemConnector) InodeHandleCount() int {
|
||||
return c.inodeMap.Count()
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) {
|
||||
if parent == nil {
|
||||
parent = c.rootNode
|
||||
}
|
||||
if fullPath == "" {
|
||||
return parent, nil
|
||||
}
|
||||
|
||||
sep := string(filepath.Separator)
|
||||
fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep)
|
||||
comps := strings.Split(fullPath, sep)
|
||||
|
||||
node := parent
|
||||
if node.mountPoint == nil {
|
||||
node.mount.treeLock.RLock()
|
||||
defer node.mount.treeLock.RUnlock()
|
||||
}
|
||||
|
||||
for i, component := range comps {
|
||||
if len(component) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if node.mountPoint != nil {
|
||||
node.mount.treeLock.RLock()
|
||||
defer node.mount.treeLock.RUnlock()
|
||||
}
|
||||
|
||||
next := node.children[component]
|
||||
if next == nil {
|
||||
return node, comps[i:]
|
||||
}
|
||||
node = next
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// Follows the path from the given parent, doing lookups as
|
||||
// necesary. The path should be '/' separated without leading slash.
|
||||
func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
|
||||
if path == "" {
|
||||
return parent
|
||||
}
|
||||
|
||||
components := strings.Split(path, "/")
|
||||
for _, r := range components {
|
||||
var a fuse.Attr
|
||||
// This will not affect inode ID lookup counts, which
|
||||
// are only update in response to kernel requests.
|
||||
var dummy fuse.InHeader
|
||||
child, _ := c.internalLookup(&a, parent, r, &dummy)
|
||||
if child == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parent = child
|
||||
}
|
||||
|
||||
return parent
|
||||
}
|
||||
|
||||
func (c *FileSystemConnector) mountRoot(opts *Options) {
|
||||
c.rootNode.mountFs(opts)
|
||||
c.rootNode.mount.connector = c
|
||||
c.verify()
|
||||
}
|
||||
|
||||
// Mount() generates a synthetic directory node, and mounts the file
|
||||
// system there. If opts is nil, the mount options of the root file
|
||||
// system are inherited. The encompassing filesystem should pretend
|
||||
// the mount point does not exist.
|
||||
//
|
||||
// It returns ENOENT if the directory containing the mount point does
|
||||
// not exist, and EBUSY if the intended mount point already exists.
|
||||
func (c *FileSystemConnector) Mount(parent *Inode, name string, root Node, opts *Options) fuse.Status {
|
||||
node, code := c.lockMount(parent, name, root, opts)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
|
||||
node.Node().OnMount(c)
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *FileSystemConnector) lockMount(parent *Inode, name string, root Node, opts *Options) (*Inode, fuse.Status) {
|
||||
defer c.verify()
|
||||
parent.mount.treeLock.Lock()
|
||||
defer parent.mount.treeLock.Unlock()
|
||||
node := parent.children[name]
|
||||
if node != nil {
|
||||
return nil, fuse.EBUSY
|
||||
}
|
||||
|
||||
node = newInode(true, root)
|
||||
if opts == nil {
|
||||
opts = c.rootNode.mountPoint.options
|
||||
}
|
||||
|
||||
node.mountFs(opts)
|
||||
node.mount.connector = c
|
||||
parent.addChild(name, node)
|
||||
|
||||
node.mountPoint.parentInode = parent
|
||||
if c.debug {
|
||||
log.Printf("Mount %T on subdir %s, parent %d", node,
|
||||
name, c.inodeMap.Handle(&parent.handled))
|
||||
}
|
||||
return node, fuse.OK
|
||||
}
|
||||
|
||||
// Unmount() tries to unmount the given inode. It returns EINVAL if the
|
||||
// path does not exist, or is not a mount point, and EBUSY if there
|
||||
// are open files or submounts below this node.
|
||||
func (c *FileSystemConnector) Unmount(node *Inode) fuse.Status {
|
||||
// TODO - racy.
|
||||
if node.mountPoint == nil {
|
||||
log.Println("not a mountpoint:", c.inodeMap.Handle(&node.handled))
|
||||
return fuse.EINVAL
|
||||
}
|
||||
|
||||
nodeID := c.inodeMap.Handle(&node.handled)
|
||||
|
||||
// Must lock parent to update tree structure.
|
||||
parentNode := node.mountPoint.parentInode
|
||||
parentNode.mount.treeLock.Lock()
|
||||
defer parentNode.mount.treeLock.Unlock()
|
||||
|
||||
mount := node.mountPoint
|
||||
name := node.mountPoint.mountName()
|
||||
if mount.openFiles.Count() > 0 {
|
||||
return fuse.EBUSY
|
||||
}
|
||||
|
||||
node.mount.treeLock.Lock()
|
||||
defer node.mount.treeLock.Unlock()
|
||||
|
||||
if mount.mountInode != node {
|
||||
log.Panicf("got two different mount inodes %v vs %v",
|
||||
c.inodeMap.Handle(&mount.mountInode.handled),
|
||||
c.inodeMap.Handle(&node.handled))
|
||||
}
|
||||
|
||||
if !node.canUnmount() {
|
||||
return fuse.EBUSY
|
||||
}
|
||||
|
||||
delete(parentNode.children, name)
|
||||
node.Node().OnUnmount()
|
||||
|
||||
parentId := c.inodeMap.Handle(&parentNode.handled)
|
||||
if parentNode == c.rootNode {
|
||||
// TODO - test coverage. Currently covered by zipfs/multizip_test.go
|
||||
parentId = fuse.FUSE_ROOT_ID
|
||||
}
|
||||
|
||||
// We have to wait until the kernel has forgotten the
|
||||
// mountpoint, so the write to node.mountPoint is no longer
|
||||
// racy.
|
||||
mount.treeLock.Unlock()
|
||||
parentNode.mount.treeLock.Unlock()
|
||||
code := c.server.DeleteNotify(parentId, nodeID, name)
|
||||
|
||||
if code.Ok() {
|
||||
delay := 100 * time.Microsecond
|
||||
|
||||
for {
|
||||
// This operation is rare, so we kludge it to avoid
|
||||
// contention.
|
||||
time.Sleep(delay)
|
||||
delay = delay * 2
|
||||
if !c.inodeMap.Has(nodeID) {
|
||||
break
|
||||
}
|
||||
|
||||
if delay >= time.Second {
|
||||
// We limit the wait at one second. If
|
||||
// it takes longer, something else is
|
||||
// amiss, and we would be waiting forever.
|
||||
log.Println("kernel did not issue FORGET for node on Unmount.")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parentNode.mount.treeLock.Lock()
|
||||
mount.treeLock.Lock()
|
||||
mount.mountInode = nil
|
||||
node.mountPoint = nil
|
||||
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
// FileNotify notifies the kernel that data and metadata of this inode
|
||||
// has changed. After this call completes, the kernel will issue a
|
||||
// new GetAttr requests for metadata and new Read calls for content.
|
||||
// Use negative offset for metadata-only invalidation, and zero-length
|
||||
// for invalidating all content.
|
||||
func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) fuse.Status {
|
||||
var nId uint64
|
||||
if node == c.rootNode {
|
||||
nId = fuse.FUSE_ROOT_ID
|
||||
} else {
|
||||
nId = c.inodeMap.Handle(&node.handled)
|
||||
}
|
||||
|
||||
if nId == 0 {
|
||||
return fuse.OK
|
||||
}
|
||||
return c.server.InodeNotify(nId, off, length)
|
||||
}
|
||||
|
||||
// EntryNotify makes the kernel forget the entry data from the given
|
||||
// name from a directory. After this call, the kernel will issue a
|
||||
// new lookup request for the given name when necessary. No filesystem
|
||||
// related locks should be held when calling this.
|
||||
func (c *FileSystemConnector) EntryNotify(node *Inode, name string) fuse.Status {
|
||||
var nId uint64
|
||||
if node == c.rootNode {
|
||||
nId = fuse.FUSE_ROOT_ID
|
||||
} else {
|
||||
nId = c.inodeMap.Handle(&node.handled)
|
||||
}
|
||||
|
||||
if nId == 0 {
|
||||
return fuse.OK
|
||||
}
|
||||
return c.server.EntryNotify(nId, name)
|
||||
}
|
||||
|
||||
// DeleteNotify signals to the kernel that the named entry in dir for
|
||||
// the child disappeared. No filesystem related locks should be held
|
||||
// when calling this.
|
||||
func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) fuse.Status {
|
||||
var nId uint64
|
||||
|
||||
if dir == c.rootNode {
|
||||
nId = fuse.FUSE_ROOT_ID
|
||||
} else {
|
||||
nId = c.inodeMap.Handle(&dir.handled)
|
||||
}
|
||||
|
||||
if nId == 0 {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
chId := c.inodeMap.Handle(&child.handled)
|
||||
|
||||
return c.server.DeleteNotify(nId, chId, name)
|
||||
}
|
||||
154
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsmount.go
generated
vendored
Normal file
154
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsmount.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// openedFile stores either an open dir or an open file.
|
||||
type openedFile struct {
|
||||
handled
|
||||
|
||||
WithFlags
|
||||
|
||||
dir *connectorDir
|
||||
}
|
||||
|
||||
type fileSystemMount struct {
|
||||
// Node that we were mounted on.
|
||||
mountInode *Inode
|
||||
|
||||
// Parent to the mountInode.
|
||||
parentInode *Inode
|
||||
|
||||
// Options for the mount.
|
||||
options *Options
|
||||
|
||||
// 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.
|
||||
treeLock sync.RWMutex
|
||||
|
||||
// Manage filehandles of open files.
|
||||
openFiles handleMap
|
||||
|
||||
Debug bool
|
||||
|
||||
connector *FileSystemConnector
|
||||
}
|
||||
|
||||
// Must called with lock for parent held.
|
||||
func (m *fileSystemMount) mountName() string {
|
||||
for k, v := range m.parentInode.children {
|
||||
if m.mountInode == v {
|
||||
return k
|
||||
}
|
||||
}
|
||||
panic("not found")
|
||||
}
|
||||
|
||||
func (m *fileSystemMount) setOwner(attr *fuse.Attr) {
|
||||
if m.options.Owner != nil {
|
||||
attr.Owner = *(*fuse.Owner)(m.options.Owner)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *fileSystemMount) fillEntry(out *fuse.EntryOut) {
|
||||
splitDuration(m.options.EntryTimeout, &out.EntryValid, &out.EntryValidNsec)
|
||||
splitDuration(m.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
|
||||
m.setOwner(&out.Attr)
|
||||
if out.Mode&fuse.S_IFDIR == 0 && out.Nlink == 0 {
|
||||
out.Nlink = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (m *fileSystemMount) fillAttr(out *fuse.AttrOut, nodeId uint64) {
|
||||
splitDuration(m.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
|
||||
m.setOwner(&out.Attr)
|
||||
if out.Ino == 0 {
|
||||
out.Ino = nodeId
|
||||
}
|
||||
}
|
||||
|
||||
func (m *fileSystemMount) getOpenedFile(h uint64) *openedFile {
|
||||
var b *openedFile
|
||||
if h != 0 {
|
||||
b = (*openedFile)(unsafe.Pointer(m.openFiles.Decode(h)))
|
||||
}
|
||||
|
||||
if b != nil && m.connector.debug && b.WithFlags.Description != "" {
|
||||
log.Printf("File %d = %q", h, b.WithFlags.Description)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (m *fileSystemMount) unregisterFileHandle(handle uint64, node *Inode) *openedFile {
|
||||
_, obj := m.openFiles.Forget(handle, 1)
|
||||
opened := (*openedFile)(unsafe.Pointer(obj))
|
||||
node.openFilesMutex.Lock()
|
||||
idx := -1
|
||||
for i, v := range node.openFiles {
|
||||
if v == opened {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
l := len(node.openFiles)
|
||||
if idx == l-1 {
|
||||
node.openFiles[idx] = nil
|
||||
} else {
|
||||
node.openFiles[idx] = node.openFiles[l-1]
|
||||
}
|
||||
node.openFiles = node.openFiles[:l-1]
|
||||
node.openFilesMutex.Unlock()
|
||||
|
||||
return opened
|
||||
}
|
||||
|
||||
func (m *fileSystemMount) registerFileHandle(node *Inode, dir *connectorDir, f File, flags uint32) (uint64, *openedFile) {
|
||||
node.openFilesMutex.Lock()
|
||||
b := &openedFile{
|
||||
dir: dir,
|
||||
WithFlags: WithFlags{
|
||||
File: f,
|
||||
OpenFlags: flags,
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
withFlags, ok := f.(*WithFlags)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
b.WithFlags.File = withFlags.File
|
||||
b.WithFlags.FuseFlags |= withFlags.FuseFlags
|
||||
b.WithFlags.Description += withFlags.Description
|
||||
f = withFlags.File
|
||||
}
|
||||
|
||||
if b.WithFlags.File != nil {
|
||||
b.WithFlags.File.SetInode(node)
|
||||
}
|
||||
node.openFiles = append(node.openFiles, b)
|
||||
handle, _ := m.openFiles.Register(&b.handled)
|
||||
node.openFilesMutex.Unlock()
|
||||
return handle, b
|
||||
}
|
||||
|
||||
// Creates a return entry for a non-existent path.
|
||||
func (m *fileSystemMount) negativeEntry(out *fuse.EntryOut) bool {
|
||||
if m.options.NegativeTimeout > 0.0 {
|
||||
out.NodeId = 0
|
||||
splitDuration(m.options.NegativeTimeout, &out.EntryValid, &out.EntryValidNsec)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
472
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsops.go
generated
vendored
Normal file
472
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsops.go
generated
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
package nodefs
|
||||
|
||||
// This file contains FileSystemConnector's implementation of
|
||||
// RawFileSystem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// Returns the RawFileSystem so it can be mounted.
|
||||
func (c *FileSystemConnector) RawFS() fuse.RawFileSystem {
|
||||
return (*rawBridge)(c)
|
||||
}
|
||||
|
||||
type rawBridge FileSystemConnector
|
||||
|
||||
func (c *rawBridge) Fsync(input *fuse.FsyncIn) fuse.Status {
|
||||
node := c.toInode(input.NodeId)
|
||||
opened := node.mount.getOpenedFile(input.Fh)
|
||||
|
||||
if opened != nil {
|
||||
return opened.WithFlags.File.Fsync(int(input.FsyncFlags))
|
||||
}
|
||||
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (c *rawBridge) SetDebug(debug bool) {
|
||||
c.fsConn().SetDebug(debug)
|
||||
}
|
||||
|
||||
func (c *rawBridge) FsyncDir(input *fuse.FsyncIn) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (c *rawBridge) fsConn() *FileSystemConnector {
|
||||
return (*FileSystemConnector)(c)
|
||||
}
|
||||
|
||||
func (c *rawBridge) String() string {
|
||||
if c.rootNode == nil || c.rootNode.mount == nil {
|
||||
return "go-fuse:unmounted"
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("%T", c.rootNode.Node())
|
||||
name = strings.TrimLeft(name, "*")
|
||||
return name
|
||||
}
|
||||
|
||||
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) {
|
||||
code = mount.mountInode.Node().GetAttr(out, nil, nil)
|
||||
if !code.Ok() {
|
||||
log.Println("Root getattr should not return error", code)
|
||||
out.Mode = fuse.S_IFDIR | 0755
|
||||
return mount.mountInode, fuse.OK
|
||||
}
|
||||
|
||||
return mount.mountInode, fuse.OK
|
||||
}
|
||||
|
||||
// internalLookup executes a lookup without affecting NodeId reference counts.
|
||||
func (c *FileSystemConnector) internalLookup(out *fuse.Attr, parent *Inode, name string, header *fuse.InHeader) (node *Inode, code fuse.Status) {
|
||||
|
||||
// We may already know the child because it was created using Create or Mkdir,
|
||||
// from an earlier lookup, or because the nodes were created in advance
|
||||
// (in-memory filesystems).
|
||||
child := parent.GetChild(name)
|
||||
|
||||
if child != nil && child.mountPoint != nil {
|
||||
return c.lookupMountUpdate(out, child.mountPoint)
|
||||
}
|
||||
|
||||
if child != nil {
|
||||
parent = nil
|
||||
}
|
||||
if child != nil {
|
||||
code = child.fsInode.GetAttr(out, nil, &header.Context)
|
||||
} else {
|
||||
child, code = parent.fsInode.Lookup(out, name, &header.Context)
|
||||
}
|
||||
|
||||
return child, code
|
||||
}
|
||||
|
||||
func (c *rawBridge) Lookup(header *fuse.InHeader, name string, out *fuse.EntryOut) (code fuse.Status) {
|
||||
parent := c.toInode(header.NodeId)
|
||||
if !parent.IsDir() {
|
||||
log.Printf("Lookup %q called on non-Directory node %d", name, header.NodeId)
|
||||
return fuse.ENOTDIR
|
||||
}
|
||||
outAttr := (*fuse.Attr)(&out.Attr)
|
||||
child, code := c.fsConn().internalLookup(outAttr, parent, name, header)
|
||||
if code == fuse.ENOENT && parent.mount.negativeEntry(out) {
|
||||
return fuse.OK
|
||||
}
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
if child == nil {
|
||||
log.Println("Lookup returned fuse.OK with nil child", name)
|
||||
}
|
||||
|
||||
child.mount.fillEntry(out)
|
||||
out.NodeId, out.Generation = c.fsConn().lookupUpdate(child)
|
||||
if out.Ino == 0 {
|
||||
out.Ino = out.NodeId
|
||||
}
|
||||
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (c *rawBridge) Forget(nodeID, nlookup uint64) {
|
||||
c.fsConn().forgetUpdate(nodeID, int(nlookup))
|
||||
}
|
||||
|
||||
func (c *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse.Status) {
|
||||
node := c.toInode(input.NodeId)
|
||||
|
||||
var f File
|
||||
if input.Flags()&fuse.FUSE_GETATTR_FH != 0 {
|
||||
if opened := node.mount.getOpenedFile(input.Fh()); opened != nil {
|
||||
f = opened.WithFlags.File
|
||||
}
|
||||
}
|
||||
|
||||
dest := (*fuse.Attr)(&out.Attr)
|
||||
code = node.fsInode.GetAttr(dest, f, &input.Context)
|
||||
if !code.Ok() {
|
||||
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
|
||||
}
|
||||
|
||||
func (c *rawBridge) OpenDir(input *fuse.OpenIn, out *fuse.OpenOut) (code fuse.Status) {
|
||||
node := c.toInode(input.NodeId)
|
||||
stream, err := node.fsInode.OpenDir(&input.Context)
|
||||
if err != fuse.OK {
|
||||
return err
|
||||
}
|
||||
stream = append(stream, node.getMountDirEntries()...)
|
||||
de := &connectorDir{
|
||||
node: node.Node(),
|
||||
stream: append(stream,
|
||||
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "."},
|
||||
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".."}),
|
||||
rawFS: c,
|
||||
}
|
||||
h, opened := node.mount.registerFileHandle(node, de, nil, input.Flags)
|
||||
out.OpenFlags = opened.FuseFlags
|
||||
out.Fh = h
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (c *rawBridge) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
|
||||
node := c.toInode(input.NodeId)
|
||||
opened := node.mount.getOpenedFile(input.Fh)
|
||||
return opened.dir.ReadDir(input, out)
|
||||
}
|
||||
|
||||
func (c *rawBridge) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
|
||||
node := c.toInode(input.NodeId)
|
||||
opened := node.mount.getOpenedFile(input.Fh)
|
||||
return opened.dir.ReadDirPlus(input, out)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Open(input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) {
|
||||
node := c.toInode(input.NodeId)
|
||||
f, code := node.fsInode.Open(input.Flags, &input.Context)
|
||||
if !code.Ok() || f == nil {
|
||||
return code
|
||||
}
|
||||
h, opened := node.mount.registerFileHandle(node, nil, f, input.Flags)
|
||||
out.OpenFlags = opened.FuseFlags
|
||||
out.Fh = h
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (c *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse.Status) {
|
||||
node := c.toInode(input.NodeId)
|
||||
|
||||
var f File
|
||||
if input.Valid&fuse.FATTR_FH != 0 {
|
||||
opened := node.mount.getOpenedFile(input.Fh)
|
||||
f = opened.WithFlags.File
|
||||
}
|
||||
|
||||
if code.Ok() && input.Valid&fuse.FATTR_MODE != 0 {
|
||||
permissions := uint32(07777) & input.Mode
|
||||
code = node.fsInode.Chmod(f, permissions, &input.Context)
|
||||
}
|
||||
if code.Ok() && (input.Valid&(fuse.FATTR_UID|fuse.FATTR_GID) != 0) {
|
||||
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)
|
||||
}
|
||||
if code.Ok() && (input.Valid&(fuse.FATTR_ATIME|fuse.FATTR_MTIME|fuse.FATTR_ATIME_NOW|fuse.FATTR_MTIME_NOW) != 0) {
|
||||
now := time.Now()
|
||||
var atime *time.Time
|
||||
var mtime *time.Time
|
||||
|
||||
if input.Valid&fuse.FATTR_ATIME != 0 {
|
||||
if input.Valid&fuse.FATTR_ATIME_NOW != 0 {
|
||||
atime = &now
|
||||
} else {
|
||||
t := time.Unix(int64(input.Atime), int64(input.Atimensec))
|
||||
atime = &t
|
||||
}
|
||||
}
|
||||
|
||||
if input.Valid&fuse.FATTR_MTIME != 0 {
|
||||
if input.Valid&fuse.FATTR_MTIME_NOW != 0 {
|
||||
mtime = &now
|
||||
} else {
|
||||
t := time.Unix(int64(input.Mtime), int64(input.Mtimensec))
|
||||
mtime = &t
|
||||
}
|
||||
}
|
||||
|
||||
code = node.fsInode.Utimens(f, atime, mtime, &input.Context)
|
||||
}
|
||||
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
|
||||
// Must call GetAttr(); the filesystem may override some of
|
||||
// the changes we effect here.
|
||||
attr := (*fuse.Attr)(&out.Attr)
|
||||
code = node.fsInode.GetAttr(attr, nil, &input.Context)
|
||||
if code.Ok() {
|
||||
node.mount.fillAttr(out, input.NodeId)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *rawBridge) Fallocate(input *fuse.FallocateIn) (code fuse.Status) {
|
||||
n := c.toInode(input.NodeId)
|
||||
opened := n.mount.getOpenedFile(input.Fh)
|
||||
|
||||
return n.fsInode.Fallocate(opened, input.Offset, input.Length, input.Mode, &input.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Readlink(header *fuse.InHeader) (out []byte, code fuse.Status) {
|
||||
n := c.toInode(header.NodeId)
|
||||
return n.fsInode.Readlink(&header.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Mknod(input *fuse.MknodIn, name string, out *fuse.EntryOut) (code fuse.Status) {
|
||||
parent := c.toInode(input.NodeId)
|
||||
|
||||
child, code := parent.fsInode.Mknod(name, input.Mode, uint32(input.Rdev), &input.Context)
|
||||
if code.Ok() {
|
||||
c.childLookup(out, child, &input.Context)
|
||||
code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &input.Context)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *rawBridge) Mkdir(input *fuse.MkdirIn, name string, out *fuse.EntryOut) (code fuse.Status) {
|
||||
parent := c.toInode(input.NodeId)
|
||||
|
||||
child, code := parent.fsInode.Mkdir(name, input.Mode, &input.Context)
|
||||
if code.Ok() {
|
||||
c.childLookup(out, child, &input.Context)
|
||||
code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &input.Context)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *rawBridge) Unlink(header *fuse.InHeader, name string) (code fuse.Status) {
|
||||
parent := c.toInode(header.NodeId)
|
||||
return parent.fsInode.Unlink(name, &header.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Rmdir(header *fuse.InHeader, name string) (code fuse.Status) {
|
||||
parent := c.toInode(header.NodeId)
|
||||
return parent.fsInode.Rmdir(name, &header.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Symlink(header *fuse.InHeader, pointedTo string, linkName string, out *fuse.EntryOut) (code fuse.Status) {
|
||||
parent := c.toInode(header.NodeId)
|
||||
|
||||
child, code := parent.fsInode.Symlink(linkName, pointedTo, &header.Context)
|
||||
if code.Ok() {
|
||||
c.childLookup(out, child, &header.Context)
|
||||
code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &header.Context)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *rawBridge) Rename(input *fuse.RenameIn, oldName string, newName string) (code fuse.Status) {
|
||||
oldParent := c.toInode(input.NodeId)
|
||||
|
||||
child := oldParent.GetChild(oldName)
|
||||
if child == nil {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
if child.mountPoint != nil {
|
||||
return fuse.EBUSY
|
||||
}
|
||||
|
||||
newParent := c.toInode(input.Newdir)
|
||||
if oldParent.mount != newParent.mount {
|
||||
return fuse.EXDEV
|
||||
}
|
||||
|
||||
return oldParent.fsInode.Rename(oldName, newParent.fsInode, newName, &input.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Link(input *fuse.LinkIn, name string, out *fuse.EntryOut) (code fuse.Status) {
|
||||
existing := c.toInode(input.Oldnodeid)
|
||||
parent := c.toInode(input.NodeId)
|
||||
|
||||
if existing.mount != parent.mount {
|
||||
return fuse.EXDEV
|
||||
}
|
||||
|
||||
child, code := parent.fsInode.Link(name, existing.fsInode, &input.Context)
|
||||
if code.Ok() {
|
||||
c.childLookup(out, child, &input.Context)
|
||||
code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &input.Context)
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *rawBridge) Access(input *fuse.AccessIn) (code fuse.Status) {
|
||||
n := c.toInode(input.NodeId)
|
||||
return n.fsInode.Access(input.Mask, &input.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOut) (code fuse.Status) {
|
||||
parent := c.toInode(input.NodeId)
|
||||
f, child, code := parent.fsInode.Create(name, uint32(input.Flags), input.Mode, &input.Context)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
|
||||
c.childLookup(&out.EntryOut, child, &input.Context)
|
||||
handle, opened := parent.mount.registerFileHandle(child, nil, f, input.Flags)
|
||||
|
||||
out.OpenOut.OpenFlags = opened.FuseFlags
|
||||
out.OpenOut.Fh = handle
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *rawBridge) Release(input *fuse.ReleaseIn) {
|
||||
if input.Fh != 0 {
|
||||
node := c.toInode(input.NodeId)
|
||||
opened := node.mount.unregisterFileHandle(input.Fh, node)
|
||||
opened.WithFlags.File.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *rawBridge) ReleaseDir(input *fuse.ReleaseIn) {
|
||||
if input.Fh != 0 {
|
||||
node := c.toInode(input.NodeId)
|
||||
node.mount.unregisterFileHandle(input.Fh, node)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *rawBridge) GetXAttrSize(header *fuse.InHeader, attribute string) (sz int, code fuse.Status) {
|
||||
node := c.toInode(header.NodeId)
|
||||
data, errno := node.fsInode.GetXAttr(attribute, &header.Context)
|
||||
return len(data), errno
|
||||
}
|
||||
|
||||
func (c *rawBridge) GetXAttrData(header *fuse.InHeader, attribute string) (data []byte, code fuse.Status) {
|
||||
node := c.toInode(header.NodeId)
|
||||
return node.fsInode.GetXAttr(attribute, &header.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) RemoveXAttr(header *fuse.InHeader, attr string) fuse.Status {
|
||||
node := c.toInode(header.NodeId)
|
||||
return node.fsInode.RemoveXAttr(attr, &header.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) SetXAttr(input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
|
||||
node := c.toInode(input.NodeId)
|
||||
return node.fsInode.SetXAttr(attr, data, int(input.Flags), &input.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) ListXAttr(header *fuse.InHeader) (data []byte, code fuse.Status) {
|
||||
node := c.toInode(header.NodeId)
|
||||
attrs, code := node.fsInode.ListXAttr(&header.Context)
|
||||
if code != fuse.OK {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer([]byte{})
|
||||
for _, v := range attrs {
|
||||
b.Write([]byte(v))
|
||||
b.WriteByte(0)
|
||||
}
|
||||
|
||||
return b.Bytes(), code
|
||||
}
|
||||
|
||||
////////////////
|
||||
// files.
|
||||
|
||||
func (c *rawBridge) Write(input *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) {
|
||||
node := c.toInode(input.NodeId)
|
||||
opened := node.mount.getOpenedFile(input.Fh)
|
||||
|
||||
var f File
|
||||
if opened != nil {
|
||||
f = opened.WithFlags.File
|
||||
}
|
||||
|
||||
return node.Node().Write(f, data, int64(input.Offset), &input.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) Read(input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) {
|
||||
node := c.toInode(input.NodeId)
|
||||
opened := node.mount.getOpenedFile(input.Fh)
|
||||
|
||||
var f File
|
||||
if opened != nil {
|
||||
f = opened.WithFlags.File
|
||||
}
|
||||
|
||||
return node.Node().Read(f, buf, int64(input.Offset), &input.Context)
|
||||
}
|
||||
|
||||
func (c *rawBridge) StatFs(header *fuse.InHeader, out *fuse.StatfsOut) fuse.Status {
|
||||
node := c.toInode(header.NodeId)
|
||||
s := node.Node().StatFs()
|
||||
if s == nil {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
*out = *s
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (c *rawBridge) Flush(input *fuse.FlushIn) fuse.Status {
|
||||
node := c.toInode(input.NodeId)
|
||||
opened := node.mount.getOpenedFile(input.Fh)
|
||||
|
||||
if opened != nil {
|
||||
return opened.WithFlags.File.Flush()
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
20
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fuse.go
generated
vendored
Normal file
20
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fuse.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
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
|
||||
}
|
||||
return s, conn, nil
|
||||
}
|
||||
147
vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle.go
generated
vendored
Normal file
147
vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// HandleMap translates objects in Go space to 64-bit handles that can
|
||||
// be given out to -say- the linux kernel as NodeIds.
|
||||
//
|
||||
// The 32 bits version of this is a threadsafe wrapper around a map.
|
||||
//
|
||||
// To use it, include "handled" as first member of the structure
|
||||
// you wish to export.
|
||||
//
|
||||
// This structure is thread-safe.
|
||||
type handleMap interface {
|
||||
// Register stores "obj" and returns a unique (NodeId, generation) tuple.
|
||||
Register(obj *handled) (handle, generation uint64)
|
||||
Count() int
|
||||
// Decode retrieves a stored object from its 64-bit handle.
|
||||
Decode(uint64) *handled
|
||||
// Forget decrements the reference counter for "handle" by "count" and drops
|
||||
// the object if the refcount reaches zero.
|
||||
// Returns a boolean whether the object was dropped and the object itself.
|
||||
Forget(handle uint64, count int) (bool, *handled)
|
||||
// Handle gets the object's NodeId.
|
||||
Handle(obj *handled) uint64
|
||||
// Has checks if NodeId is stored.
|
||||
Has(uint64) bool
|
||||
}
|
||||
|
||||
type handled struct {
|
||||
check uint32
|
||||
handle uint64
|
||||
count int
|
||||
}
|
||||
|
||||
func (h *handled) verify() {
|
||||
if h.count < 0 {
|
||||
log.Panicf("negative lookup count %d", h.count)
|
||||
}
|
||||
if (h.count == 0) != (h.handle == 0) {
|
||||
log.Panicf("registration mismatch: lookup %d id %d", h.count, h.handle)
|
||||
}
|
||||
}
|
||||
|
||||
const _ALREADY_MSG = "Object already has a handle"
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// portable version using 32 bit integers.
|
||||
|
||||
type portableHandleMap struct {
|
||||
sync.RWMutex
|
||||
// The generation counter is incremented each time a NodeId is reused,
|
||||
// hence the (NodeId, Generation) tuple is always unique.
|
||||
generation uint64
|
||||
// Number of currently used handles
|
||||
used int
|
||||
// Array of Go objects indexed by NodeId
|
||||
handles []*handled
|
||||
// Free slots in the "handles" array
|
||||
freeIds []uint64
|
||||
}
|
||||
|
||||
func newPortableHandleMap() *portableHandleMap {
|
||||
return &portableHandleMap{
|
||||
// Avoid handing out ID 0 and 1.
|
||||
handles: []*handled{nil, nil},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *portableHandleMap) Register(obj *handled) (handle, generation uint64) {
|
||||
m.Lock()
|
||||
if obj.count == 0 {
|
||||
if obj.check != 0 {
|
||||
panic(_ALREADY_MSG)
|
||||
}
|
||||
|
||||
if len(m.freeIds) == 0 {
|
||||
handle = uint64(len(m.handles))
|
||||
m.handles = append(m.handles, obj)
|
||||
} else {
|
||||
handle = m.freeIds[len(m.freeIds)-1]
|
||||
m.freeIds = m.freeIds[:len(m.freeIds)-1]
|
||||
m.generation++
|
||||
m.handles[handle] = obj
|
||||
}
|
||||
m.used++
|
||||
obj.handle = handle
|
||||
} else {
|
||||
handle = obj.handle
|
||||
}
|
||||
obj.count++
|
||||
generation = m.generation
|
||||
m.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (m *portableHandleMap) Handle(obj *handled) (h uint64) {
|
||||
m.RLock()
|
||||
if obj.count == 0 {
|
||||
h = 0
|
||||
} else {
|
||||
h = obj.handle
|
||||
}
|
||||
m.RUnlock()
|
||||
return h
|
||||
}
|
||||
|
||||
func (m *portableHandleMap) Count() int {
|
||||
m.RLock()
|
||||
c := m.used
|
||||
m.RUnlock()
|
||||
return c
|
||||
}
|
||||
|
||||
func (m *portableHandleMap) Decode(h uint64) *handled {
|
||||
m.RLock()
|
||||
v := m.handles[h]
|
||||
m.RUnlock()
|
||||
return v
|
||||
}
|
||||
|
||||
func (m *portableHandleMap) Forget(h uint64, count int) (forgotten bool, obj *handled) {
|
||||
m.Lock()
|
||||
obj = m.handles[h]
|
||||
obj.count -= count
|
||||
if obj.count < 0 {
|
||||
log.Panicf("underflow: handle %d, count %d, object %d", h, count, obj.count)
|
||||
} else if obj.count == 0 {
|
||||
m.handles[h] = nil
|
||||
m.freeIds = append(m.freeIds, h)
|
||||
m.used--
|
||||
forgotten = true
|
||||
obj.handle = 0
|
||||
}
|
||||
m.Unlock()
|
||||
return forgotten, obj
|
||||
}
|
||||
|
||||
func (m *portableHandleMap) Has(h uint64) bool {
|
||||
m.RLock()
|
||||
ok := m.handles[h] != nil
|
||||
m.RUnlock()
|
||||
return ok
|
||||
}
|
||||
127
vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle_test.go
generated
vendored
Normal file
127
vendor/github.com/hanwen/go-fuse/fuse/nodefs/handle_test.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func markSeen(t *testing.T, substr string) {
|
||||
if r := recover(); r != nil {
|
||||
s := r.(string)
|
||||
if strings.Contains(s, substr) {
|
||||
t.Log("expected recovery from: ", r)
|
||||
} else {
|
||||
panic(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMapLookupCount(t *testing.T) {
|
||||
for _, portable := range []bool{true, false} {
|
||||
t.Log("portable:", portable)
|
||||
v := new(handled)
|
||||
hm := newPortableHandleMap()
|
||||
h1, g1 := hm.Register(v)
|
||||
h2, g2 := hm.Register(v)
|
||||
|
||||
if h1 != h2 {
|
||||
t.Fatalf("double register should reuse handle: got %d want %d.", h2, h1)
|
||||
}
|
||||
|
||||
if g1 != g2 {
|
||||
t.Fatalf("double register should reuse generation: got %d want %d.", g2, g1)
|
||||
}
|
||||
|
||||
hm.Register(v)
|
||||
|
||||
forgotten, obj := hm.Forget(h1, 1)
|
||||
if forgotten {
|
||||
t.Fatalf("single forget unref forget object.")
|
||||
}
|
||||
|
||||
if obj != v {
|
||||
t.Fatalf("should return input object.")
|
||||
}
|
||||
|
||||
if !hm.Has(h1) {
|
||||
t.Fatalf("handlemap.Has() returned false for live object.")
|
||||
}
|
||||
|
||||
forgotten, obj = hm.Forget(h1, 2)
|
||||
if !forgotten {
|
||||
t.Fatalf("unref did not forget object.")
|
||||
}
|
||||
|
||||
if obj != v {
|
||||
t.Fatalf("should return input object.")
|
||||
}
|
||||
|
||||
if hm.Has(h1) {
|
||||
t.Fatalf("handlemap.Has() returned false for live object.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMapBasic(t *testing.T) {
|
||||
v := new(handled)
|
||||
hm := newPortableHandleMap()
|
||||
h, _ := hm.Register(v)
|
||||
t.Logf("Got handle 0x%x", h)
|
||||
if !hm.Has(h) {
|
||||
t.Fatal("Does not have handle")
|
||||
}
|
||||
if hm.Handle(v) != h {
|
||||
t.Fatalf("handle mismatch, got %x want %x", hm.Handle(v), h)
|
||||
}
|
||||
if hm.Decode(h) != v {
|
||||
t.Fatal("address mismatch")
|
||||
}
|
||||
if hm.Count() != 1 {
|
||||
t.Fatal("count error")
|
||||
}
|
||||
hm.Forget(h, 1)
|
||||
if hm.Count() != 0 {
|
||||
t.Fatal("count error")
|
||||
}
|
||||
if hm.Has(h) {
|
||||
t.Fatal("Still has handle")
|
||||
}
|
||||
if v.check != 0 {
|
||||
t.Errorf("forgotten object still has a check.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMapMultiple(t *testing.T) {
|
||||
hm := newPortableHandleMap()
|
||||
for i := 0; i < 10; i++ {
|
||||
v := &handled{}
|
||||
h, _ := hm.Register(v)
|
||||
if hm.Decode(h) != v {
|
||||
t.Fatal("address mismatch")
|
||||
}
|
||||
if hm.Count() != i+1 {
|
||||
t.Fatal("count error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMapGeneration(t *testing.T) {
|
||||
hm := newPortableHandleMap()
|
||||
|
||||
h1, g1 := hm.Register(&handled{})
|
||||
|
||||
forgotten, _ := hm.Forget(h1, 1)
|
||||
if !forgotten {
|
||||
t.Fatalf("unref did not forget object.")
|
||||
}
|
||||
|
||||
h2, g2 := hm.Register(&handled{})
|
||||
|
||||
if h1 != h2 {
|
||||
t.Fatalf("register should reuse handle: got %d want %d.", h2, h1)
|
||||
}
|
||||
|
||||
if g1 >= g2 {
|
||||
t.Fatalf("register should increase generation: got %d want greater than %d.", g2, g1)
|
||||
}
|
||||
}
|
||||
285
vendor/github.com/hanwen/go-fuse/fuse/nodefs/inode.go
generated
vendored
Normal file
285
vendor/github.com/hanwen/go-fuse/fuse/nodefs/inode.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"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
|
||||
// Inode object is paired with a Node object, which file system
|
||||
// implementers should supply.
|
||||
type Inode struct {
|
||||
handled handled
|
||||
|
||||
// Generation number of the inode. Each (re)use of an inode
|
||||
// should have a unique generation number.
|
||||
generation uint64
|
||||
|
||||
// Number of open files and its protection.
|
||||
openFilesMutex sync.Mutex
|
||||
openFiles []*openedFile
|
||||
|
||||
fsInode Node
|
||||
|
||||
// Each inode belongs to exactly one fileSystemMount. This
|
||||
// pointer is constant during the lifetime, except upon
|
||||
// Unmount() when it is set to nil.
|
||||
mount *fileSystemMount
|
||||
|
||||
// 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.
|
||||
mountPoint *fileSystemMount
|
||||
}
|
||||
|
||||
func newInode(isDir bool, fsNode Node) *Inode {
|
||||
me := new(Inode)
|
||||
me.parents = map[parentData]struct{}{}
|
||||
if isDir {
|
||||
me.children = make(map[string]*Inode, initDirSize)
|
||||
}
|
||||
me.fsInode = fsNode
|
||||
me.fsInode.SetInode(me)
|
||||
return me
|
||||
}
|
||||
|
||||
// public methods.
|
||||
|
||||
// Returns any open file, preferably a r/w one.
|
||||
func (n *Inode) AnyFile() (file File) {
|
||||
n.openFilesMutex.Lock()
|
||||
for _, f := range n.openFiles {
|
||||
if file == nil || f.WithFlags.OpenFlags&fuse.O_ANYWRITE != 0 {
|
||||
file = f.WithFlags.File
|
||||
}
|
||||
}
|
||||
n.openFilesMutex.Unlock()
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
// Children returns all children of this inode.
|
||||
func (n *Inode) Children() (out map[string]*Inode) {
|
||||
n.mount.treeLock.RLock()
|
||||
out = make(map[string]*Inode, len(n.children))
|
||||
for k, v := range n.children {
|
||||
out[k] = v
|
||||
}
|
||||
n.mount.treeLock.RUnlock()
|
||||
|
||||
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) {
|
||||
n.mount.treeLock.RLock()
|
||||
out = map[string]*Inode{}
|
||||
for k, v := range n.children {
|
||||
if v.mount == n.mount {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
n.mount.treeLock.RUnlock()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Node returns the file-system specific node.
|
||||
func (n *Inode) Node() Node {
|
||||
return n.fsInode
|
||||
}
|
||||
|
||||
// Files() returns an opens file that have bits in common with the
|
||||
// give mask. Use mask==0 to return all files.
|
||||
func (n *Inode) Files(mask uint32) (files []WithFlags) {
|
||||
n.openFilesMutex.Lock()
|
||||
for _, f := range n.openFiles {
|
||||
if mask == 0 || f.WithFlags.OpenFlags&mask != 0 {
|
||||
files = append(files, f.WithFlags)
|
||||
}
|
||||
}
|
||||
n.openFilesMutex.Unlock()
|
||||
return files
|
||||
}
|
||||
|
||||
// IsDir returns true if this is a directory.
|
||||
func (n *Inode) IsDir() bool {
|
||||
return n.children != nil
|
||||
}
|
||||
|
||||
// NewChild adds a new child inode to this inode.
|
||||
func (n *Inode) NewChild(name string, isDir bool, fsi Node) *Inode {
|
||||
ch := newInode(isDir, fsi)
|
||||
ch.mount = n.mount
|
||||
n.AddChild(name, ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
// GetChild returns a child inode with the given name, or nil if it
|
||||
// does not exist.
|
||||
func (n *Inode) GetChild(name string) (child *Inode) {
|
||||
n.mount.treeLock.RLock()
|
||||
child = n.children[name]
|
||||
n.mount.treeLock.RUnlock()
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
// AddChild adds a child inode. The parent inode must be a directory
|
||||
// node.
|
||||
func (n *Inode) AddChild(name string, child *Inode) {
|
||||
if child == nil {
|
||||
log.Panicf("adding nil child as %q", name)
|
||||
}
|
||||
n.mount.treeLock.Lock()
|
||||
n.addChild(name, child)
|
||||
n.mount.treeLock.Unlock()
|
||||
}
|
||||
|
||||
// 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. These functions run under a lock, so they should
|
||||
// not do blocking operations.
|
||||
type TreeWatcher interface {
|
||||
OnAdd(parent *Inode, name string)
|
||||
OnRemove(parent *Inode, name string)
|
||||
}
|
||||
|
||||
// RmChild removes an inode by name, and returns it. It returns nil if
|
||||
// child does not exist.
|
||||
func (n *Inode) RmChild(name string) (ch *Inode) {
|
||||
n.mount.treeLock.Lock()
|
||||
ch = n.rmChild(name)
|
||||
n.mount.treeLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// private
|
||||
|
||||
// addChild adds "child" to our children under name "name".
|
||||
// Must be called with treeLock for the mount held.
|
||||
func (n *Inode) addChild(name string, child *Inode) {
|
||||
if paranoia {
|
||||
ch := n.children[name]
|
||||
if ch != nil {
|
||||
log.Panicf("Already have an Inode with same name: %v: %v", name, ch)
|
||||
}
|
||||
}
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
// Can only be called on untouched root inodes.
|
||||
func (n *Inode) mountFs(opts *Options) {
|
||||
n.mountPoint = &fileSystemMount{
|
||||
openFiles: newPortableHandleMap(),
|
||||
mountInode: n,
|
||||
options: opts,
|
||||
}
|
||||
n.mount = n.mountPoint
|
||||
}
|
||||
|
||||
// Must be called with treeLock held.
|
||||
func (n *Inode) canUnmount() bool {
|
||||
for _, v := range n.children {
|
||||
if v.mountPoint != nil {
|
||||
// This access may be out of date, but it is no
|
||||
// problem to err on the safe side.
|
||||
return false
|
||||
}
|
||||
if !v.canUnmount() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
n.openFilesMutex.Lock()
|
||||
ok := len(n.openFiles) == 0
|
||||
n.openFilesMutex.Unlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (n *Inode) getMountDirEntries() (out []fuse.DirEntry) {
|
||||
n.mount.treeLock.RLock()
|
||||
for k, v := range n.children {
|
||||
if v.mountPoint != nil {
|
||||
out = append(out, fuse.DirEntry{
|
||||
Name: k,
|
||||
Mode: fuse.S_IFDIR,
|
||||
})
|
||||
}
|
||||
}
|
||||
n.mount.treeLock.RUnlock()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
const initDirSize = 20
|
||||
|
||||
func (n *Inode) verify(cur *fileSystemMount) {
|
||||
n.handled.verify()
|
||||
if n.mountPoint != nil {
|
||||
if n != n.mountPoint.mountInode {
|
||||
log.Panicf("mountpoint mismatch %v %v", n, n.mountPoint.mountInode)
|
||||
}
|
||||
cur = n.mountPoint
|
||||
|
||||
cur.treeLock.Lock()
|
||||
defer cur.treeLock.Unlock()
|
||||
}
|
||||
if n.mount != cur {
|
||||
log.Panicf("n.mount not set correctly %v %v", n.mount, cur)
|
||||
}
|
||||
|
||||
for nm, ch := range n.children {
|
||||
if ch == nil {
|
||||
log.Panicf("Found nil child: %q", nm)
|
||||
}
|
||||
ch.verify(cur)
|
||||
}
|
||||
}
|
||||
99
vendor/github.com/hanwen/go-fuse/fuse/nodefs/lockingfile.go
generated
vendored
Normal file
99
vendor/github.com/hanwen/go-fuse/fuse/nodefs/lockingfile.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
type lockingFile struct {
|
||||
mu *sync.Mutex
|
||||
file File
|
||||
}
|
||||
|
||||
// NewLockingFile serializes operations an existing File.
|
||||
func NewLockingFile(mu *sync.Mutex, f File) File {
|
||||
return &lockingFile{
|
||||
mu: mu,
|
||||
file: f,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *lockingFile) SetInode(*Inode) {
|
||||
}
|
||||
|
||||
func (f *lockingFile) InnerFile() File {
|
||||
return f.file
|
||||
}
|
||||
|
||||
func (f *lockingFile) String() string {
|
||||
return fmt.Sprintf("lockingFile(%s)", f.file.String())
|
||||
}
|
||||
|
||||
func (f *lockingFile) Read(buf []byte, off int64) (fuse.ReadResult, fuse.Status) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Read(buf, off)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Write(data []byte, off int64) (uint32, fuse.Status) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Write(data, off)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Flush() fuse.Status {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Flush()
|
||||
}
|
||||
|
||||
func (f *lockingFile) Release() {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.file.Release()
|
||||
}
|
||||
|
||||
func (f *lockingFile) GetAttr(a *fuse.Attr) fuse.Status {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.GetAttr(a)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Fsync(flags int) (code fuse.Status) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Fsync(flags)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Utimens(atime *time.Time, mtime *time.Time) fuse.Status {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Utimens(atime, mtime)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Truncate(size uint64) fuse.Status {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Truncate(size)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Chown(uid uint32, gid uint32) fuse.Status {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Chown(uid, gid)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Chmod(perms uint32) fuse.Status {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Chmod(perms)
|
||||
}
|
||||
|
||||
func (f *lockingFile) Allocate(off uint64, size uint64, mode uint32) (code fuse.Status) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.file.Allocate(off, size, mode)
|
||||
}
|
||||
228
vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode.go
generated
vendored
Normal file
228
vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode.go
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// NewMemNodeFSRoot creates an in-memory node-based filesystem. Files
|
||||
// are written into a backing store under the given prefix.
|
||||
func NewMemNodeFSRoot(prefix string) Node {
|
||||
fs := &memNodeFs{
|
||||
backingStorePrefix: prefix,
|
||||
}
|
||||
fs.root = fs.newNode()
|
||||
return fs.root
|
||||
}
|
||||
|
||||
type memNodeFs struct {
|
||||
backingStorePrefix string
|
||||
root *memNode
|
||||
|
||||
mutex sync.Mutex
|
||||
nextFree int
|
||||
}
|
||||
|
||||
func (fs *memNodeFs) String() string {
|
||||
return fmt.Sprintf("MemNodeFs(%s)", fs.backingStorePrefix)
|
||||
}
|
||||
|
||||
func (fs *memNodeFs) Root() Node {
|
||||
return fs.root
|
||||
}
|
||||
|
||||
func (fs *memNodeFs) SetDebug(bool) {
|
||||
}
|
||||
|
||||
func (fs *memNodeFs) OnMount(*FileSystemConnector) {
|
||||
}
|
||||
|
||||
func (fs *memNodeFs) OnUnmount() {
|
||||
}
|
||||
|
||||
func (fs *memNodeFs) newNode() *memNode {
|
||||
fs.mutex.Lock()
|
||||
id := fs.nextFree
|
||||
fs.nextFree++
|
||||
fs.mutex.Unlock()
|
||||
n := &memNode{
|
||||
Node: NewDefaultNode(),
|
||||
fs: fs,
|
||||
id: id,
|
||||
}
|
||||
now := time.Now()
|
||||
n.info.SetTimes(&now, &now, &now)
|
||||
n.info.Mode = fuse.S_IFDIR | 0777
|
||||
return n
|
||||
}
|
||||
|
||||
func (fs *memNodeFs) Filename(n *Inode) string {
|
||||
mn := n.Node().(*memNode)
|
||||
return mn.filename()
|
||||
}
|
||||
|
||||
type memNode struct {
|
||||
Node
|
||||
fs *memNodeFs
|
||||
id int
|
||||
|
||||
link string
|
||||
info fuse.Attr
|
||||
}
|
||||
|
||||
func (n *memNode) filename() string {
|
||||
return fmt.Sprintf("%s%d", n.fs.backingStorePrefix, n.id)
|
||||
}
|
||||
|
||||
func (n *memNode) Deletable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *memNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
|
||||
return []byte(n.link), fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) StatFs() *fuse.StatfsOut {
|
||||
return &fuse.StatfsOut{}
|
||||
}
|
||||
|
||||
func (n *memNode) Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status) {
|
||||
ch := n.fs.newNode()
|
||||
ch.info.Mode = mode | fuse.S_IFDIR
|
||||
n.Inode().NewChild(name, true, ch)
|
||||
return ch.Inode(), fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
ch := n.Inode().RmChild(name)
|
||||
if ch == nil {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return n.Unlink(name, context)
|
||||
}
|
||||
|
||||
func (n *memNode) Symlink(name string, content string, context *fuse.Context) (newNode *Inode, code fuse.Status) {
|
||||
ch := n.fs.newNode()
|
||||
ch.info.Mode = fuse.S_IFLNK | 0777
|
||||
ch.link = content
|
||||
n.Inode().NewChild(name, false, ch)
|
||||
return ch.Inode(), fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
ch := n.Inode().RmChild(oldName)
|
||||
newParent.Inode().RmChild(newName)
|
||||
newParent.Inode().AddChild(newName, ch)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Link(name string, existing Node, context *fuse.Context) (*Inode, fuse.Status) {
|
||||
n.Inode().AddChild(name, existing.Inode())
|
||||
return existing.Inode(), fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, node *Inode, code fuse.Status) {
|
||||
ch := n.fs.newNode()
|
||||
ch.info.Mode = mode | fuse.S_IFREG
|
||||
|
||||
f, err := os.Create(ch.filename())
|
||||
if err != nil {
|
||||
return nil, nil, fuse.ToStatus(err)
|
||||
}
|
||||
n.Inode().NewChild(name, false, ch)
|
||||
return ch.newFile(f), ch.Inode(), fuse.OK
|
||||
}
|
||||
|
||||
type memNodeFile struct {
|
||||
File
|
||||
node *memNode
|
||||
}
|
||||
|
||||
func (n *memNodeFile) String() string {
|
||||
return fmt.Sprintf("memNodeFile(%s)", n.File.String())
|
||||
}
|
||||
|
||||
func (n *memNodeFile) InnerFile() File {
|
||||
return n.File
|
||||
}
|
||||
|
||||
func (n *memNodeFile) Flush() fuse.Status {
|
||||
code := n.File.Flush()
|
||||
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
|
||||
st := syscall.Stat_t{}
|
||||
err := syscall.Stat(n.node.filename(), &st)
|
||||
n.node.info.Size = uint64(st.Size)
|
||||
n.node.info.Blocks = uint64(st.Blocks)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (n *memNode) newFile(f *os.File) File {
|
||||
return &memNodeFile{
|
||||
File: NewLoopbackFile(f),
|
||||
node: n,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *memNode) Open(flags uint32, context *fuse.Context) (file File, code fuse.Status) {
|
||||
f, err := os.OpenFile(n.filename(), int(flags), 0666)
|
||||
if err != nil {
|
||||
return nil, fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
return n.newFile(f), fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) GetAttr(fi *fuse.Attr, file File, context *fuse.Context) (code fuse.Status) {
|
||||
*fi = n.info
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status) {
|
||||
if file != nil {
|
||||
code = file.Truncate(size)
|
||||
} else {
|
||||
err := os.Truncate(n.filename(), int64(size))
|
||||
code = fuse.ToStatus(err)
|
||||
}
|
||||
if code.Ok() {
|
||||
now := time.Now()
|
||||
n.info.SetTimes(nil, nil, &now)
|
||||
// TODO - should update mtime too?
|
||||
n.info.Size = size
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *memNode) Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
c := time.Now()
|
||||
n.info.SetTimes(atime, mtime, &c)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status) {
|
||||
n.info.Mode = (n.info.Mode &^ 07777) | perms
|
||||
now := time.Now()
|
||||
n.info.SetTimes(nil, nil, &now)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (n *memNode) Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
n.info.Uid = uid
|
||||
n.info.Gid = gid
|
||||
now := time.Now()
|
||||
n.info.SetTimes(nil, nil, &now)
|
||||
return fuse.OK
|
||||
}
|
||||
110
vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode_test.go
generated
vendored
Normal file
110
vendor/github.com/hanwen/go-fuse/fuse/nodefs/memnode_test.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
const testTtl = 100 * time.Millisecond
|
||||
|
||||
func setupMemNodeTest(t *testing.T) (wd string, root Node, clean func()) {
|
||||
tmp, err := ioutil.TempDir("", "go-fuse-memnode_test")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
back := tmp + "/backing"
|
||||
os.Mkdir(back, 0700)
|
||||
root = NewMemNodeFSRoot(back)
|
||||
mnt := tmp + "/mnt"
|
||||
os.Mkdir(mnt, 0700)
|
||||
|
||||
connector := NewFileSystemConnector(root,
|
||||
&Options{
|
||||
EntryTimeout: testTtl,
|
||||
AttrTimeout: testTtl,
|
||||
NegativeTimeout: 0.0,
|
||||
Debug: VerboseTest(),
|
||||
})
|
||||
state, err := fuse.NewServer(connector.RawFS(), mnt, &fuse.MountOptions{Debug: VerboseTest()})
|
||||
if err != nil {
|
||||
t.Fatal("NewServer", err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(wd + "/test")
|
||||
if string(content) != want {
|
||||
t.Fatalf("content mismatch: got %q, want %q", content, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemNodeFsBasic(t *testing.T) {
|
||||
wd, _, clean := setupMemNodeTest(t)
|
||||
defer clean()
|
||||
|
||||
err := ioutil.WriteFile(wd+"/test", []byte{42}, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteFile failed: %v", err)
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(wd + "/test")
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
if fi.Size() != 1 {
|
||||
t.Errorf("Size after write incorrect: got %d want 1", fi.Size())
|
||||
}
|
||||
|
||||
entries, err := ioutil.ReadDir(wd)
|
||||
if len(entries) != 1 || entries[0].Name() != "test" {
|
||||
t.Fatalf("Readdir got %v, expected 1 file named 'test'", entries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemNodeSetattr(t *testing.T) {
|
||||
wd, _, clean := setupMemNodeTest(t)
|
||||
defer clean()
|
||||
|
||||
f, err := os.OpenFile(wd+"/test", os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = f.Truncate(4096)
|
||||
if err != nil {
|
||||
t.Fatalf("Truncate failed: %v", err)
|
||||
}
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("Stat failed: %v", err)
|
||||
}
|
||||
if fi.Size() != 4096 {
|
||||
t.Errorf("Size should be 4096 after Truncate: %d", fi.Size())
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/hanwen/go-fuse/fuse/nodefs/nodefs.go
generated
vendored
Normal file
9
vendor/github.com/hanwen/go-fuse/fuse/nodefs/nodefs.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package nodefs
|
||||
|
||||
import "time"
|
||||
|
||||
func splitDuration(dt time.Duration, secs *uint64, nsecs *uint32) {
|
||||
ns := int64(dt)
|
||||
*nsecs = uint32(ns % 1e9)
|
||||
*secs = uint64(ns / 1e9)
|
||||
}
|
||||
14
vendor/github.com/hanwen/go-fuse/fuse/nodefs/print.go
generated
vendored
Normal file
14
vendor/github.com/hanwen/go-fuse/fuse/nodefs/print.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// String provides a debug string for the given file.
|
||||
func (f *WithFlags) String() string {
|
||||
return fmt.Sprintf("File %s (%s) %s %s",
|
||||
f.File, f.Description, fuse.FlagString(fuse.OpenFlagNames, int64(f.OpenFlags), "O_RDONLY"),
|
||||
fuse.FlagString(fuse.FuseOpenFlagNames, int64(f.FuseFlags), ""))
|
||||
}
|
||||
16
vendor/github.com/hanwen/go-fuse/fuse/nodefs/syscall_linux.go
generated
vendored
Normal file
16
vendor/github.com/hanwen/go-fuse/fuse/nodefs/syscall_linux.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package nodefs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// futimens - futimens(3) calls utimensat(2) with "pathname" set to null and
|
||||
// "flags" set to zero
|
||||
func futimens(fd int, times *[2]syscall.Timespec) (err error) {
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(fd), 0, uintptr(unsafe.Pointer(times)), uintptr(0), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = syscall.Errno(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
9
vendor/github.com/hanwen/go-fuse/fuse/nodefs/verbose_test.go
generated
vendored
Normal file
9
vendor/github.com/hanwen/go-fuse/fuse/nodefs/verbose_test.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package nodefs
|
||||
|
||||
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"
|
||||
}
|
||||
651
vendor/github.com/hanwen/go-fuse/fuse/opcode.go
generated
vendored
Normal file
651
vendor/github.com/hanwen/go-fuse/fuse/opcode.go
generated
vendored
Normal file
@@ -0,0 +1,651 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
_OP_LOOKUP = int32(1)
|
||||
_OP_FORGET = int32(2)
|
||||
_OP_GETATTR = int32(3)
|
||||
_OP_SETATTR = int32(4)
|
||||
_OP_READLINK = int32(5)
|
||||
_OP_SYMLINK = int32(6)
|
||||
_OP_MKNOD = int32(8)
|
||||
_OP_MKDIR = int32(9)
|
||||
_OP_UNLINK = int32(10)
|
||||
_OP_RMDIR = int32(11)
|
||||
_OP_RENAME = int32(12)
|
||||
_OP_LINK = int32(13)
|
||||
_OP_OPEN = int32(14)
|
||||
_OP_READ = int32(15)
|
||||
_OP_WRITE = int32(16)
|
||||
_OP_STATFS = int32(17)
|
||||
_OP_RELEASE = int32(18)
|
||||
_OP_FSYNC = int32(20)
|
||||
_OP_SETXATTR = int32(21)
|
||||
_OP_GETXATTR = int32(22)
|
||||
_OP_LISTXATTR = int32(23)
|
||||
_OP_REMOVEXATTR = int32(24)
|
||||
_OP_FLUSH = int32(25)
|
||||
_OP_INIT = int32(26)
|
||||
_OP_OPENDIR = int32(27)
|
||||
_OP_READDIR = int32(28)
|
||||
_OP_RELEASEDIR = int32(29)
|
||||
_OP_FSYNCDIR = int32(30)
|
||||
_OP_GETLK = int32(31)
|
||||
_OP_SETLK = int32(32)
|
||||
_OP_SETLKW = int32(33)
|
||||
_OP_ACCESS = int32(34)
|
||||
_OP_CREATE = int32(35)
|
||||
_OP_INTERRUPT = int32(36)
|
||||
_OP_BMAP = int32(37)
|
||||
_OP_DESTROY = int32(38)
|
||||
_OP_IOCTL = int32(39)
|
||||
_OP_POLL = int32(40)
|
||||
_OP_NOTIFY_REPLY = int32(41)
|
||||
_OP_BATCH_FORGET = int32(42)
|
||||
_OP_FALLOCATE = int32(43) // protocol version 19.
|
||||
_OP_READDIRPLUS = int32(44) // protocol version 21.
|
||||
_OP_FUSE_RENAME2 = int32(45) // protocol version 23.
|
||||
|
||||
// The following entries don't have to be compatible across Go-FUSE versions.
|
||||
_OP_NOTIFY_ENTRY = int32(100)
|
||||
_OP_NOTIFY_INODE = int32(101)
|
||||
_OP_NOTIFY_DELETE = int32(102) // protocol version 18
|
||||
|
||||
_OPCODE_COUNT = int32(103)
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
func doInit(server *Server, req *request) {
|
||||
input := (*InitIn)(req.inData)
|
||||
if input.Major != _FUSE_KERNEL_VERSION {
|
||||
log.Printf("Major versions does not match. Given %d, want %d\n", input.Major, _FUSE_KERNEL_VERSION)
|
||||
req.status = EIO
|
||||
return
|
||||
}
|
||||
if input.Minor < _MINIMUM_MINOR_VERSION {
|
||||
log.Printf("Minor version is less than we support. Given %d, want at least %d\n", input.Minor, _MINIMUM_MINOR_VERSION)
|
||||
req.status = EIO
|
||||
return
|
||||
}
|
||||
|
||||
server.reqMu.Lock()
|
||||
server.kernelSettings = *input
|
||||
server.kernelSettings.Flags = input.Flags & (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS |
|
||||
CAP_AUTO_INVAL_DATA | CAP_READDIRPLUS | CAP_NO_OPEN_SUPPORT)
|
||||
|
||||
if input.Minor >= 13 {
|
||||
server.setSplice()
|
||||
}
|
||||
server.reqMu.Unlock()
|
||||
|
||||
out := &InitOut{
|
||||
Major: _FUSE_KERNEL_VERSION,
|
||||
Minor: _OUR_MINOR_VERSION,
|
||||
MaxReadAhead: input.MaxReadAhead,
|
||||
Flags: server.kernelSettings.Flags,
|
||||
MaxWrite: uint32(server.opts.MaxWrite),
|
||||
CongestionThreshold: uint16(server.opts.MaxBackground * 3 / 4),
|
||||
MaxBackground: uint16(server.opts.MaxBackground),
|
||||
}
|
||||
if out.Minor > input.Minor {
|
||||
out.Minor = input.Minor
|
||||
}
|
||||
|
||||
if out.Minor <= 22 {
|
||||
tweaked := *req.handler
|
||||
|
||||
// v8-v22 don't have TimeGran and further fields.
|
||||
tweaked.OutputSize = 24
|
||||
req.handler = &tweaked
|
||||
}
|
||||
|
||||
req.outData = unsafe.Pointer(out)
|
||||
req.status = OK
|
||||
}
|
||||
|
||||
func doOpen(server *Server, req *request) {
|
||||
out := (*OpenOut)(req.outData)
|
||||
status := server.fileSystem.Open((*OpenIn)(req.inData), out)
|
||||
req.status = status
|
||||
if status != OK {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func doCreate(server *Server, req *request) {
|
||||
out := (*CreateOut)(req.outData)
|
||||
status := server.fileSystem.Create((*CreateIn)(req.inData), req.filenames[0], out)
|
||||
req.status = status
|
||||
}
|
||||
|
||||
func doReadDir(server *Server, req *request) {
|
||||
in := (*ReadIn)(req.inData)
|
||||
buf := server.allocOut(req, in.Size)
|
||||
out := NewDirEntryList(buf, uint64(in.Offset))
|
||||
|
||||
code := server.fileSystem.ReadDir(in, out)
|
||||
req.flatData = out.bytes()
|
||||
req.status = code
|
||||
}
|
||||
|
||||
func doReadDirPlus(server *Server, req *request) {
|
||||
in := (*ReadIn)(req.inData)
|
||||
buf := server.allocOut(req, in.Size)
|
||||
out := NewDirEntryList(buf, uint64(in.Offset))
|
||||
|
||||
code := server.fileSystem.ReadDirPlus(in, out)
|
||||
req.flatData = out.bytes()
|
||||
req.status = code
|
||||
}
|
||||
|
||||
func doOpenDir(server *Server, req *request) {
|
||||
out := (*OpenOut)(req.outData)
|
||||
status := server.fileSystem.OpenDir((*OpenIn)(req.inData), out)
|
||||
req.status = status
|
||||
}
|
||||
|
||||
func doSetattr(server *Server, req *request) {
|
||||
out := (*AttrOut)(req.outData)
|
||||
req.status = server.fileSystem.SetAttr((*SetAttrIn)(req.inData), out)
|
||||
}
|
||||
|
||||
func doWrite(server *Server, req *request) {
|
||||
n, status := server.fileSystem.Write((*WriteIn)(req.inData), req.arg)
|
||||
o := (*WriteOut)(req.outData)
|
||||
o.Size = n
|
||||
req.status = status
|
||||
}
|
||||
|
||||
const _SECURITY_CAPABILITY = "security.capability"
|
||||
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
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
input := (*GetXAttrIn)(req.inData)
|
||||
|
||||
if input.Size == 0 {
|
||||
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)
|
||||
}
|
||||
req.status = code
|
||||
return
|
||||
case _OP_LISTXATTR:
|
||||
data, code := server.fileSystem.ListXAttr(req.inHeader)
|
||||
if code.Ok() {
|
||||
out.Size = uint32(len(data))
|
||||
}
|
||||
req.status = code
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
req.outData = nil
|
||||
var data []byte
|
||||
switch req.inHeader.Opcode {
|
||||
case _OP_GETXATTR:
|
||||
data, req.status = server.fileSystem.GetXAttrData(req.inHeader, req.filenames[0])
|
||||
case _OP_LISTXATTR:
|
||||
data, req.status = server.fileSystem.ListXAttr(req.inHeader)
|
||||
default:
|
||||
log.Panicf("xattr opcode %v", req.inHeader.Opcode)
|
||||
req.status = ENOSYS
|
||||
}
|
||||
|
||||
if len(data) > int(input.Size) {
|
||||
req.status = ERANGE
|
||||
}
|
||||
|
||||
if !req.status.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
req.flatData = data
|
||||
}
|
||||
|
||||
func doGetAttr(server *Server, req *request) {
|
||||
out := (*AttrOut)(req.outData)
|
||||
s := server.fileSystem.GetAttr((*GetAttrIn)(req.inData), out)
|
||||
req.status = s
|
||||
}
|
||||
|
||||
// doForget - forget one NodeId
|
||||
func doForget(server *Server, req *request) {
|
||||
if !server.opts.RememberInodes {
|
||||
server.fileSystem.Forget(req.inHeader.NodeId, (*ForgetIn)(req.inData).Nlookup)
|
||||
}
|
||||
}
|
||||
|
||||
// doBatchForget - forget a list of NodeIds
|
||||
func doBatchForget(server *Server, req *request) {
|
||||
in := (*_BatchForgetIn)(req.inData)
|
||||
wantBytes := uintptr(in.Count) * unsafe.Sizeof(_ForgetOne{})
|
||||
if uintptr(len(req.arg)) < wantBytes {
|
||||
// We have no return value to complain, so log an error.
|
||||
log.Printf("Too few bytes for batch forget. Got %d bytes, want %d (%d entries)",
|
||||
len(req.arg), wantBytes, in.Count)
|
||||
}
|
||||
|
||||
h := &reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(&req.arg[0])),
|
||||
Len: int(in.Count),
|
||||
Cap: int(in.Count),
|
||||
}
|
||||
|
||||
forgets := *(*[]_ForgetOne)(unsafe.Pointer(h))
|
||||
for i, f := range forgets {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func doReadlink(server *Server, req *request) {
|
||||
req.flatData, req.status = server.fileSystem.Readlink(req.inHeader)
|
||||
}
|
||||
|
||||
func doLookup(server *Server, req *request) {
|
||||
out := (*EntryOut)(req.outData)
|
||||
s := server.fileSystem.Lookup(req.inHeader, req.filenames[0], out)
|
||||
req.status = s
|
||||
req.outData = unsafe.Pointer(out)
|
||||
}
|
||||
|
||||
func doMknod(server *Server, req *request) {
|
||||
out := (*EntryOut)(req.outData)
|
||||
|
||||
req.status = server.fileSystem.Mknod((*MknodIn)(req.inData), req.filenames[0], out)
|
||||
}
|
||||
|
||||
func doMkdir(server *Server, req *request) {
|
||||
out := (*EntryOut)(req.outData)
|
||||
req.status = server.fileSystem.Mkdir((*MkdirIn)(req.inData), req.filenames[0], out)
|
||||
}
|
||||
|
||||
func doUnlink(server *Server, req *request) {
|
||||
req.status = server.fileSystem.Unlink(req.inHeader, req.filenames[0])
|
||||
}
|
||||
|
||||
func doRmdir(server *Server, req *request) {
|
||||
req.status = server.fileSystem.Rmdir(req.inHeader, req.filenames[0])
|
||||
}
|
||||
|
||||
func doLink(server *Server, req *request) {
|
||||
out := (*EntryOut)(req.outData)
|
||||
req.status = server.fileSystem.Link((*LinkIn)(req.inData), req.filenames[0], out)
|
||||
}
|
||||
|
||||
func doRead(server *Server, req *request) {
|
||||
in := (*ReadIn)(req.inData)
|
||||
buf := server.allocOut(req, in.Size)
|
||||
|
||||
req.readResult, req.status = server.fileSystem.Read(in, buf)
|
||||
if fd, ok := req.readResult.(*readResultFd); ok {
|
||||
req.fdData = fd
|
||||
req.flatData = nil
|
||||
} else if req.readResult != nil && req.status.Ok() {
|
||||
req.flatData, req.status = req.readResult.Bytes(buf)
|
||||
}
|
||||
}
|
||||
|
||||
func doFlush(server *Server, req *request) {
|
||||
req.status = server.fileSystem.Flush((*FlushIn)(req.inData))
|
||||
}
|
||||
|
||||
func doRelease(server *Server, req *request) {
|
||||
server.fileSystem.Release((*ReleaseIn)(req.inData))
|
||||
}
|
||||
|
||||
func doFsync(server *Server, req *request) {
|
||||
req.status = server.fileSystem.Fsync((*FsyncIn)(req.inData))
|
||||
}
|
||||
|
||||
func doReleaseDir(server *Server, req *request) {
|
||||
server.fileSystem.ReleaseDir((*ReleaseIn)(req.inData))
|
||||
}
|
||||
|
||||
func doFsyncDir(server *Server, req *request) {
|
||||
req.status = server.fileSystem.FsyncDir((*FsyncIn)(req.inData))
|
||||
}
|
||||
|
||||
func doSetXAttr(server *Server, req *request) {
|
||||
splits := bytes.SplitN(req.arg, []byte{0}, 2)
|
||||
req.status = server.fileSystem.SetXAttr((*SetXAttrIn)(req.inData), string(splits[0]), splits[1])
|
||||
}
|
||||
|
||||
func doRemoveXAttr(server *Server, req *request) {
|
||||
req.status = server.fileSystem.RemoveXAttr(req.inHeader, req.filenames[0])
|
||||
}
|
||||
|
||||
func doAccess(server *Server, req *request) {
|
||||
req.status = server.fileSystem.Access((*AccessIn)(req.inData))
|
||||
}
|
||||
|
||||
func doSymlink(server *Server, req *request) {
|
||||
out := (*EntryOut)(req.outData)
|
||||
req.status = server.fileSystem.Symlink(req.inHeader, req.filenames[1], req.filenames[0], out)
|
||||
}
|
||||
|
||||
func doRename(server *Server, req *request) {
|
||||
req.status = server.fileSystem.Rename((*RenameIn)(req.inData), req.filenames[0], req.filenames[1])
|
||||
}
|
||||
|
||||
func doStatFs(server *Server, req *request) {
|
||||
out := (*StatfsOut)(req.outData)
|
||||
req.status = server.fileSystem.StatFs(req.inHeader, out)
|
||||
if req.status == ENOSYS && runtime.GOOS == "darwin" {
|
||||
// OSX FUSE requires Statfs to be implemented for the
|
||||
// mount to succeed.
|
||||
*out = StatfsOut{}
|
||||
req.status = OK
|
||||
}
|
||||
}
|
||||
|
||||
func doIoctl(server *Server, req *request) {
|
||||
req.status = ENOSYS
|
||||
}
|
||||
|
||||
func doDestroy(server *Server, req *request) {
|
||||
req.status = OK
|
||||
}
|
||||
|
||||
func doFallocate(server *Server, req *request) {
|
||||
req.status = server.fileSystem.Fallocate((*FallocateIn)(req.inData))
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
type operationFunc func(*Server, *request)
|
||||
type castPointerFunc func(unsafe.Pointer) interface{}
|
||||
|
||||
type operationHandler struct {
|
||||
Name string
|
||||
Func operationFunc
|
||||
InputSize uintptr
|
||||
OutputSize uintptr
|
||||
DecodeIn castPointerFunc
|
||||
DecodeOut castPointerFunc
|
||||
FileNames int
|
||||
FileNameOut bool
|
||||
}
|
||||
|
||||
var operationHandlers []*operationHandler
|
||||
|
||||
func operationName(op int32) string {
|
||||
h := getHandler(op)
|
||||
if h == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return h.Name
|
||||
}
|
||||
|
||||
func getHandler(o int32) *operationHandler {
|
||||
if o >= _OPCODE_COUNT {
|
||||
return nil
|
||||
}
|
||||
return operationHandlers[o]
|
||||
}
|
||||
|
||||
func init() {
|
||||
operationHandlers = make([]*operationHandler, _OPCODE_COUNT)
|
||||
for i := range operationHandlers {
|
||||
operationHandlers[i] = &operationHandler{Name: "UNKNOWN"}
|
||||
}
|
||||
|
||||
fileOps := []int32{_OP_READLINK, _OP_NOTIFY_ENTRY, _OP_NOTIFY_DELETE}
|
||||
for _, op := range fileOps {
|
||||
operationHandlers[op].FileNameOut = true
|
||||
}
|
||||
|
||||
for op, sz := range map[int32]uintptr{
|
||||
_OP_FORGET: unsafe.Sizeof(ForgetIn{}),
|
||||
_OP_BATCH_FORGET: unsafe.Sizeof(_BatchForgetIn{}),
|
||||
_OP_GETATTR: unsafe.Sizeof(GetAttrIn{}),
|
||||
_OP_SETATTR: unsafe.Sizeof(SetAttrIn{}),
|
||||
_OP_MKNOD: unsafe.Sizeof(MknodIn{}),
|
||||
_OP_MKDIR: unsafe.Sizeof(MkdirIn{}),
|
||||
_OP_RENAME: unsafe.Sizeof(RenameIn{}),
|
||||
_OP_LINK: unsafe.Sizeof(LinkIn{}),
|
||||
_OP_OPEN: unsafe.Sizeof(OpenIn{}),
|
||||
_OP_READ: unsafe.Sizeof(ReadIn{}),
|
||||
_OP_WRITE: unsafe.Sizeof(WriteIn{}),
|
||||
_OP_RELEASE: unsafe.Sizeof(ReleaseIn{}),
|
||||
_OP_FSYNC: unsafe.Sizeof(FsyncIn{}),
|
||||
_OP_SETXATTR: unsafe.Sizeof(SetXAttrIn{}),
|
||||
_OP_GETXATTR: unsafe.Sizeof(GetXAttrIn{}),
|
||||
_OP_LISTXATTR: unsafe.Sizeof(GetXAttrIn{}),
|
||||
_OP_FLUSH: unsafe.Sizeof(FlushIn{}),
|
||||
_OP_INIT: unsafe.Sizeof(InitIn{}),
|
||||
_OP_OPENDIR: unsafe.Sizeof(OpenIn{}),
|
||||
_OP_READDIR: unsafe.Sizeof(ReadIn{}),
|
||||
_OP_RELEASEDIR: unsafe.Sizeof(ReleaseIn{}),
|
||||
_OP_FSYNCDIR: unsafe.Sizeof(FsyncIn{}),
|
||||
_OP_ACCESS: unsafe.Sizeof(AccessIn{}),
|
||||
_OP_CREATE: unsafe.Sizeof(CreateIn{}),
|
||||
_OP_INTERRUPT: unsafe.Sizeof(InterruptIn{}),
|
||||
_OP_BMAP: unsafe.Sizeof(_BmapIn{}),
|
||||
_OP_IOCTL: unsafe.Sizeof(_IoctlIn{}),
|
||||
_OP_POLL: unsafe.Sizeof(_PollIn{}),
|
||||
_OP_FALLOCATE: unsafe.Sizeof(FallocateIn{}),
|
||||
_OP_READDIRPLUS: unsafe.Sizeof(ReadIn{}),
|
||||
} {
|
||||
operationHandlers[op].InputSize = sz
|
||||
}
|
||||
|
||||
for op, sz := range map[int32]uintptr{
|
||||
_OP_LOOKUP: unsafe.Sizeof(EntryOut{}),
|
||||
_OP_GETATTR: unsafe.Sizeof(AttrOut{}),
|
||||
_OP_SETATTR: unsafe.Sizeof(AttrOut{}),
|
||||
_OP_SYMLINK: unsafe.Sizeof(EntryOut{}),
|
||||
_OP_MKNOD: unsafe.Sizeof(EntryOut{}),
|
||||
_OP_MKDIR: unsafe.Sizeof(EntryOut{}),
|
||||
_OP_LINK: unsafe.Sizeof(EntryOut{}),
|
||||
_OP_OPEN: unsafe.Sizeof(OpenOut{}),
|
||||
_OP_WRITE: unsafe.Sizeof(WriteOut{}),
|
||||
_OP_STATFS: unsafe.Sizeof(StatfsOut{}),
|
||||
_OP_GETXATTR: unsafe.Sizeof(GetXAttrOut{}),
|
||||
_OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}),
|
||||
_OP_INIT: unsafe.Sizeof(InitOut{}),
|
||||
_OP_OPENDIR: unsafe.Sizeof(OpenOut{}),
|
||||
_OP_CREATE: unsafe.Sizeof(CreateOut{}),
|
||||
_OP_BMAP: unsafe.Sizeof(_BmapOut{}),
|
||||
_OP_IOCTL: unsafe.Sizeof(_IoctlOut{}),
|
||||
_OP_POLL: unsafe.Sizeof(_PollOut{}),
|
||||
_OP_NOTIFY_ENTRY: unsafe.Sizeof(NotifyInvalEntryOut{}),
|
||||
_OP_NOTIFY_INODE: unsafe.Sizeof(NotifyInvalInodeOut{}),
|
||||
_OP_NOTIFY_DELETE: unsafe.Sizeof(NotifyInvalDeleteOut{}),
|
||||
} {
|
||||
operationHandlers[op].OutputSize = sz
|
||||
}
|
||||
|
||||
for op, v := range map[int32]string{
|
||||
_OP_LOOKUP: "LOOKUP",
|
||||
_OP_FORGET: "FORGET",
|
||||
_OP_BATCH_FORGET: "BATCH_FORGET",
|
||||
_OP_GETATTR: "GETATTR",
|
||||
_OP_SETATTR: "SETATTR",
|
||||
_OP_READLINK: "READLINK",
|
||||
_OP_SYMLINK: "SYMLINK",
|
||||
_OP_MKNOD: "MKNOD",
|
||||
_OP_MKDIR: "MKDIR",
|
||||
_OP_UNLINK: "UNLINK",
|
||||
_OP_RMDIR: "RMDIR",
|
||||
_OP_RENAME: "RENAME",
|
||||
_OP_LINK: "LINK",
|
||||
_OP_OPEN: "OPEN",
|
||||
_OP_READ: "READ",
|
||||
_OP_WRITE: "WRITE",
|
||||
_OP_STATFS: "STATFS",
|
||||
_OP_RELEASE: "RELEASE",
|
||||
_OP_FSYNC: "FSYNC",
|
||||
_OP_SETXATTR: "SETXATTR",
|
||||
_OP_GETXATTR: "GETXATTR",
|
||||
_OP_LISTXATTR: "LISTXATTR",
|
||||
_OP_REMOVEXATTR: "REMOVEXATTR",
|
||||
_OP_FLUSH: "FLUSH",
|
||||
_OP_INIT: "INIT",
|
||||
_OP_OPENDIR: "OPENDIR",
|
||||
_OP_READDIR: "READDIR",
|
||||
_OP_RELEASEDIR: "RELEASEDIR",
|
||||
_OP_FSYNCDIR: "FSYNCDIR",
|
||||
_OP_GETLK: "GETLK",
|
||||
_OP_SETLK: "SETLK",
|
||||
_OP_SETLKW: "SETLKW",
|
||||
_OP_ACCESS: "ACCESS",
|
||||
_OP_CREATE: "CREATE",
|
||||
_OP_INTERRUPT: "INTERRUPT",
|
||||
_OP_BMAP: "BMAP",
|
||||
_OP_DESTROY: "DESTROY",
|
||||
_OP_IOCTL: "IOCTL",
|
||||
_OP_POLL: "POLL",
|
||||
_OP_NOTIFY_ENTRY: "NOTIFY_ENTRY",
|
||||
_OP_NOTIFY_INODE: "NOTIFY_INODE",
|
||||
_OP_NOTIFY_DELETE: "NOTIFY_DELETE",
|
||||
_OP_FALLOCATE: "FALLOCATE",
|
||||
_OP_READDIRPLUS: "READDIRPLUS",
|
||||
} {
|
||||
operationHandlers[op].Name = v
|
||||
}
|
||||
|
||||
for op, v := range map[int32]operationFunc{
|
||||
_OP_OPEN: doOpen,
|
||||
_OP_READDIR: doReadDir,
|
||||
_OP_WRITE: doWrite,
|
||||
_OP_OPENDIR: doOpenDir,
|
||||
_OP_CREATE: doCreate,
|
||||
_OP_SETATTR: doSetattr,
|
||||
_OP_GETXATTR: doGetXAttr,
|
||||
_OP_LISTXATTR: doGetXAttr,
|
||||
_OP_GETATTR: doGetAttr,
|
||||
_OP_FORGET: doForget,
|
||||
_OP_BATCH_FORGET: doBatchForget,
|
||||
_OP_READLINK: doReadlink,
|
||||
_OP_INIT: doInit,
|
||||
_OP_LOOKUP: doLookup,
|
||||
_OP_MKNOD: doMknod,
|
||||
_OP_MKDIR: doMkdir,
|
||||
_OP_UNLINK: doUnlink,
|
||||
_OP_RMDIR: doRmdir,
|
||||
_OP_LINK: doLink,
|
||||
_OP_READ: doRead,
|
||||
_OP_FLUSH: doFlush,
|
||||
_OP_RELEASE: doRelease,
|
||||
_OP_FSYNC: doFsync,
|
||||
_OP_RELEASEDIR: doReleaseDir,
|
||||
_OP_FSYNCDIR: doFsyncDir,
|
||||
_OP_SETXATTR: doSetXAttr,
|
||||
_OP_REMOVEXATTR: doRemoveXAttr,
|
||||
_OP_ACCESS: doAccess,
|
||||
_OP_SYMLINK: doSymlink,
|
||||
_OP_RENAME: doRename,
|
||||
_OP_STATFS: doStatFs,
|
||||
_OP_IOCTL: doIoctl,
|
||||
_OP_DESTROY: doDestroy,
|
||||
_OP_FALLOCATE: doFallocate,
|
||||
_OP_READDIRPLUS: doReadDirPlus,
|
||||
} {
|
||||
operationHandlers[op].Func = v
|
||||
}
|
||||
|
||||
// Outputs.
|
||||
for op, f := range map[int32]castPointerFunc{
|
||||
_OP_LOOKUP: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
|
||||
_OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
|
||||
_OP_OPENDIR: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
|
||||
_OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
|
||||
_OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateOut)(ptr) },
|
||||
_OP_LINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
|
||||
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
|
||||
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) },
|
||||
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
|
||||
_OP_NOTIFY_ENTRY: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalEntryOut)(ptr) },
|
||||
_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
|
||||
}
|
||||
|
||||
// Inputs.
|
||||
for op, f := range map[int32]castPointerFunc{
|
||||
_OP_FLUSH: func(ptr unsafe.Pointer) interface{} { return (*FlushIn)(ptr) },
|
||||
_OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*GetAttrIn)(ptr) },
|
||||
_OP_GETXATTR: func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
|
||||
_OP_LISTXATTR: func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
|
||||
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*SetAttrIn)(ptr) },
|
||||
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitIn)(ptr) },
|
||||
_OP_IOCTL: func(ptr unsafe.Pointer) interface{} { return (*_IoctlIn)(ptr) },
|
||||
_OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenIn)(ptr) },
|
||||
_OP_MKNOD: func(ptr unsafe.Pointer) interface{} { return (*MknodIn)(ptr) },
|
||||
_OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateIn)(ptr) },
|
||||
_OP_READ: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
|
||||
_OP_READDIR: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
|
||||
_OP_ACCESS: func(ptr unsafe.Pointer) interface{} { return (*AccessIn)(ptr) },
|
||||
_OP_FORGET: func(ptr unsafe.Pointer) interface{} { return (*ForgetIn)(ptr) },
|
||||
_OP_BATCH_FORGET: func(ptr unsafe.Pointer) interface{} { return (*_BatchForgetIn)(ptr) },
|
||||
_OP_LINK: func(ptr unsafe.Pointer) interface{} { return (*LinkIn)(ptr) },
|
||||
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*MkdirIn)(ptr) },
|
||||
_OP_RELEASE: func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
|
||||
_OP_RELEASEDIR: func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
|
||||
_OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*FallocateIn)(ptr) },
|
||||
_OP_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
|
||||
_OP_RENAME: func(ptr unsafe.Pointer) interface{} { return (*RenameIn)(ptr) },
|
||||
} {
|
||||
operationHandlers[op].DecodeIn = f
|
||||
}
|
||||
|
||||
// File name args.
|
||||
for op, count := range map[int32]int{
|
||||
_OP_CREATE: 1,
|
||||
_OP_GETXATTR: 1,
|
||||
_OP_LINK: 1,
|
||||
_OP_LOOKUP: 1,
|
||||
_OP_MKDIR: 1,
|
||||
_OP_MKNOD: 1,
|
||||
_OP_REMOVEXATTR: 1,
|
||||
_OP_RENAME: 2,
|
||||
_OP_RMDIR: 1,
|
||||
_OP_SYMLINK: 2,
|
||||
_OP_UNLINK: 1,
|
||||
} {
|
||||
operationHandlers[op].FileNames = count
|
||||
}
|
||||
|
||||
var r request
|
||||
sizeOfOutHeader := unsafe.Sizeof(OutHeader{})
|
||||
for code, h := range operationHandlers {
|
||||
if h.OutputSize+sizeOfOutHeader > unsafe.Sizeof(r.outBuf) {
|
||||
log.Panicf("request output buffer too small: code %v, sz %d + %d %v", code, h.OutputSize, sizeOfOutHeader, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
81
vendor/github.com/hanwen/go-fuse/fuse/pathfs/api.go
generated
vendored
Normal file
81
vendor/github.com/hanwen/go-fuse/fuse/pathfs/api.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
// A filesystem API that uses paths rather than inodes. A minimal
|
||||
// file system should have at least a functional GetAttr method.
|
||||
// Typically, each call happens in its own goroutine, so take care to
|
||||
// make the file system thread-safe.
|
||||
//
|
||||
// NewDefaultFileSystem provides a null implementation of required
|
||||
// methods.
|
||||
type FileSystem interface {
|
||||
// Used for pretty printing.
|
||||
String() string
|
||||
|
||||
// If called, provide debug output through the log package.
|
||||
SetDebug(debug bool)
|
||||
|
||||
// Attributes. This function is the main entry point, through
|
||||
// which FUSE discovers which files and directories exist.
|
||||
//
|
||||
// If the filesystem wants to implement hard-links, it should
|
||||
// return consistent non-zero FileInfo.Ino data. Using
|
||||
// hardlinks incurs a performance hit.
|
||||
GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status)
|
||||
|
||||
// These should update the file's ctime too.
|
||||
Chmod(name string, mode uint32, context *fuse.Context) (code fuse.Status)
|
||||
Chown(name string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status)
|
||||
Utimens(name string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status)
|
||||
|
||||
Truncate(name string, size uint64, context *fuse.Context) (code fuse.Status)
|
||||
|
||||
Access(name string, mode uint32, context *fuse.Context) (code fuse.Status)
|
||||
|
||||
// Tree structure
|
||||
Link(oldName string, newName string, context *fuse.Context) (code fuse.Status)
|
||||
Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status
|
||||
Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status
|
||||
Rename(oldName string, newName string, context *fuse.Context) (code fuse.Status)
|
||||
Rmdir(name string, context *fuse.Context) (code fuse.Status)
|
||||
Unlink(name string, context *fuse.Context) (code fuse.Status)
|
||||
|
||||
// Extended attributes.
|
||||
GetXAttr(name string, attribute string, context *fuse.Context) (data []byte, code fuse.Status)
|
||||
ListXAttr(name string, context *fuse.Context) (attributes []string, code fuse.Status)
|
||||
RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status
|
||||
SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status
|
||||
|
||||
// Called after mount.
|
||||
OnMount(nodeFs *PathNodeFs)
|
||||
OnUnmount()
|
||||
|
||||
// File handling. If opening for writing, the file's mtime
|
||||
// should be updated too.
|
||||
Open(name string, flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status)
|
||||
Create(name string, flags uint32, mode uint32, context *fuse.Context) (file nodefs.File, code fuse.Status)
|
||||
|
||||
// Directory handling
|
||||
OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, code fuse.Status)
|
||||
|
||||
// Symlinks.
|
||||
Symlink(value string, linkName string, context *fuse.Context) (code fuse.Status)
|
||||
Readlink(name string, context *fuse.Context) (string, fuse.Status)
|
||||
|
||||
StatFs(name string) *fuse.StatfsOut
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
57
vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy.go
generated
vendored
Normal file
57
vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
func CopyFile(srcFs, destFs FileSystem, srcFile, destFile string, context *fuse.Context) fuse.Status {
|
||||
src, code := srcFs.Open(srcFile, uint32(os.O_RDONLY), context)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
defer src.Release()
|
||||
defer src.Flush()
|
||||
|
||||
attr, code := srcFs.GetAttr(srcFile, context)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
|
||||
dst, code := destFs.Create(destFile, uint32(os.O_WRONLY|os.O_CREATE|os.O_TRUNC), attr.Mode, context)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
defer dst.Release()
|
||||
defer dst.Flush()
|
||||
|
||||
buf := make([]byte, 128*(1<<10))
|
||||
off := int64(0)
|
||||
for {
|
||||
res, code := src.Read(buf, off)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
data, code := res.Bytes(buf)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
break
|
||||
}
|
||||
n, code := dst.Write(data, off)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
if int(n) < len(data) {
|
||||
return fuse.EIO
|
||||
}
|
||||
if len(data) < len(buf) {
|
||||
break
|
||||
}
|
||||
off += int64(len(data))
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
59
vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy_test.go
generated
vendored
Normal file
59
vendor/github.com/hanwen/go-fuse/fuse/pathfs/copy_test.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
d1, err := ioutil.TempDir("", "go-fuse")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(d1)
|
||||
d2, err := ioutil.TempDir("", "go-fuse")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(d2)
|
||||
|
||||
fs1 := NewLoopbackFileSystem(d1)
|
||||
fs2 := NewLoopbackFileSystem(d2)
|
||||
|
||||
content1 := "blabla"
|
||||
|
||||
err = ioutil.WriteFile(d1+"/file", []byte(content1), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteFile failed: %v", err)
|
||||
}
|
||||
|
||||
code := CopyFile(fs1, fs2, "file", "file", nil)
|
||||
if !code.Ok() {
|
||||
t.Fatal("Unexpected ret code", code)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(d2 + "/file")
|
||||
if content1 != string(data) {
|
||||
t.Fatal("Unexpected content", string(data))
|
||||
}
|
||||
|
||||
content2 := "foobar"
|
||||
|
||||
err = ioutil.WriteFile(d2+"/file", []byte(content2), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteFile failed: %v", err)
|
||||
}
|
||||
|
||||
// Copy back: should overwrite.
|
||||
code = CopyFile(fs2, fs1, "file", "file", nil)
|
||||
if !code.Ok() {
|
||||
t.Fatal("Unexpected ret code", code)
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadFile(d1 + "/file")
|
||||
if content2 != string(data) {
|
||||
t.Fatal("Unexpected content", string(data))
|
||||
}
|
||||
|
||||
}
|
||||
118
vendor/github.com/hanwen/go-fuse/fuse/pathfs/default.go
generated
vendored
Normal file
118
vendor/github.com/hanwen/go-fuse/fuse/pathfs/default.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
// NewDefaultFileSystem creates a filesystem that responds ENOSYS for
|
||||
// all methods
|
||||
func NewDefaultFileSystem() FileSystem {
|
||||
return (*defaultFileSystem)(nil)
|
||||
}
|
||||
|
||||
// defaultFileSystem implements a FileSystem that returns ENOSYS for every operation.
|
||||
type defaultFileSystem struct{}
|
||||
|
||||
func (fs *defaultFileSystem) SetDebug(debug bool) {}
|
||||
|
||||
// defaultFileSystem
|
||||
func (fs *defaultFileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
|
||||
return "", fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Symlink(value string, linkName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Rename(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Link(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Chmod(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Chown(name string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Truncate(name string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Open(name string, flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) OnMount(nodeFs *PathNodeFs) {
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) OnUnmount() {
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) Utimens(name string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) String() string {
|
||||
return "defaultFileSystem"
|
||||
}
|
||||
|
||||
func (fs *defaultFileSystem) StatFs(name string) *fuse.StatfsOut {
|
||||
return nil
|
||||
}
|
||||
163
vendor/github.com/hanwen/go-fuse/fuse/pathfs/locking.go
generated
vendored
Normal file
163
vendor/github.com/hanwen/go-fuse/fuse/pathfs/locking.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
type lockingFileSystem struct {
|
||||
// Should be public so people reusing can access the wrapped
|
||||
// FS.
|
||||
FS FileSystem
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// NewLockingFileSystem is a wrapper that makes a FileSystem
|
||||
// threadsafe by serializing each operation.
|
||||
func NewLockingFileSystem(pfs FileSystem) FileSystem {
|
||||
l := new(lockingFileSystem)
|
||||
l.FS = pfs
|
||||
return l
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) String() string {
|
||||
defer fs.locked()()
|
||||
return fs.FS.String()
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) SetDebug(debug bool) {
|
||||
defer fs.locked()()
|
||||
fs.FS.SetDebug(debug)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) StatFs(name string) *fuse.StatfsOut {
|
||||
defer fs.locked()()
|
||||
return fs.FS.StatFs(name)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) locked() func() {
|
||||
fs.lock.Lock()
|
||||
return func() { fs.lock.Unlock() }
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.GetAttr(name, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Readlink(name, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Mknod(name, mode, dev, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Mkdir(name, mode, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Unlink(name, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Rmdir(name, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Symlink(value string, linkName string, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Symlink(value, linkName, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Rename(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Rename(oldName, newName, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Link(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Link(oldName, newName, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Chmod(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Chmod(name, mode, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Chown(name string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Chown(name, uid, gid, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Truncate(name string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Truncate(name, offset, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Open(name string, flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
file, code = fs.FS.Open(name, flags, context)
|
||||
file = nodefs.NewLockingFile(&fs.lock, file)
|
||||
return
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.OpenDir(name, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) OnMount(nodeFs *PathNodeFs) {
|
||||
defer fs.locked()()
|
||||
fs.FS.OnMount(nodeFs)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) OnUnmount() {
|
||||
defer fs.locked()()
|
||||
fs.FS.OnUnmount()
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Access(name, mode, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
file, code = fs.FS.Create(name, flags, mode, context)
|
||||
|
||||
file = nodefs.NewLockingFile(&fs.lock, file)
|
||||
return file, code
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) Utimens(name string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.Utimens(name, Atime, Mtime, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.GetXAttr(name, attr, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
defer fs.locked()()
|
||||
return fs.FS.SetXAttr(name, attr, data, flags, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
|
||||
defer fs.locked()()
|
||||
return fs.FS.ListXAttr(name, context)
|
||||
}
|
||||
|
||||
func (fs *lockingFileSystem) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
||||
defer fs.locked()()
|
||||
return fs.FS.RemoveXAttr(name, attr, context)
|
||||
}
|
||||
162
vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback.go
generated
vendored
Normal file
162
vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
type loopbackFileSystem struct {
|
||||
// TODO - this should need default fill in.
|
||||
FileSystem
|
||||
Root string
|
||||
}
|
||||
|
||||
// A FUSE filesystem that shunts all request to an underlying file
|
||||
// system. Its main purpose is to provide test coverage without
|
||||
// having to build a synthetic filesystem.
|
||||
func NewLoopbackFileSystem(root string) FileSystem {
|
||||
return &loopbackFileSystem{
|
||||
FileSystem: NewDefaultFileSystem(),
|
||||
Root: root,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) OnMount(nodeFs *PathNodeFs) {
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) OnUnmount() {}
|
||||
|
||||
func (fs *loopbackFileSystem) GetPath(relPath string) string {
|
||||
return filepath.Join(fs.Root, relPath)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, code fuse.Status) {
|
||||
fullPath := fs.GetPath(name)
|
||||
var err error = nil
|
||||
st := syscall.Stat_t{}
|
||||
if name == "" {
|
||||
// When GetAttr is called for the toplevel directory, we always want
|
||||
// to look through symlinks.
|
||||
err = syscall.Stat(fullPath, &st)
|
||||
} else {
|
||||
err = syscall.Lstat(fullPath, &st)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fuse.ToStatus(err)
|
||||
}
|
||||
a = &fuse.Attr{}
|
||||
a.FromStat(&st)
|
||||
return a, fuse.OK
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
|
||||
// What other ways beyond O_RDONLY are there to open
|
||||
// directories?
|
||||
f, err := os.Open(fs.GetPath(name))
|
||||
if err != nil {
|
||||
return nil, fuse.ToStatus(err)
|
||||
}
|
||||
want := 500
|
||||
output := make([]fuse.DirEntry, 0, want)
|
||||
for {
|
||||
infos, err := f.Readdir(want)
|
||||
for i := range infos {
|
||||
// workaround forhttps://code.google.com/p/go/issues/detail?id=5960
|
||||
if infos[i] == nil {
|
||||
continue
|
||||
}
|
||||
n := infos[i].Name()
|
||||
d := fuse.DirEntry{
|
||||
Name: n,
|
||||
}
|
||||
if s := fuse.ToStatT(infos[i]); s != nil {
|
||||
d.Mode = uint32(s.Mode)
|
||||
} else {
|
||||
log.Printf("ReadDir entry %q for %q has no stat info", n, name)
|
||||
}
|
||||
output = append(output, d)
|
||||
}
|
||||
if len(infos) < want || err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("Readdir() returned err:", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
|
||||
return output, fuse.OK
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
||||
f, err := os.OpenFile(fs.GetPath(name), int(flags), 0)
|
||||
if err != nil {
|
||||
return nil, fuse.ToStatus(err)
|
||||
}
|
||||
return nodefs.NewLoopbackFile(f), fuse.OK
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
err := os.Chmod(fs.GetPath(path), os.FileMode(mode))
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(os.Chown(fs.GetPath(path), int(uid), int(gid)))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(os.Truncate(fs.GetPath(path), int64(offset)))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Readlink(name string, context *fuse.Context) (out string, code fuse.Status) {
|
||||
f, err := os.Readlink(fs.GetPath(name))
|
||||
return f, fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(syscall.Mknod(fs.GetPath(name), mode, int(dev)))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Mkdir(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(os.Mkdir(fs.GetPath(path), os.FileMode(mode)))
|
||||
}
|
||||
|
||||
// Don't use os.Remove, it removes twice (unlink followed by rmdir).
|
||||
func (fs *loopbackFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(syscall.Unlink(fs.GetPath(name)))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(syscall.Rmdir(fs.GetPath(name)))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(os.Symlink(pointedTo, fs.GetPath(linkName)))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Rename(oldPath string, newPath string, context *fuse.Context) (codee fuse.Status) {
|
||||
err := os.Rename(fs.GetPath(oldPath), fs.GetPath(newPath))
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Link(orig string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(os.Link(fs.GetPath(orig), fs.GetPath(newName)))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.ToStatus(syscall.Access(fs.GetPath(name), mode))
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
|
||||
f, err := os.OpenFile(fs.GetPath(path), int(flags)|os.O_CREATE, os.FileMode(mode))
|
||||
return nodefs.NewLoopbackFile(f), fuse.ToStatus(err)
|
||||
}
|
||||
59
vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_darwin.go
generated
vendored
Normal file
59
vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
func (fs *loopbackFileSystem) StatFs(name string) *fuse.StatfsOut {
|
||||
s := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(fs.GetPath(name), &s)
|
||||
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,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const _UTIME_NOW = ((1 << 30) - 1)
|
||||
const _UTIME_OMIT = ((1 << 30) - 2)
|
||||
|
||||
// timeToTimeval - Convert time.Time to syscall.Timeval
|
||||
//
|
||||
// Note: This does not use syscall.NsecToTimespec because
|
||||
// that does not work properly for times before 1970,
|
||||
// see https://github.com/golang/go/issues/12777
|
||||
func timeToTimeval(t *time.Time) syscall.Timeval {
|
||||
var tv syscall.Timeval
|
||||
tv.Usec = int32(t.Nanosecond() / 1000)
|
||||
tv.Sec = t.Unix()
|
||||
return tv
|
||||
}
|
||||
|
||||
// OSX does not have the utimensat syscall neded to implement this properly.
|
||||
// We do our best to emulate it using futimes.
|
||||
func (fs *loopbackFileSystem) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) fuse.Status {
|
||||
tv := make([]syscall.Timeval, 2)
|
||||
if a == nil {
|
||||
tv[0].Usec = _UTIME_OMIT
|
||||
} else {
|
||||
tv[0] = timeToTimeval(a)
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
tv[1].Usec = _UTIME_OMIT
|
||||
} else {
|
||||
tv[1] = timeToTimeval(m)
|
||||
}
|
||||
|
||||
err := syscall.Utimes(path, tv)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
79
vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_linux.go
generated
vendored
Normal file
79
vendor/github.com/hanwen/go-fuse/fuse/pathfs/loopback_linux.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
func (fs *loopbackFileSystem) StatFs(name string) *fuse.StatfsOut {
|
||||
s := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(fs.GetPath(name), &s)
|
||||
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,
|
||||
Frsize: uint32(s.Frsize),
|
||||
NameLen: uint32(s.Namelen),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
|
||||
data, err := listXAttr(fs.GetPath(name))
|
||||
|
||||
return data, fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
||||
err := sysRemovexattr(fs.GetPath(name), attr)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) String() string {
|
||||
return fmt.Sprintf("LoopbackFs(%s)", fs.Root)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||
data := make([]byte, 1024)
|
||||
data, err := getXAttr(fs.GetPath(name), attr, data)
|
||||
|
||||
return data, fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
func (fs *loopbackFileSystem) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
err := syscall.Setxattr(fs.GetPath(name), attr, data, flags)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
|
||||
const _UTIME_NOW = ((1 << 30) - 1)
|
||||
const _UTIME_OMIT = ((1 << 30) - 2)
|
||||
|
||||
// Utimens - path based version of loopbackFile.Utimens()
|
||||
func (fs *loopbackFileSystem) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
var ts [2]syscall.Timespec
|
||||
|
||||
if a == nil {
|
||||
ts[0].Nsec = _UTIME_OMIT
|
||||
} else {
|
||||
ts[0] = syscall.NsecToTimespec(a.UnixNano())
|
||||
ts[0].Nsec = 0
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
ts[1].Nsec = _UTIME_OMIT
|
||||
} else {
|
||||
ts[1] = syscall.NsecToTimespec(m.UnixNano())
|
||||
ts[1].Nsec = 0
|
||||
}
|
||||
|
||||
err := sysUtimensat(0, fs.GetPath(path), &ts, _AT_SYMLINK_NOFOLLOW)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
95
vendor/github.com/hanwen/go-fuse/fuse/pathfs/owner_test.go
generated
vendored
Normal file
95
vendor/github.com/hanwen/go-fuse/fuse/pathfs/owner_test.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
type ownerFs struct {
|
||||
FileSystem
|
||||
}
|
||||
|
||||
const _RANDOM_OWNER = 31415265
|
||||
|
||||
func (fs *ownerFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
if name == "" {
|
||||
return &fuse.Attr{
|
||||
Mode: fuse.S_IFDIR | 0755,
|
||||
}, fuse.OK
|
||||
}
|
||||
a := &fuse.Attr{
|
||||
Mode: fuse.S_IFREG | 0644,
|
||||
}
|
||||
a.Uid = _RANDOM_OWNER
|
||||
a.Gid = _RANDOM_OWNER
|
||||
return a, fuse.OK
|
||||
}
|
||||
|
||||
func setupOwnerTest(t *testing.T, opts *nodefs.Options) (workdir string, cleanup func()) {
|
||||
wd, err := ioutil.TempDir("", "go-fuse-owner_test")
|
||||
|
||||
fs := &ownerFs{NewDefaultFileSystem()}
|
||||
nfs := NewPathNodeFs(fs, nil)
|
||||
state, _, err := nodefs.MountRoot(wd, nfs.Root(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("MountNodeFileSystem failed: %v", err)
|
||||
}
|
||||
go state.Serve()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
return wd, func() {
|
||||
state.Unmount()
|
||||
os.RemoveAll(wd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOwnerDefault(t *testing.T) {
|
||||
wd, cleanup := setupOwnerTest(t, nodefs.NewOptions())
|
||||
defer cleanup()
|
||||
|
||||
var stat syscall.Stat_t
|
||||
err := syscall.Lstat(wd+"/foo", &stat)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
if int(stat.Uid) != os.Getuid() || int(stat.Gid) != os.Getgid() {
|
||||
t.Fatal("Should use current uid for mount")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOwnerRoot(t *testing.T) {
|
||||
wd, cleanup := setupOwnerTest(t, &nodefs.Options{})
|
||||
defer cleanup()
|
||||
|
||||
var st syscall.Stat_t
|
||||
err := syscall.Lstat(wd+"/foo", &st)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
if st.Uid != _RANDOM_OWNER || st.Gid != _RANDOM_OWNER {
|
||||
t.Fatal("Should use FS owner uid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOwnerOverride(t *testing.T) {
|
||||
wd, cleanup := setupOwnerTest(t, &nodefs.Options{Owner: &fuse.Owner{Uid: 42, Gid: 43}})
|
||||
defer cleanup()
|
||||
|
||||
var stat syscall.Stat_t
|
||||
err := syscall.Lstat(wd+"/foo", &stat)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
if stat.Uid != 42 || stat.Gid != 43 {
|
||||
t.Fatal("Should use current uid for mount")
|
||||
}
|
||||
}
|
||||
737
vendor/github.com/hanwen/go-fuse/fuse/pathfs/pathfs.go
generated
vendored
Normal file
737
vendor/github.com/hanwen/go-fuse/fuse/pathfs/pathfs.go
generated
vendored
Normal file
@@ -0,0 +1,737 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
// 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
|
||||
// path. The path name is then used to call into an object that has
|
||||
// the FileSystem interface.
|
||||
//
|
||||
// Lookups (ie. FileSystem.GetAttr) may return a inode number in its
|
||||
// return value. The inode number ("clientInode") is used to indicate
|
||||
// linked files.
|
||||
type PathNodeFs struct {
|
||||
debug bool
|
||||
fs FileSystem
|
||||
root *pathInode
|
||||
connector *nodefs.FileSystemConnector
|
||||
|
||||
// protects clientInodeMap
|
||||
pathLock sync.RWMutex
|
||||
|
||||
// This map lists all the parent links known for a given inode number.
|
||||
clientInodeMap map[uint64]*refCountedInode
|
||||
|
||||
options *PathNodeFsOptions
|
||||
}
|
||||
|
||||
// SetDebug toggles debug information: it will log path names for
|
||||
// each operation processed.
|
||||
func (fs *PathNodeFs) SetDebug(dbg bool) {
|
||||
fs.debug = dbg
|
||||
}
|
||||
|
||||
// Mount mounts a another node filesystem with the given root on the
|
||||
// path. The last component of the path should not exist yet.
|
||||
func (fs *PathNodeFs) Mount(path string, root nodefs.Node, opts *nodefs.Options) fuse.Status {
|
||||
dir, name := filepath.Split(path)
|
||||
if dir != "" {
|
||||
dir = filepath.Clean(dir)
|
||||
}
|
||||
parent := fs.LookupNode(dir)
|
||||
if parent == nil {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
return fs.connector.Mount(parent, name, root, opts)
|
||||
}
|
||||
|
||||
// ForgetClientInodes forgets all known information on client inodes.
|
||||
func (fs *PathNodeFs) ForgetClientInodes() {
|
||||
if !fs.options.ClientInodes {
|
||||
return
|
||||
}
|
||||
fs.pathLock.Lock()
|
||||
fs.clientInodeMap = map[uint64]*refCountedInode{}
|
||||
fs.root.forgetClientInodes()
|
||||
fs.pathLock.Unlock()
|
||||
}
|
||||
|
||||
// Rereads all inode numbers for all known files.
|
||||
func (fs *PathNodeFs) RereadClientInodes() {
|
||||
if !fs.options.ClientInodes {
|
||||
return
|
||||
}
|
||||
fs.ForgetClientInodes()
|
||||
fs.root.updateClientInodes()
|
||||
}
|
||||
|
||||
// UnmountNode unmounts the node filesystem with the given root.
|
||||
func (fs *PathNodeFs) UnmountNode(node *nodefs.Inode) fuse.Status {
|
||||
return fs.connector.Unmount(node)
|
||||
}
|
||||
|
||||
// UnmountNode unmounts the node filesystem with the given root.
|
||||
func (fs *PathNodeFs) Unmount(path string) fuse.Status {
|
||||
node := fs.Node(path)
|
||||
if node == nil {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
return fs.connector.Unmount(node)
|
||||
}
|
||||
|
||||
// String returns a name for this file system
|
||||
func (fs *PathNodeFs) String() string {
|
||||
name := fs.fs.String()
|
||||
if name == "defaultFileSystem" {
|
||||
name = fmt.Sprintf("%T", fs.fs)
|
||||
name = strings.TrimLeft(name, "*")
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Connector returns the FileSystemConnector (the bridge to the raw
|
||||
// protocol) for this PathNodeFs.
|
||||
func (fs *PathNodeFs) Connector() *nodefs.FileSystemConnector {
|
||||
return fs.connector
|
||||
}
|
||||
|
||||
// Node looks up the Inode that corresponds to the given path name, or
|
||||
// returns nil if not found.
|
||||
func (fs *PathNodeFs) Node(name string) *nodefs.Inode {
|
||||
n, rest := fs.LastNode(name)
|
||||
if len(rest) > 0 {
|
||||
return nil
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Like Node, but use Lookup to discover inodes we may not have yet.
|
||||
func (fs *PathNodeFs) LookupNode(name string) *nodefs.Inode {
|
||||
return fs.connector.LookupNode(fs.Root().Inode(), name)
|
||||
}
|
||||
|
||||
// Path constructs a path for the given Inode. If the file system
|
||||
// implements hard links through client-inode numbers, the path may
|
||||
// not be unique.
|
||||
func (fs *PathNodeFs) Path(node *nodefs.Inode) string {
|
||||
pNode := node.Node().(*pathInode)
|
||||
return pNode.GetPath()
|
||||
}
|
||||
|
||||
// LastNode finds the deepest inode known corresponding to a path. The
|
||||
// unknown part of the filename is also returned.
|
||||
func (fs *PathNodeFs) LastNode(name string) (*nodefs.Inode, []string) {
|
||||
return fs.connector.Node(fs.Root().Inode(), name)
|
||||
}
|
||||
|
||||
// FileNotify notifies that file contents were changed within the
|
||||
// given range. Use negative offset for metadata-only invalidation,
|
||||
// and zero-length for invalidating all content.
|
||||
func (fs *PathNodeFs) FileNotify(path string, off int64, length int64) fuse.Status {
|
||||
node, r := fs.connector.Node(fs.root.Inode(), path)
|
||||
if len(r) > 0 {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
return fs.connector.FileNotify(node, off, length)
|
||||
}
|
||||
|
||||
// EntryNotify makes the kernel forget the entry data from the given
|
||||
// name from a directory. After this call, the kernel will issue a
|
||||
// new lookup request for the given name when necessary.
|
||||
func (fs *PathNodeFs) EntryNotify(dir string, name string) fuse.Status {
|
||||
node, rest := fs.connector.Node(fs.root.Inode(), dir)
|
||||
if len(rest) > 0 {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
return fs.connector.EntryNotify(node, name)
|
||||
}
|
||||
|
||||
// Notify ensures that the path name is invalidates: if the inode is
|
||||
// known, it issues an file content Notify, if not, an entry notify
|
||||
// for the path is issued. The latter will clear out non-existence
|
||||
// cache entries.
|
||||
func (fs *PathNodeFs) Notify(path string) fuse.Status {
|
||||
node, rest := fs.connector.Node(fs.root.Inode(), path)
|
||||
if len(rest) > 0 {
|
||||
return fs.connector.EntryNotify(node, rest[0])
|
||||
}
|
||||
return fs.connector.FileNotify(node, 0, 0)
|
||||
}
|
||||
|
||||
// AllFiles returns all open files for the inode corresponding with
|
||||
// the given mask.
|
||||
func (fs *PathNodeFs) AllFiles(name string, mask uint32) []nodefs.WithFlags {
|
||||
n := fs.Node(name)
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
return n.Files(mask)
|
||||
}
|
||||
|
||||
// NewPathNodeFs returns a file system that translates from inodes to
|
||||
// path names.
|
||||
func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs {
|
||||
root := &pathInode{}
|
||||
root.fs = fs
|
||||
|
||||
if opts == nil {
|
||||
opts = &PathNodeFsOptions{}
|
||||
}
|
||||
|
||||
pfs := &PathNodeFs{
|
||||
fs: fs,
|
||||
root: root,
|
||||
clientInodeMap: map[uint64]*refCountedInode{},
|
||||
options: opts,
|
||||
}
|
||||
root.pathFs = pfs
|
||||
return pfs
|
||||
}
|
||||
|
||||
// Root returns the root node for the path filesystem.
|
||||
func (fs *PathNodeFs) Root() nodefs.Node {
|
||||
return fs.root
|
||||
}
|
||||
|
||||
// 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.
|
||||
type pathInode struct {
|
||||
pathFs *PathNodeFs
|
||||
fs FileSystem
|
||||
|
||||
// This is to correctly resolve hardlinks of the underlying
|
||||
// real filesystem.
|
||||
clientInode uint64
|
||||
inode *nodefs.Inode
|
||||
}
|
||||
|
||||
func (n *pathInode) OnMount(conn *nodefs.FileSystemConnector) {
|
||||
n.pathFs.connector = conn
|
||||
n.pathFs.fs.OnMount(n.pathFs)
|
||||
}
|
||||
|
||||
func (n *pathInode) OnUnmount() {
|
||||
}
|
||||
|
||||
// Drop all known client inodes. Must have the treeLock.
|
||||
func (n *pathInode) forgetClientInodes() {
|
||||
n.clientInode = 0
|
||||
for _, ch := range n.Inode().FsChildren() {
|
||||
ch.Node().(*pathInode).forgetClientInodes()
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *pathInode) Deletable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *pathInode) Inode() *nodefs.Inode {
|
||||
return n.inode
|
||||
}
|
||||
|
||||
func (n *pathInode) SetInode(node *nodefs.Inode) {
|
||||
n.inode = node
|
||||
}
|
||||
|
||||
// Reread all client nodes below this node. Must run outside the treeLock.
|
||||
func (n *pathInode) updateClientInodes() {
|
||||
n.GetAttr(&fuse.Attr{}, nil, nil)
|
||||
for _, ch := range n.Inode().FsChildren() {
|
||||
ch.Node().(*pathInode).updateClientInodes()
|
||||
}
|
||||
}
|
||||
|
||||
// GetPath returns the path relative to the mount governing this
|
||||
// inode. It returns nil for mount if the file was deleted or the
|
||||
// filesystem unmounted.
|
||||
func (n *pathInode) GetPath() string {
|
||||
if n == n.pathFs.root {
|
||||
return ""
|
||||
}
|
||||
|
||||
pathLen := 1
|
||||
|
||||
// The simple solution is to collect names, and reverse join
|
||||
// them, them, but since this is a hot path, we take some
|
||||
// effort to avoid allocations.
|
||||
|
||||
n.pathFs.pathLock.RLock()
|
||||
walkUp := n.Inode()
|
||||
|
||||
// TODO - guess depth?
|
||||
segments := make([]string, 0, 10)
|
||||
for {
|
||||
parent, name := walkUp.Parent()
|
||||
if parent == nil {
|
||||
break
|
||||
}
|
||||
segments = append(segments, name)
|
||||
pathLen += len(name) + 1
|
||||
walkUp = parent
|
||||
}
|
||||
pathLen--
|
||||
|
||||
pathBytes := make([]byte, 0, pathLen)
|
||||
for i := len(segments) - 1; i >= 0; i-- {
|
||||
pathBytes = append(pathBytes, segments[i]...)
|
||||
if i > 0 {
|
||||
pathBytes = append(pathBytes, '/')
|
||||
}
|
||||
}
|
||||
n.pathFs.pathLock.RUnlock()
|
||||
|
||||
path := string(pathBytes)
|
||||
if n.pathFs.debug {
|
||||
log.Printf("Inode = %q (%s)", path, n.fs.String())
|
||||
}
|
||||
|
||||
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
|
||||
// still opened.
|
||||
|
||||
// TODO - add a deterministic disambiguating suffix.
|
||||
return ".deleted"
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (n *pathInode) OnAdd(parent *nodefs.Inode, name string) {
|
||||
// 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 {
|
||||
childInode := n.Inode().RmChild(name)
|
||||
if childInode == nil {
|
||||
return nil
|
||||
}
|
||||
return childInode.Node().(*pathInode)
|
||||
}
|
||||
|
||||
func (n *pathInode) OnRemove(parent *nodefs.Inode, name string) {
|
||||
if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() {
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// 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 == 0 || n.clientInode != 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() {
|
||||
return
|
||||
}
|
||||
n.pathFs.pathLock.Lock()
|
||||
defer n.pathFs.pathLock.Unlock()
|
||||
n.clientInode = ino
|
||||
n.pathFs.clientInodeMap[ino] = &refCountedInode{node: n, refCount: 1}
|
||||
}
|
||||
|
||||
func (n *pathInode) OnForget() {
|
||||
if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() {
|
||||
return
|
||||
}
|
||||
n.pathFs.pathLock.Lock()
|
||||
delete(n.pathFs.clientInodeMap, n.clientInode)
|
||||
n.pathFs.pathLock.Unlock()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// FS operations
|
||||
|
||||
func (n *pathInode) StatFs() *fuse.StatfsOut {
|
||||
return n.fs.StatFs(n.GetPath())
|
||||
}
|
||||
|
||||
func (n *pathInode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
|
||||
path := n.GetPath()
|
||||
|
||||
val, err := n.fs.Readlink(path, c)
|
||||
return []byte(val), err
|
||||
}
|
||||
|
||||
func (n *pathInode) Access(mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
p := n.GetPath()
|
||||
return n.fs.Access(p, mode, context)
|
||||
}
|
||||
|
||||
func (n *pathInode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
|
||||
return n.fs.GetXAttr(n.GetPath(), attribute, context)
|
||||
}
|
||||
|
||||
func (n *pathInode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status {
|
||||
p := n.GetPath()
|
||||
return n.fs.RemoveXAttr(p, attr, context)
|
||||
}
|
||||
|
||||
func (n *pathInode) SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
return n.fs.SetXAttr(n.GetPath(), attr, data, flags, context)
|
||||
}
|
||||
|
||||
func (n *pathInode) ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status) {
|
||||
return n.fs.ListXAttr(n.GetPath(), context)
|
||||
}
|
||||
|
||||
func (n *pathInode) Flush(file nodefs.File, openFlags uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return file.Flush()
|
||||
}
|
||||
|
||||
func (n *pathInode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
|
||||
return n.fs.OpenDir(n.GetPath(), context)
|
||||
}
|
||||
|
||||
func (n *pathInode) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
|
||||
fullPath := filepath.Join(n.GetPath(), name)
|
||||
code := n.fs.Mknod(fullPath, mode, dev, context)
|
||||
var child *nodefs.Inode
|
||||
if code.Ok() {
|
||||
pNode := n.createChild(name, false)
|
||||
child = pNode.Inode()
|
||||
}
|
||||
return child, code
|
||||
}
|
||||
|
||||
func (n *pathInode) Mkdir(name string, mode uint32, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
|
||||
fullPath := filepath.Join(n.GetPath(), name)
|
||||
code := n.fs.Mkdir(fullPath, mode, context)
|
||||
var child *nodefs.Inode
|
||||
if code.Ok() {
|
||||
pNode := n.createChild(name, true)
|
||||
child = pNode.Inode()
|
||||
}
|
||||
return child, code
|
||||
}
|
||||
|
||||
func (n *pathInode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
code = n.fs.Unlink(filepath.Join(n.GetPath(), name), context)
|
||||
if code.Ok() {
|
||||
n.Inode().RmChild(name)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
code = n.fs.Rmdir(filepath.Join(n.GetPath(), name), context)
|
||||
if code.Ok() {
|
||||
n.Inode().RmChild(name)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
|
||||
fullPath := filepath.Join(n.GetPath(), name)
|
||||
code := n.fs.Symlink(content, fullPath, context)
|
||||
var child *nodefs.Inode
|
||||
if code.Ok() {
|
||||
pNode := n.createChild(name, false)
|
||||
child = pNode.Inode()
|
||||
}
|
||||
return child, code
|
||||
}
|
||||
|
||||
func (n *pathInode) Rename(oldName string, newParent nodefs.Node, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
p := newParent.(*pathInode)
|
||||
oldPath := filepath.Join(n.GetPath(), oldName)
|
||||
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)
|
||||
if ch != nil {
|
||||
// oldName may have been forgotten in the meantime.
|
||||
p.Inode().AddChild(newName, ch)
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Link(name string, existingFsnode nodefs.Node, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
|
||||
if !n.pathFs.options.ClientInodes {
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
newPath := filepath.Join(n.GetPath(), name)
|
||||
existing := existingFsnode.(*pathInode)
|
||||
oldPath := existing.GetPath()
|
||||
code := n.fs.Link(oldPath, newPath, context)
|
||||
|
||||
var a *fuse.Attr
|
||||
if code.Ok() {
|
||||
a, code = n.fs.GetAttr(newPath, context)
|
||||
}
|
||||
|
||||
var child *nodefs.Inode
|
||||
if code.Ok() {
|
||||
if existing.clientInode != 0 && existing.clientInode == a.Ino {
|
||||
child = existing.Inode()
|
||||
n.Inode().AddChild(name, existing.Inode())
|
||||
} else {
|
||||
pNode := n.createChild(name, false)
|
||||
child = pNode.Inode()
|
||||
pNode.clientInode = a.Ino
|
||||
}
|
||||
}
|
||||
return child, code
|
||||
}
|
||||
|
||||
func (n *pathInode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, *nodefs.Inode, fuse.Status) {
|
||||
var child *nodefs.Inode
|
||||
fullPath := filepath.Join(n.GetPath(), name)
|
||||
file, code := n.fs.Create(fullPath, flags, mode, context)
|
||||
if code.Ok() {
|
||||
pNode := n.createChild(name, false)
|
||||
child = pNode.Inode()
|
||||
}
|
||||
return file, child, code
|
||||
}
|
||||
|
||||
func (n *pathInode) createChild(name string, isDir bool) *pathInode {
|
||||
i := &pathInode{
|
||||
fs: n.fs,
|
||||
pathFs: n.pathFs,
|
||||
}
|
||||
|
||||
n.Inode().NewChild(name, isDir, i)
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func (n *pathInode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
p := n.GetPath()
|
||||
file, code = n.fs.Open(p, flags, context)
|
||||
if n.pathFs.debug {
|
||||
file = &nodefs.WithFlags{
|
||||
File: file,
|
||||
Description: n.GetPath(),
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (n *pathInode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (node *nodefs.Inode, code fuse.Status) {
|
||||
fullPath := filepath.Join(n.GetPath(), name)
|
||||
fi, code := n.fs.GetAttr(fullPath, context)
|
||||
if code.Ok() {
|
||||
node = n.findChild(fi, name, fullPath).Inode()
|
||||
*out = *fi
|
||||
}
|
||||
|
||||
return node, code
|
||||
}
|
||||
|
||||
func (n *pathInode) findChild(fi *fuse.Attr, name string, fullPath string) (out *pathInode) {
|
||||
if fi.Ino > 0 {
|
||||
n.pathFs.pathLock.RLock()
|
||||
r := n.pathFs.clientInodeMap[fi.Ino]
|
||||
if r != nil {
|
||||
out = r.node
|
||||
r.refCount++
|
||||
if fi.Nlink == 1 {
|
||||
log.Printf("Found linked inode, but Nlink == 1, ino=%d, fullPath=%q", fi.Ino, fullPath)
|
||||
}
|
||||
}
|
||||
n.pathFs.pathLock.RUnlock()
|
||||
}
|
||||
|
||||
if out == nil {
|
||||
out = n.createChild(name, fi.IsDir())
|
||||
out.setClientInode(fi.Ino)
|
||||
} else {
|
||||
n.Inode().AddChild(name, out.Inode())
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (n *pathInode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
|
||||
var fi *fuse.Attr
|
||||
if file == nil {
|
||||
// Linux currently (tested on v4.4) does not pass a file descriptor for
|
||||
// fstat. To be able to stat a deleted file we have to find ourselves
|
||||
// an open fd.
|
||||
file = n.Inode().AnyFile()
|
||||
}
|
||||
|
||||
if file != nil {
|
||||
code = file.GetAttr(out)
|
||||
}
|
||||
|
||||
if file == nil || code == fuse.ENOSYS || code == fuse.EBADF {
|
||||
fi, code = n.fs.GetAttr(n.GetPath(), context)
|
||||
}
|
||||
|
||||
if fi != nil {
|
||||
n.setClientInode(fi.Ino)
|
||||
}
|
||||
|
||||
if fi != nil && !fi.IsDir() && fi.Nlink == 0 {
|
||||
fi.Nlink = 1
|
||||
}
|
||||
|
||||
if fi != nil {
|
||||
*out = *fi
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Chmod(file nodefs.File, perms uint32, context *fuse.Context) (code fuse.Status) {
|
||||
// Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor
|
||||
// to FUSE for fchmod. We still check because that may change in the future.
|
||||
if file != nil {
|
||||
code = file.Chmod(perms)
|
||||
if code != fuse.ENOSYS {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
files := n.Inode().Files(fuse.O_ANYWRITE)
|
||||
for _, f := range files {
|
||||
// TODO - pass context
|
||||
code = f.Chmod(perms)
|
||||
if code.Ok() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
|
||||
code = n.fs.Chmod(n.GetPath(), perms, context)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Chown(file nodefs.File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
// Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor
|
||||
// to FUSE for fchown. We still check because it may change in the future.
|
||||
if file != nil {
|
||||
code = file.Chown(uid, gid)
|
||||
if code != fuse.ENOSYS {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
files := n.Inode().Files(fuse.O_ANYWRITE)
|
||||
for _, f := range files {
|
||||
// TODO - pass context
|
||||
code = f.Chown(uid, gid)
|
||||
if code.Ok() {
|
||||
return code
|
||||
}
|
||||
}
|
||||
if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
|
||||
// TODO - can we get just FATTR_GID but not FATTR_UID ?
|
||||
code = n.fs.Chown(n.GetPath(), uid, gid, context)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Truncate(file nodefs.File, size uint64, context *fuse.Context) (code fuse.Status) {
|
||||
// A file descriptor was passed in AND the filesystem implements the
|
||||
// operation on the file handle. This the common case for ftruncate.
|
||||
if file != nil {
|
||||
code = file.Truncate(size)
|
||||
if code != fuse.ENOSYS {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
files := n.Inode().Files(fuse.O_ANYWRITE)
|
||||
for _, f := range files {
|
||||
// TODO - pass context
|
||||
code = f.Truncate(size)
|
||||
if code.Ok() {
|
||||
return code
|
||||
}
|
||||
}
|
||||
if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
|
||||
code = n.fs.Truncate(n.GetPath(), size, context)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Utimens(file nodefs.File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
// Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor
|
||||
// to FUSE for futimens. We still check because it may change in the future.
|
||||
if file != nil {
|
||||
code = file.Utimens(atime, mtime)
|
||||
if code != fuse.ENOSYS {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
files := n.Inode().Files(fuse.O_ANYWRITE)
|
||||
for _, f := range files {
|
||||
// TODO - pass context
|
||||
code = f.Utimens(atime, mtime)
|
||||
if code.Ok() {
|
||||
return code
|
||||
}
|
||||
}
|
||||
if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
|
||||
code = n.fs.Utimens(n.GetPath(), atime, mtime, context)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Fallocate(file nodefs.File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
if file != nil {
|
||||
code = file.Allocate(off, size, mode)
|
||||
if code.Ok() {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
files := n.Inode().Files(fuse.O_ANYWRITE)
|
||||
for _, f := range files {
|
||||
// TODO - pass context
|
||||
code = f.Allocate(off, size, mode)
|
||||
if code.Ok() {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
func (n *pathInode) Read(file nodefs.File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) {
|
||||
if file != nil {
|
||||
return file.Read(dest, off)
|
||||
}
|
||||
return nil, fuse.ENOSYS
|
||||
}
|
||||
|
||||
func (n *pathInode) Write(file nodefs.File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status) {
|
||||
if file != nil {
|
||||
return file.Write(data, off)
|
||||
}
|
||||
return 0, fuse.ENOSYS
|
||||
}
|
||||
128
vendor/github.com/hanwen/go-fuse/fuse/pathfs/prefixfs.go
generated
vendored
Normal file
128
vendor/github.com/hanwen/go-fuse/fuse/pathfs/prefixfs.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
// PrefixFileSystem adds a path prefix to incoming calls.
|
||||
type prefixFileSystem struct {
|
||||
FileSystem FileSystem
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func NewPrefixFileSystem(fs FileSystem, prefix string) FileSystem {
|
||||
return &prefixFileSystem{fs, prefix}
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) SetDebug(debug bool) {
|
||||
fs.FileSystem.SetDebug(debug)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) prefixed(n string) string {
|
||||
return filepath.Join(fs.Prefix, n)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
return fs.FileSystem.GetAttr(fs.prefixed(name), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
|
||||
return fs.FileSystem.Readlink(fs.prefixed(name), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status {
|
||||
return fs.FileSystem.Mknod(fs.prefixed(name), mode, dev, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status {
|
||||
return fs.FileSystem.Mkdir(fs.prefixed(name), mode, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Unlink(fs.prefixed(name), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Rmdir(fs.prefixed(name), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Symlink(value string, linkName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Symlink(value, fs.prefixed(linkName), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Rename(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Rename(fs.prefixed(oldName), fs.prefixed(newName), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Link(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Link(fs.prefixed(oldName), fs.prefixed(newName), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Chmod(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Chmod(fs.prefixed(name), mode, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Chown(name string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Chown(fs.prefixed(name), uid, gid, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Truncate(name string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Truncate(fs.prefixed(name), offset, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Open(name string, flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
return fs.FileSystem.Open(fs.prefixed(name), flags, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
|
||||
return fs.FileSystem.OpenDir(fs.prefixed(name), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) OnMount(nodeFs *PathNodeFs) {
|
||||
fs.FileSystem.OnMount(nodeFs)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) OnUnmount() {
|
||||
fs.FileSystem.OnUnmount()
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Access(fs.prefixed(name), mode, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
return fs.FileSystem.Create(fs.prefixed(name), flags, mode, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) Utimens(name string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Utimens(fs.prefixed(name), Atime, Mtime, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||
return fs.FileSystem.GetXAttr(fs.prefixed(name), attr, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
return fs.FileSystem.SetXAttr(fs.prefixed(name), attr, data, flags, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
|
||||
return fs.FileSystem.ListXAttr(fs.prefixed(name), context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
||||
return fs.FileSystem.RemoveXAttr(fs.prefixed(name), attr, context)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) String() string {
|
||||
return fmt.Sprintf("prefixFileSystem(%s,%s)", fs.FileSystem.String(), fs.Prefix)
|
||||
}
|
||||
|
||||
func (fs *prefixFileSystem) StatFs(name string) *fuse.StatfsOut {
|
||||
return fs.FileSystem.StatFs(fs.prefixed(name))
|
||||
}
|
||||
119
vendor/github.com/hanwen/go-fuse/fuse/pathfs/readonlyfs.go
generated
vendored
Normal file
119
vendor/github.com/hanwen/go-fuse/fuse/pathfs/readonlyfs.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
// NewReadonlyFileSystem returns a wrapper that only exposes read-only
|
||||
// operations.
|
||||
func NewReadonlyFileSystem(fs FileSystem) FileSystem {
|
||||
return &readonlyFileSystem{fs}
|
||||
}
|
||||
|
||||
type readonlyFileSystem struct {
|
||||
FileSystem
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
return fs.FileSystem.GetAttr(name, context)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
|
||||
return fs.FileSystem.Readlink(name, context)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Symlink(value string, linkName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Rename(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Link(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Chmod(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Chown(name string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Truncate(name string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Open(name string, flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
if flags&fuse.O_ANYWRITE != 0 {
|
||||
return nil, fuse.EPERM
|
||||
}
|
||||
file, code = fs.FileSystem.Open(name, flags, context)
|
||||
return nodefs.NewReadOnlyFile(file), code
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
|
||||
return fs.FileSystem.OpenDir(name, context)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) OnMount(nodeFs *PathNodeFs) {
|
||||
fs.FileSystem.OnMount(nodeFs)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) OnUnmount() {
|
||||
fs.FileSystem.OnUnmount()
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) String() string {
|
||||
return fmt.Sprintf("readonlyFileSystem(%v)", fs.FileSystem)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||
return fs.FileSystem.Access(name, mode, context)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
|
||||
return nil, fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) Utimens(name string, atime *time.Time, ctime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||
return fs.FileSystem.GetXAttr(name, attr, context)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
|
||||
return fs.FileSystem.ListXAttr(name, context)
|
||||
}
|
||||
|
||||
func (fs *readonlyFileSystem) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
||||
return fuse.EPERM
|
||||
}
|
||||
140
vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_linux.go
generated
vendored
Normal file
140
vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_linux.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _zero uintptr
|
||||
|
||||
func getXAttr(path string, attr string, dest []byte) (value []byte, err error) {
|
||||
sz, err := sysGetxattr(path, attr, dest)
|
||||
for sz > cap(dest) && err == nil {
|
||||
dest = make([]byte, sz)
|
||||
sz, err = sysGetxattr(path, attr, dest)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dest[:sz], err
|
||||
}
|
||||
|
||||
func listXAttr(path string) (attributes []string, err error) {
|
||||
dest := make([]byte, 0)
|
||||
sz, err := sysListxattr(path, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for sz > cap(dest) && err == nil {
|
||||
dest = make([]byte, sz)
|
||||
sz, err = sysListxattr(path, dest)
|
||||
}
|
||||
|
||||
if sz == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// -1 to drop the final empty slice.
|
||||
dest = dest[:sz-1]
|
||||
attributesBytes := bytes.Split(dest, []byte{0})
|
||||
attributes = make([]string, len(attributesBytes))
|
||||
for i, v := range attributesBytes {
|
||||
attributes[i] = string(v)
|
||||
}
|
||||
return attributes, err
|
||||
}
|
||||
|
||||
// Below is cut & paste from std lib syscall, so gccgo 4.8.1 can
|
||||
// compile this too.
|
||||
func sysGetxattr(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 {
|
||||
_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
|
||||
}
|
||||
|
||||
func sysRemovexattr(path string, attr string) (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
|
||||
}
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func sysListxattr(path string, dest []byte) (sz int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(dest) > 0 {
|
||||
_p1 = unsafe.Pointer(&dest[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
|
||||
sz = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func sysSetxattr(path string, attr string, val []byte, flag int) error {
|
||||
return syscall.Setxattr(path, attr, val, flag)
|
||||
}
|
||||
|
||||
const _AT_SYMLINK_NOFOLLOW = 0x100
|
||||
|
||||
// Linux kernel syscall utimensat(2)
|
||||
//
|
||||
// Needed to implement SetAttr on symlinks correctly as only utimensat provides
|
||||
// AT_SYMLINK_NOFOLLOW.
|
||||
func sysUtimensat(dirfd int, pathname string, times *[2]syscall.Timespec, flags int) (err error) {
|
||||
|
||||
// Null-terminated version of pathname
|
||||
p0, err := syscall.BytePtrFromString(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS_UTIMENSAT,
|
||||
uintptr(dirfd), uintptr(unsafe.Pointer(p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = syscall.Errno(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
45
vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_test.go
generated
vendored
Normal file
45
vendor/github.com/hanwen/go-fuse/fuse/pathfs/syscall_test.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// +build linux
|
||||
|
||||
package pathfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSysUtimensat(t *testing.T) {
|
||||
symlink := "/tmp/TestSysUtimensat"
|
||||
os.Remove(symlink)
|
||||
err := os.Symlink("/nonexisting/file", symlink)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var ts [2]syscall.Timespec
|
||||
// Atime
|
||||
ts[0].Nsec = 1111
|
||||
ts[0].Sec = 2222
|
||||
// Mtime
|
||||
ts[1].Nsec = 3333
|
||||
ts[1].Sec = 4444
|
||||
|
||||
// Linux specific.
|
||||
err = sysUtimensat(0, symlink, &ts, _AT_SYMLINK_NOFOLLOW)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var st syscall.Stat_t
|
||||
err = syscall.Lstat(symlink, &st)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(st.Atim, ts[0]) {
|
||||
t.Errorf("Wrong atime: %v", st.Atim)
|
||||
}
|
||||
if !reflect.DeepEqual(st.Mtim, ts[1]) {
|
||||
t.Errorf("Wrong mtime: %v", st.Mtim)
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/hanwen/go-fuse/fuse/pathfs/verbose_test.go
generated
vendored
Normal file
9
vendor/github.com/hanwen/go-fuse/fuse/pathfs/verbose_test.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package pathfs
|
||||
|
||||
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"
|
||||
}
|
||||
202
vendor/github.com/hanwen/go-fuse/fuse/pathfs/xattr_test.go
generated
vendored
Normal file
202
vendor/github.com/hanwen/go-fuse/fuse/pathfs/xattr_test.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
// +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"
|
||||
)
|
||||
|
||||
var xattrGolden = map[string][]byte{
|
||||
"user.attr1": []byte("val1"),
|
||||
"user.attr2": []byte("val2")}
|
||||
var xattrFilename = "filename"
|
||||
|
||||
type XAttrTestFs struct {
|
||||
filename string
|
||||
attrs map[string][]byte
|
||||
|
||||
FileSystem
|
||||
}
|
||||
|
||||
func NewXAttrFs(nm string, m map[string][]byte) *XAttrTestFs {
|
||||
x := &XAttrTestFs{
|
||||
filename: nm,
|
||||
attrs: make(map[string][]byte, len(m)),
|
||||
FileSystem: NewDefaultFileSystem(),
|
||||
}
|
||||
|
||||
for k, v := range m {
|
||||
x.attrs[k] = v
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (fs *XAttrTestFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
a := &fuse.Attr{}
|
||||
if name == "" || name == "/" {
|
||||
a.Mode = fuse.S_IFDIR | 0700
|
||||
return a, fuse.OK
|
||||
}
|
||||
if name == fs.filename {
|
||||
a.Mode = fuse.S_IFREG | 0600
|
||||
return a, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (fs *XAttrTestFs) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||
if name != fs.filename {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
dest := make([]byte, len(data))
|
||||
copy(dest, data)
|
||||
fs.attrs[attr] = dest
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (fs *XAttrTestFs) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||
if name != fs.filename {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
v, ok := fs.attrs[attr]
|
||||
if !ok {
|
||||
return nil, fuse.ENODATA
|
||||
}
|
||||
return v, fuse.OK
|
||||
}
|
||||
|
||||
func (fs *XAttrTestFs) ListXAttr(name string, context *fuse.Context) (data []string, code fuse.Status) {
|
||||
if name != fs.filename {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
for k := range fs.attrs {
|
||||
data = append(data, k)
|
||||
}
|
||||
return data, fuse.OK
|
||||
}
|
||||
|
||||
func (fs *XAttrTestFs) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
||||
if name != fs.filename {
|
||||
return fuse.ENOENT
|
||||
}
|
||||
_, ok := fs.attrs[attr]
|
||||
if !ok {
|
||||
return fuse.ENODATA
|
||||
}
|
||||
delete(fs.attrs, attr)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func readXAttr(p, a string) (val []byte, err error) {
|
||||
val = make([]byte, 1024)
|
||||
return getXAttr(p, a, val)
|
||||
}
|
||||
|
||||
func xattrTestCase(t *testing.T, nm string, m map[string][]byte) (mountPoint string, cleanup func()) {
|
||||
xfs := NewXAttrFs(nm, m)
|
||||
mountPoint, err := ioutil.TempDir("", "go-fuse-xattr_test")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
|
||||
nfs := NewPathNodeFs(xfs, nil)
|
||||
state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(),
|
||||
&nodefs.Options{Debug: VerboseTest()})
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
|
||||
go state.Serve()
|
||||
return mountPoint, func() {
|
||||
state.Unmount()
|
||||
os.RemoveAll(mountPoint)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXAttrNoAttrs(t *testing.T) {
|
||||
nm := xattrFilename
|
||||
mountPoint, clean := xattrTestCase(t, nm, make(map[string][]byte))
|
||||
defer clean()
|
||||
|
||||
mounted := filepath.Join(mountPoint, nm)
|
||||
attrs, err := listXAttr(mounted)
|
||||
if err != nil {
|
||||
t.Error("Unexpected ListXAttr error", err)
|
||||
}
|
||||
|
||||
if len(attrs) > 0 {
|
||||
t.Errorf("ListXAttr(%s) = %s, want empty slice", mounted, attrs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXAttrNoExist(t *testing.T) {
|
||||
nm := xattrFilename
|
||||
mountPoint, clean := xattrTestCase(t, nm, xattrGolden)
|
||||
defer clean()
|
||||
|
||||
mounted := filepath.Join(mountPoint, nm)
|
||||
_, err := os.Lstat(mounted)
|
||||
if err != nil {
|
||||
t.Error("Unexpected stat error", err)
|
||||
}
|
||||
|
||||
val, err := readXAttr(mounted, "noexist")
|
||||
if err == nil {
|
||||
t.Error("Expected GetXAttr error", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXAttrRead(t *testing.T) {
|
||||
nm := xattrFilename
|
||||
mountPoint, clean := xattrTestCase(t, nm, xattrGolden)
|
||||
defer clean()
|
||||
|
||||
mounted := filepath.Join(mountPoint, nm)
|
||||
attrs, err := listXAttr(mounted)
|
||||
readback := make(map[string][]byte)
|
||||
if err != nil {
|
||||
t.Error("Unexpected ListXAttr error", err)
|
||||
} else {
|
||||
for _, a := range attrs {
|
||||
val, err := readXAttr(mounted, a)
|
||||
if err != nil {
|
||||
t.Errorf("GetXAttr(%q) failed: %v", a, err)
|
||||
}
|
||||
readback[a] = val
|
||||
}
|
||||
}
|
||||
|
||||
if len(readback) != len(xattrGolden) {
|
||||
t.Error("length mismatch", xattrGolden, readback)
|
||||
} else {
|
||||
for k, v := range readback {
|
||||
if bytes.Compare(xattrGolden[k], v) != 0 {
|
||||
t.Error("val mismatch", k, v, xattrGolden[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = sysSetxattr(mounted, "third", []byte("value"), 0)
|
||||
if err != nil {
|
||||
t.Error("Setxattr error", err)
|
||||
}
|
||||
val, err := readXAttr(mounted, "third")
|
||||
if err != nil || string(val) != "value" {
|
||||
t.Error("Read back set xattr:", err, string(val))
|
||||
}
|
||||
|
||||
sysRemovexattr(mounted, "third")
|
||||
val, err = readXAttr(mounted, "third")
|
||||
if err != syscall.ENODATA {
|
||||
t.Error("Data not removed?", err, val)
|
||||
}
|
||||
}
|
||||
246
vendor/github.com/hanwen/go-fuse/fuse/print.go
generated
vendored
Normal file
246
vendor/github.com/hanwen/go-fuse/fuse/print.go
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var initFlagNames map[int64]string
|
||||
var releaseFlagNames map[int64]string
|
||||
var OpenFlagNames map[int64]string
|
||||
var FuseOpenFlagNames map[int64]string
|
||||
var accessFlagName map[int64]string
|
||||
var writeFlagNames map[int64]string
|
||||
var readFlagNames map[int64]string
|
||||
|
||||
func init() {
|
||||
writeFlagNames = map[int64]string{
|
||||
WRITE_CACHE: "CACHE",
|
||||
WRITE_LOCKOWNER: "LOCKOWNER",
|
||||
}
|
||||
readFlagNames = map[int64]string{
|
||||
READ_LOCKOWNER: "LOCKOWNER",
|
||||
}
|
||||
initFlagNames = map[int64]string{
|
||||
CAP_ASYNC_READ: "ASYNC_READ",
|
||||
CAP_POSIX_LOCKS: "POSIX_LOCKS",
|
||||
CAP_FILE_OPS: "FILE_OPS",
|
||||
CAP_ATOMIC_O_TRUNC: "ATOMIC_O_TRUNC",
|
||||
CAP_EXPORT_SUPPORT: "EXPORT_SUPPORT",
|
||||
CAP_BIG_WRITES: "BIG_WRITES",
|
||||
CAP_DONT_MASK: "DONT_MASK",
|
||||
CAP_SPLICE_WRITE: "SPLICE_WRITE",
|
||||
CAP_SPLICE_MOVE: "SPLICE_MOVE",
|
||||
CAP_SPLICE_READ: "SPLICE_READ",
|
||||
CAP_FLOCK_LOCKS: "FLOCK_LOCKS",
|
||||
CAP_IOCTL_DIR: "IOCTL_DIR",
|
||||
CAP_AUTO_INVAL_DATA: "AUTO_INVAL_DATA",
|
||||
CAP_READDIRPLUS: "READDIRPLUS",
|
||||
CAP_READDIRPLUS_AUTO: "READDIRPLUS_AUTO",
|
||||
CAP_ASYNC_DIO: "ASYNC_DIO",
|
||||
CAP_WRITEBACK_CACHE: "WRITEBACK_CACHE",
|
||||
CAP_NO_OPEN_SUPPORT: "NO_OPEN_SUPPORT",
|
||||
}
|
||||
releaseFlagNames = map[int64]string{
|
||||
RELEASE_FLUSH: "FLUSH",
|
||||
}
|
||||
OpenFlagNames = map[int64]string{
|
||||
int64(os.O_WRONLY): "WRONLY",
|
||||
int64(os.O_RDWR): "RDWR",
|
||||
int64(os.O_APPEND): "APPEND",
|
||||
int64(syscall.O_ASYNC): "ASYNC",
|
||||
int64(os.O_CREATE): "CREAT",
|
||||
int64(os.O_EXCL): "EXCL",
|
||||
int64(syscall.O_NOCTTY): "NOCTTY",
|
||||
int64(syscall.O_NONBLOCK): "NONBLOCK",
|
||||
int64(os.O_SYNC): "SYNC",
|
||||
int64(os.O_TRUNC): "TRUNC",
|
||||
|
||||
int64(syscall.O_CLOEXEC): "CLOEXEC",
|
||||
int64(syscall.O_DIRECTORY): "DIRECTORY",
|
||||
}
|
||||
FuseOpenFlagNames = map[int64]string{
|
||||
FOPEN_DIRECT_IO: "DIRECT",
|
||||
FOPEN_KEEP_CACHE: "CACHE",
|
||||
FOPEN_NONSEEKABLE: "NONSEEK",
|
||||
}
|
||||
accessFlagName = map[int64]string{
|
||||
X_OK: "x",
|
||||
W_OK: "w",
|
||||
R_OK: "r",
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func FlagString(names map[int64]string, fl int64, def string) string {
|
||||
s := []string{}
|
||||
for k, v := range names {
|
||||
if fl&k != 0 {
|
||||
s = append(s, v)
|
||||
fl ^= k
|
||||
}
|
||||
}
|
||||
if len(s) == 0 && def != "" {
|
||||
s = []string{def}
|
||||
}
|
||||
if fl != 0 {
|
||||
s = append(s, fmt.Sprintf("0x%x", fl))
|
||||
}
|
||||
|
||||
return strings.Join(s, ",")
|
||||
}
|
||||
|
||||
func (me *ForgetIn) string() string {
|
||||
return fmt.Sprintf("{Nlookup=%d}", me.Nlookup)
|
||||
}
|
||||
|
||||
func (me *_BatchForgetIn) string() string {
|
||||
return fmt.Sprintf("{Count=%d}", me.Count)
|
||||
}
|
||||
|
||||
func (me *MkdirIn) string() string {
|
||||
return fmt.Sprintf("{0%o (0%o)}", me.Mode, me.Umask)
|
||||
}
|
||||
|
||||
func (me *RenameIn) string() string {
|
||||
return fmt.Sprintf("{%d}", me.Newdir)
|
||||
}
|
||||
|
||||
func (me *SetAttrIn) string() string {
|
||||
s := []string{}
|
||||
if me.Valid&FATTR_MODE != 0 {
|
||||
s = append(s, fmt.Sprintf("mode 0%o", me.Mode))
|
||||
}
|
||||
if me.Valid&FATTR_UID != 0 {
|
||||
s = append(s, fmt.Sprintf("uid %d", me.Uid))
|
||||
}
|
||||
if me.Valid&FATTR_GID != 0 {
|
||||
s = append(s, fmt.Sprintf("uid %d", me.Gid))
|
||||
}
|
||||
if me.Valid&FATTR_SIZE != 0 {
|
||||
s = append(s, fmt.Sprintf("size %d", me.Size))
|
||||
}
|
||||
if me.Valid&FATTR_ATIME != 0 {
|
||||
s = append(s, fmt.Sprintf("atime %d.%09d", me.Atime, me.Atimensec))
|
||||
}
|
||||
if me.Valid&FATTR_MTIME != 0 {
|
||||
s = append(s, fmt.Sprintf("mtime %d.%09d", me.Mtime, me.Mtimensec))
|
||||
}
|
||||
if me.Valid&FATTR_FH != 0 {
|
||||
s = append(s, fmt.Sprintf("fh %d", me.Fh))
|
||||
}
|
||||
// TODO - FATTR_ATIME_NOW = (1 << 7), FATTR_MTIME_NOW = (1 << 8), FATTR_LOCKOWNER = (1 << 9)
|
||||
return fmt.Sprintf("{%s}", strings.Join(s, ", "))
|
||||
}
|
||||
|
||||
func (me *ReleaseIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d %s %s L%d}",
|
||||
me.Fh, FlagString(OpenFlagNames, int64(me.Flags), ""),
|
||||
FlagString(releaseFlagNames, int64(me.ReleaseFlags), ""),
|
||||
me.LockOwner)
|
||||
}
|
||||
|
||||
func (me *OpenIn) string() string {
|
||||
return fmt.Sprintf("{%s}", FlagString(OpenFlagNames, int64(me.Flags), "O_RDONLY"))
|
||||
}
|
||||
|
||||
func (me *OpenOut) string() string {
|
||||
return fmt.Sprintf("{Fh %d %s}", me.Fh,
|
||||
FlagString(FuseOpenFlagNames, int64(me.OpenFlags), ""))
|
||||
}
|
||||
|
||||
func (me *InitIn) string() string {
|
||||
return fmt.Sprintf("{%d.%d Ra 0x%x %s}",
|
||||
me.Major, me.Minor, me.MaxReadAhead,
|
||||
FlagString(initFlagNames, int64(me.Flags), ""))
|
||||
}
|
||||
|
||||
func (me *InitOut) string() string {
|
||||
return fmt.Sprintf("{%d.%d Ra 0x%x %s %d/%d Wr 0x%x Tg 0x%x}",
|
||||
me.Major, me.Minor, me.MaxReadAhead,
|
||||
FlagString(initFlagNames, int64(me.Flags), ""),
|
||||
me.CongestionThreshold, me.MaxBackground, me.MaxWrite,
|
||||
me.TimeGran)
|
||||
}
|
||||
|
||||
func (s *FsyncIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d Flags %x}", s.Fh, s.FsyncFlags)
|
||||
}
|
||||
|
||||
func (me *SetXAttrIn) string() string {
|
||||
return fmt.Sprintf("{sz %d f%o}", me.Size, me.Flags)
|
||||
}
|
||||
|
||||
func (me *GetXAttrIn) string() string {
|
||||
return fmt.Sprintf("{sz %d}", me.Size)
|
||||
}
|
||||
|
||||
func (me *GetXAttrOut) string() string {
|
||||
return fmt.Sprintf("{sz %d}", me.Size)
|
||||
}
|
||||
|
||||
func (me *AccessIn) string() string {
|
||||
return fmt.Sprintf("{%s}", FlagString(accessFlagName, int64(me.Mask), ""))
|
||||
}
|
||||
|
||||
func (me *FlushIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d}", me.Fh)
|
||||
}
|
||||
|
||||
func (me *AttrOut) string() string {
|
||||
return fmt.Sprintf(
|
||||
"{A%d.%09d %v}",
|
||||
me.AttrValid, me.AttrValidNsec, &me.Attr)
|
||||
}
|
||||
|
||||
// Returned by LOOKUP
|
||||
func (me *EntryOut) string() string {
|
||||
return fmt.Sprintf("{NodeId: %d Generation=%d EntryValid=%d.%03d AttrValid=%d.%03d Attr=%v}",
|
||||
me.NodeId, me.Generation, me.EntryValid, me.EntryValidNsec/1000000,
|
||||
me.AttrValid, me.AttrValidNsec/1000000, &me.Attr)
|
||||
}
|
||||
|
||||
func (me *CreateOut) string() string {
|
||||
return fmt.Sprintf("{NodeId: %d Generation=%d %v %v}", me.NodeId, me.Generation, &me.EntryOut, &me.OpenOut)
|
||||
}
|
||||
|
||||
func (me *StatfsOut) string() string {
|
||||
return fmt.Sprintf(
|
||||
"{blocks (%d,%d)/%d files %d/%d bs%d nl%d frs%d}",
|
||||
me.Bfree, me.Bavail, me.Blocks, me.Ffree, me.Files,
|
||||
me.Bsize, me.NameLen, me.Frsize)
|
||||
}
|
||||
|
||||
func (o *NotifyInvalEntryOut) string() string {
|
||||
return fmt.Sprintf("{parent %d sz %d}", o.Parent, o.NameLen)
|
||||
}
|
||||
|
||||
func (o *NotifyInvalInodeOut) string() string {
|
||||
return fmt.Sprintf("{ino %d off %d sz %d}", o.Ino, o.Off, o.Length)
|
||||
}
|
||||
|
||||
func (o *NotifyInvalDeleteOut) string() string {
|
||||
return fmt.Sprintf("{parent %d ch %d sz %d}", o.Parent, o.Child, o.NameLen)
|
||||
}
|
||||
|
||||
func (f *FallocateIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d off %d sz %d mod 0%o}",
|
||||
f.Fh, f.Offset, f.Length, f.Mode)
|
||||
}
|
||||
|
||||
func (f *LinkIn) string() string {
|
||||
return fmt.Sprintf("{Oldnodeid: %d}", f.Oldnodeid)
|
||||
}
|
||||
|
||||
// Print pretty prints FUSE data types for kernel communication
|
||||
func Print(obj interface{}) string {
|
||||
t, ok := obj.(interface {
|
||||
string() string
|
||||
})
|
||||
if ok {
|
||||
return t.string()
|
||||
}
|
||||
return fmt.Sprintf("%T: %v", obj, obj)
|
||||
}
|
||||
50
vendor/github.com/hanwen/go-fuse/fuse/print_darwin.go
generated
vendored
Normal file
50
vendor/github.com/hanwen/go-fuse/fuse/print_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initFlagNames[CAP_XTIMES] = "XTIMES"
|
||||
initFlagNames[CAP_VOL_RENAME] = "VOL_RENAME"
|
||||
initFlagNames[CAP_CASE_INSENSITIVE] = "CASE_INSENSITIVE"
|
||||
}
|
||||
|
||||
func (a *Attr) string() string {
|
||||
return fmt.Sprintf(
|
||||
"{M0%o SZ=%d L=%d "+
|
||||
"%d:%d "+
|
||||
"%d %d:%d "+
|
||||
"A %d.%09d "+
|
||||
"M %d.%09d "+
|
||||
"C %d.%09d}",
|
||||
a.Mode, a.Size, a.Nlink,
|
||||
a.Uid, a.Gid,
|
||||
a.Blocks,
|
||||
a.Rdev, a.Ino, a.Atime, a.Atimensec, a.Mtime, a.Mtimensec,
|
||||
a.Ctime, a.Ctimensec)
|
||||
}
|
||||
|
||||
func (me *CreateIn) string() string {
|
||||
return fmt.Sprintf(
|
||||
"{0%o [%s]}", me.Mode,
|
||||
FlagString(OpenFlagNames, int64(me.Flags), "O_RDONLY"))
|
||||
}
|
||||
|
||||
func (me *GetAttrIn) string() string { return "" }
|
||||
|
||||
func (me *MknodIn) string() string {
|
||||
return fmt.Sprintf("{0%o, %d}", me.Mode, me.Rdev)
|
||||
}
|
||||
|
||||
func (me *ReadIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d off %d sz %d %s}",
|
||||
me.Fh, me.Offset, me.Size,
|
||||
FlagString(readFlagNames, int64(me.ReadFlags), ""))
|
||||
}
|
||||
|
||||
func (me *WriteIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d off %d sz %d %s}",
|
||||
me.Fh, me.Offset, me.Size,
|
||||
FlagString(writeFlagNames, int64(me.WriteFlags), ""))
|
||||
}
|
||||
58
vendor/github.com/hanwen/go-fuse/fuse/print_linux.go
generated
vendored
Normal file
58
vendor/github.com/hanwen/go-fuse/fuse/print_linux.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
OpenFlagNames[syscall.O_DIRECT] = "DIRECT"
|
||||
OpenFlagNames[syscall.O_LARGEFILE] = "LARGEFILE"
|
||||
OpenFlagNames[syscall_O_NOATIME] = "NOATIME"
|
||||
|
||||
}
|
||||
|
||||
func (a *Attr) string() string {
|
||||
return fmt.Sprintf(
|
||||
"{M0%o SZ=%d L=%d "+
|
||||
"%d:%d "+
|
||||
"B%d*%d i%d:%d "+
|
||||
"A %d.%09d "+
|
||||
"M %d.%09d "+
|
||||
"C %d.%09d}",
|
||||
a.Mode, a.Size, a.Nlink,
|
||||
a.Uid, a.Gid,
|
||||
a.Blocks, a.Blksize,
|
||||
a.Rdev, a.Ino, a.Atime, a.Atimensec, a.Mtime, a.Mtimensec,
|
||||
a.Ctime, a.Ctimensec)
|
||||
}
|
||||
|
||||
func (me *CreateIn) string() string {
|
||||
return fmt.Sprintf(
|
||||
"{0%o [%s] (0%o)}", me.Mode,
|
||||
FlagString(OpenFlagNames, int64(me.Flags), "O_RDONLY"), me.Umask)
|
||||
}
|
||||
|
||||
func (me *GetAttrIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d}", me.Fh_)
|
||||
}
|
||||
|
||||
func (me *MknodIn) string() string {
|
||||
return fmt.Sprintf("{0%o (0%o), %d}", me.Mode, me.Umask, me.Rdev)
|
||||
}
|
||||
|
||||
func (me *ReadIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d off %d sz %d %s L %d %s}",
|
||||
me.Fh, me.Offset, me.Size,
|
||||
FlagString(readFlagNames, int64(me.ReadFlags), ""),
|
||||
me.LockOwner,
|
||||
FlagString(OpenFlagNames, int64(me.Flags), "RDONLY"))
|
||||
}
|
||||
|
||||
func (me *WriteIn) string() string {
|
||||
return fmt.Sprintf("{Fh %d off %d sz %d %s L %d %s}",
|
||||
me.Fh, me.Offset, me.Size,
|
||||
FlagString(writeFlagNames, int64(me.WriteFlags), ""),
|
||||
me.LockOwner,
|
||||
FlagString(OpenFlagNames, int64(me.Flags), "RDONLY"))
|
||||
}
|
||||
71
vendor/github.com/hanwen/go-fuse/fuse/read.go
generated
vendored
Normal file
71
vendor/github.com/hanwen/go-fuse/fuse/read.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ReadResultData is the read return for returning bytes directly.
|
||||
type readResultData struct {
|
||||
// Raw bytes for the read.
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (r *readResultData) Size() int {
|
||||
return len(r.Data)
|
||||
}
|
||||
|
||||
func (r *readResultData) Done() {
|
||||
}
|
||||
|
||||
func (r *readResultData) Bytes(buf []byte) ([]byte, Status) {
|
||||
return r.Data, OK
|
||||
}
|
||||
|
||||
func ReadResultData(b []byte) ReadResult {
|
||||
return &readResultData{b}
|
||||
}
|
||||
|
||||
func ReadResultFd(fd uintptr, off int64, sz int) ReadResult {
|
||||
return &readResultFd{fd, off, sz}
|
||||
}
|
||||
|
||||
// ReadResultFd is the read return for zero-copy file data.
|
||||
type readResultFd struct {
|
||||
// Splice from the following file.
|
||||
Fd uintptr
|
||||
|
||||
// Offset within Fd, or -1 to use current offset.
|
||||
Off int64
|
||||
|
||||
// Size of data to be loaded. Actual data available may be
|
||||
// less at the EOF.
|
||||
Sz int
|
||||
}
|
||||
|
||||
// Reads raw bytes from file descriptor if necessary, using the passed
|
||||
// buffer as storage.
|
||||
func (r *readResultFd) Bytes(buf []byte) ([]byte, Status) {
|
||||
sz := r.Sz
|
||||
if len(buf) < sz {
|
||||
sz = len(buf)
|
||||
}
|
||||
|
||||
n, err := syscall.Pread(int(r.Fd), buf[:sz], r.Off)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
|
||||
return buf[:n], ToStatus(err)
|
||||
}
|
||||
|
||||
func (r *readResultFd) Size() int {
|
||||
return r.Sz
|
||||
}
|
||||
|
||||
func (r *readResultFd) Done() {
|
||||
}
|
||||
213
vendor/github.com/hanwen/go-fuse/fuse/request.go
generated
vendored
Normal file
213
vendor/github.com/hanwen/go-fuse/fuse/request.go
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var sizeOfOutHeader = unsafe.Sizeof(OutHeader{})
|
||||
var zeroOutBuf [outputHeaderSize]byte
|
||||
|
||||
type request struct {
|
||||
inputBuf []byte
|
||||
|
||||
// These split up inputBuf.
|
||||
inHeader *InHeader // generic header
|
||||
inData unsafe.Pointer // per op data
|
||||
arg []byte // flat data.
|
||||
|
||||
filenames []string // filename arguments
|
||||
|
||||
// Unstructured data, a pointer to the relevant XxxxOut struct.
|
||||
outData unsafe.Pointer
|
||||
status Status
|
||||
flatData []byte
|
||||
fdData *readResultFd
|
||||
|
||||
// In case of read, keep read result here so we can call
|
||||
// Done() on it.
|
||||
readResult ReadResult
|
||||
|
||||
// Start timestamp for timing info.
|
||||
startTime time.Time
|
||||
|
||||
// All information pertaining to opcode of this request.
|
||||
handler *operationHandler
|
||||
|
||||
// Request storage. For large inputs and outputs, use data
|
||||
// obtained through bufferpool.
|
||||
bufferPoolInputBuf []byte
|
||||
bufferPoolOutputBuf []byte
|
||||
|
||||
// For small pieces of data, we use the following inlines
|
||||
// arrays:
|
||||
//
|
||||
// Output header and structured data.
|
||||
outBuf [outputHeaderSize]byte
|
||||
|
||||
// Input, if small enough to fit here.
|
||||
smallInputBuf [128]byte
|
||||
|
||||
context Context
|
||||
}
|
||||
|
||||
func (r *request) clear() {
|
||||
r.inputBuf = nil
|
||||
r.inHeader = nil
|
||||
r.inData = nil
|
||||
r.arg = nil
|
||||
r.filenames = nil
|
||||
r.outData = nil
|
||||
r.status = OK
|
||||
r.flatData = nil
|
||||
r.fdData = nil
|
||||
r.startTime = time.Time{}
|
||||
r.handler = nil
|
||||
r.readResult = nil
|
||||
}
|
||||
|
||||
func (r *request) InputDebug() string {
|
||||
val := " "
|
||||
if r.handler.DecodeIn != nil {
|
||||
val = fmt.Sprintf(" data: %v ", Print(r.handler.DecodeIn(r.inData)))
|
||||
}
|
||||
|
||||
names := ""
|
||||
if r.filenames != nil {
|
||||
names = fmt.Sprintf("names: %v", r.filenames)
|
||||
}
|
||||
|
||||
if len(r.arg) > 0 {
|
||||
names += fmt.Sprintf(" %d bytes", len(r.arg))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Dispatch %d: %s, NodeId: %v.%v%v",
|
||||
r.inHeader.Unique, operationName(r.inHeader.Opcode),
|
||||
r.inHeader.NodeId, val, names)
|
||||
}
|
||||
|
||||
func (r *request) OutputDebug() string {
|
||||
var dataStr string
|
||||
if r.handler.DecodeOut != nil && r.outData != nil {
|
||||
dataStr = Print(r.handler.DecodeOut(r.outData))
|
||||
}
|
||||
|
||||
max := 1024
|
||||
if len(dataStr) > max {
|
||||
dataStr = dataStr[:max] + fmt.Sprintf(" ...trimmed")
|
||||
}
|
||||
|
||||
flatStr := ""
|
||||
if r.flatDataSize() > 0 {
|
||||
if r.handler.FileNameOut {
|
||||
s := strings.TrimRight(string(r.flatData), "\x00")
|
||||
flatStr = fmt.Sprintf(" %q", s)
|
||||
} else {
|
||||
spl := ""
|
||||
if r.fdData != nil {
|
||||
spl = " (fd data)"
|
||||
}
|
||||
flatStr = fmt.Sprintf(" %d bytes data%s\n", r.flatDataSize(), spl)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Serialize %d: %s code: %v value: %v%v",
|
||||
r.inHeader.Unique, operationName(r.inHeader.Opcode), r.status,
|
||||
dataStr, flatStr)
|
||||
}
|
||||
|
||||
// setInput returns true if it takes ownership of the argument, false if not.
|
||||
func (r *request) setInput(input []byte) bool {
|
||||
if len(input) < len(r.smallInputBuf) {
|
||||
copy(r.smallInputBuf[:], input)
|
||||
r.inputBuf = r.smallInputBuf[:len(input)]
|
||||
return false
|
||||
}
|
||||
r.inputBuf = input
|
||||
r.bufferPoolInputBuf = input[:cap(input)]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *request) parse() {
|
||||
inHSize := int(unsafe.Sizeof(InHeader{}))
|
||||
if len(r.inputBuf) < inHSize {
|
||||
log.Printf("Short read for input header: %v", r.inputBuf)
|
||||
return
|
||||
}
|
||||
|
||||
r.inHeader = (*InHeader)(unsafe.Pointer(&r.inputBuf[0]))
|
||||
r.arg = r.inputBuf[:]
|
||||
|
||||
r.handler = getHandler(r.inHeader.Opcode)
|
||||
if r.handler == nil {
|
||||
log.Printf("Unknown opcode %d", r.inHeader.Opcode)
|
||||
r.status = ENOSYS
|
||||
return
|
||||
}
|
||||
|
||||
if len(r.arg) < int(r.handler.InputSize) {
|
||||
log.Printf("Short read for %v: %v", operationName(r.inHeader.Opcode), r.arg)
|
||||
r.status = EIO
|
||||
return
|
||||
}
|
||||
|
||||
if r.handler.InputSize > 0 {
|
||||
r.inData = unsafe.Pointer(&r.arg[0])
|
||||
r.arg = r.arg[r.handler.InputSize:]
|
||||
} else {
|
||||
r.arg = r.arg[inHSize:]
|
||||
}
|
||||
|
||||
count := r.handler.FileNames
|
||||
if count > 0 {
|
||||
if count == 1 {
|
||||
r.filenames = []string{string(r.arg[:len(r.arg)-1])}
|
||||
} else {
|
||||
names := bytes.SplitN(r.arg[:len(r.arg)-1], []byte{0}, count)
|
||||
r.filenames = make([]string, len(names))
|
||||
for i, n := range names {
|
||||
r.filenames[i] = string(n)
|
||||
}
|
||||
if len(names) != count {
|
||||
log.Println("filename argument mismatch", names, count)
|
||||
r.status = EIO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copy(r.outBuf[:r.handler.OutputSize+sizeOfOutHeader],
|
||||
zeroOutBuf[:r.handler.OutputSize+sizeOfOutHeader])
|
||||
r.outData = unsafe.Pointer(&r.outBuf[sizeOfOutHeader])
|
||||
}
|
||||
|
||||
func (r *request) serializeHeader(dataSize int) (header []byte) {
|
||||
dataLength := r.handler.OutputSize
|
||||
if r.outData == nil || r.status > OK {
|
||||
dataLength = 0
|
||||
}
|
||||
|
||||
sizeOfOutHeader := unsafe.Sizeof(OutHeader{})
|
||||
header = r.outBuf[:sizeOfOutHeader+dataLength]
|
||||
o := (*OutHeader)(unsafe.Pointer(&header[0]))
|
||||
o.Unique = r.inHeader.Unique
|
||||
o.Status = int32(-r.status)
|
||||
o.Length = uint32(
|
||||
int(sizeOfOutHeader) + int(dataLength) + dataSize)
|
||||
|
||||
var asSlice []byte
|
||||
toSlice(&asSlice, r.outData, dataLength)
|
||||
copy(header[sizeOfOutHeader:], asSlice)
|
||||
return header
|
||||
}
|
||||
|
||||
func (r *request) flatDataSize() int {
|
||||
if r.fdData != nil {
|
||||
return r.fdData.Size()
|
||||
}
|
||||
return len(r.flatData)
|
||||
}
|
||||
9
vendor/github.com/hanwen/go-fuse/fuse/request_darwin.go
generated
vendored
Normal file
9
vendor/github.com/hanwen/go-fuse/fuse/request_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package fuse
|
||||
|
||||
const outputHeaderSize = 200
|
||||
|
||||
const (
|
||||
_FUSE_KERNEL_VERSION = 7
|
||||
_MINIMUM_MINOR_VERSION = 8
|
||||
_OUR_MINOR_VERSION = 8
|
||||
)
|
||||
9
vendor/github.com/hanwen/go-fuse/fuse/request_linux.go
generated
vendored
Normal file
9
vendor/github.com/hanwen/go-fuse/fuse/request_linux.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package fuse
|
||||
|
||||
const outputHeaderSize = 160
|
||||
|
||||
const (
|
||||
_FUSE_KERNEL_VERSION = 7
|
||||
_MINIMUM_MINOR_VERSION = 12
|
||||
_OUR_MINOR_VERSION = 23
|
||||
)
|
||||
555
vendor/github.com/hanwen/go-fuse/fuse/server.go
generated
vendored
Normal file
555
vendor/github.com/hanwen/go-fuse/fuse/server.go
generated
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// The kernel caps writes at 128k.
|
||||
MAX_KERNEL_WRITE = 128 * 1024
|
||||
)
|
||||
|
||||
// Server contains the logic for reading from the FUSE device and
|
||||
// translating it to RawFileSystem interface calls.
|
||||
type Server struct {
|
||||
// Empty if unmounted.
|
||||
mountPoint string
|
||||
fileSystem RawFileSystem
|
||||
|
||||
// writeMu serializes close and notify writes
|
||||
writeMu sync.Mutex
|
||||
|
||||
// I/O with kernel and daemon.
|
||||
mountFd int
|
||||
|
||||
latencies LatencyMap
|
||||
|
||||
opts *MountOptions
|
||||
|
||||
// Pool for request structs.
|
||||
reqPool sync.Pool
|
||||
|
||||
// Pool for raw requests data
|
||||
readPool sync.Pool
|
||||
reqMu sync.Mutex
|
||||
reqReaders int
|
||||
kernelSettings InitIn
|
||||
|
||||
singleReader bool
|
||||
canSplice bool
|
||||
loops sync.WaitGroup
|
||||
|
||||
ready chan error
|
||||
}
|
||||
|
||||
// SetDebug is deprecated. Use MountOptions.Debug instead.
|
||||
func (ms *Server) SetDebug(dbg bool) {
|
||||
// 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 {
|
||||
ms.reqMu.Lock()
|
||||
s := ms.kernelSettings
|
||||
ms.reqMu.Unlock()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
const _MAX_NAME_LEN = 20
|
||||
|
||||
// This type may be provided for recording latencies of each FUSE
|
||||
// operation.
|
||||
type LatencyMap interface {
|
||||
Add(name string, dt time.Duration)
|
||||
}
|
||||
|
||||
// RecordLatencies switches on collection of timing for each request
|
||||
// coming from the kernel.P assing a nil argument switches off the
|
||||
func (ms *Server) RecordLatencies(l LatencyMap) {
|
||||
ms.latencies = l
|
||||
}
|
||||
|
||||
// Unmount calls fusermount -u on the mount. This has the effect of
|
||||
// shutting down the filesystem. After the Server is unmounted, it
|
||||
// should be discarded.
|
||||
func (ms *Server) Unmount() (err error) {
|
||||
if ms.mountPoint == "" {
|
||||
return nil
|
||||
}
|
||||
delay := time.Duration(0)
|
||||
for try := 0; try < 5; try++ {
|
||||
err = unmount(ms.mountPoint)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Sleep for a bit. This is not pretty, but there is
|
||||
// no way we can be certain that the kernel thinks all
|
||||
// open files have already been closed.
|
||||
delay = 2*delay + 5*time.Millisecond
|
||||
time.Sleep(delay)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Wait for event loops to exit.
|
||||
ms.loops.Wait()
|
||||
ms.mountPoint = ""
|
||||
return err
|
||||
}
|
||||
|
||||
// NewServer creates a server and attaches it to the given directory.
|
||||
func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server, error) {
|
||||
if opts == nil {
|
||||
opts = &MountOptions{
|
||||
MaxBackground: _DEFAULT_BACKGROUND_TASKS,
|
||||
}
|
||||
}
|
||||
o := *opts
|
||||
if o.SingleThreaded {
|
||||
fs = NewLockingRawFileSystem(fs)
|
||||
}
|
||||
|
||||
if o.Buffers == nil {
|
||||
o.Buffers = defaultBufferPool
|
||||
}
|
||||
if o.MaxWrite < 0 {
|
||||
o.MaxWrite = 0
|
||||
}
|
||||
if o.MaxWrite == 0 {
|
||||
o.MaxWrite = 1 << 16
|
||||
}
|
||||
if o.MaxWrite > MAX_KERNEL_WRITE {
|
||||
o.MaxWrite = MAX_KERNEL_WRITE
|
||||
}
|
||||
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,
|
||||
opts: &o,
|
||||
// OSX has races when multiple routines read from the
|
||||
// FUSE device: on unmount, sometime some reads do not
|
||||
// error-out, meaning that unmount will hang.
|
||||
singleReader: runtime.GOOS == "darwin",
|
||||
}
|
||||
ms.reqPool.New = func() interface{} { return new(request) }
|
||||
ms.readPool.New = func() interface{} { return make([]byte, o.MaxWrite+PAGESIZE) }
|
||||
|
||||
mountPoint = filepath.Clean(mountPoint)
|
||||
if !filepath.IsAbs(mountPoint) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mountPoint = filepath.Clean(filepath.Join(cwd, mountPoint))
|
||||
}
|
||||
ms.ready = make(chan error, 1)
|
||||
fd, err := mount(mountPoint, &o, ms.ready)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 {
|
||||
var r int
|
||||
ms.reqMu.Lock()
|
||||
r = ms.reqReaders
|
||||
ms.reqMu.Unlock()
|
||||
|
||||
return fmt.Sprintf("readers: %d", r)
|
||||
}
|
||||
|
||||
// What is a good number? Maybe the number of CPUs?
|
||||
const _MAX_READERS = 2
|
||||
|
||||
// handleEINTR retries the given function until it doesn't return syscall.EINTR.
|
||||
// This is similar to the HANDLE_EINTR() macro from Chromium ( see
|
||||
// https://code.google.com/p/chromium/codesearch#chromium/src/base/posix/eintr_wrapper.h
|
||||
// ) and the TEMP_FAILURE_RETRY() from glibc (see
|
||||
// https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html
|
||||
// ).
|
||||
//
|
||||
// Don't use handleEINTR() with syscall.Close(); see
|
||||
// https://code.google.com/p/chromium/issues/detail?id=269623 .
|
||||
func handleEINTR(fn func() error) (err error) {
|
||||
for {
|
||||
err = fn()
|
||||
if err != syscall.EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new request, or error. In case exitIdle is given, returns
|
||||
// nil, OK if we have too many readers already.
|
||||
func (ms *Server) readRequest(exitIdle bool) (req *request, code Status) {
|
||||
ms.reqMu.Lock()
|
||||
if ms.reqReaders > _MAX_READERS {
|
||||
ms.reqMu.Unlock()
|
||||
return nil, OK
|
||||
}
|
||||
req = ms.reqPool.Get().(*request)
|
||||
dest := ms.readPool.Get().([]byte)
|
||||
ms.reqReaders++
|
||||
ms.reqMu.Unlock()
|
||||
|
||||
var n int
|
||||
err := handleEINTR(func() error {
|
||||
var err error
|
||||
n, err = syscall.Read(ms.mountFd, dest)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
code = ToStatus(err)
|
||||
ms.reqPool.Put(req)
|
||||
ms.reqMu.Lock()
|
||||
ms.reqReaders--
|
||||
ms.reqMu.Unlock()
|
||||
return nil, code
|
||||
}
|
||||
|
||||
if ms.latencies != nil {
|
||||
req.startTime = time.Now()
|
||||
}
|
||||
gobbled := req.setInput(dest[:n])
|
||||
|
||||
ms.reqMu.Lock()
|
||||
if !gobbled {
|
||||
ms.readPool.Put(dest)
|
||||
dest = nil
|
||||
}
|
||||
ms.reqReaders--
|
||||
if !ms.singleReader && ms.reqReaders <= 0 {
|
||||
ms.loops.Add(1)
|
||||
go ms.loop(true)
|
||||
}
|
||||
ms.reqMu.Unlock()
|
||||
|
||||
return req, OK
|
||||
}
|
||||
|
||||
// returnRequest returns a request to the pool of unused requests.
|
||||
func (ms *Server) returnRequest(req *request) {
|
||||
ms.recordStats(req)
|
||||
|
||||
if req.bufferPoolOutputBuf != nil {
|
||||
ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf)
|
||||
req.bufferPoolOutputBuf = nil
|
||||
}
|
||||
|
||||
req.clear()
|
||||
|
||||
if p := req.bufferPoolInputBuf; p != nil {
|
||||
req.bufferPoolInputBuf = nil
|
||||
ms.readPool.Put(p)
|
||||
}
|
||||
ms.reqPool.Put(req)
|
||||
}
|
||||
|
||||
func (ms *Server) recordStats(req *request) {
|
||||
if ms.latencies != nil {
|
||||
dt := time.Now().Sub(req.startTime)
|
||||
opname := operationName(req.inHeader.Opcode)
|
||||
ms.latencies.Add(opname, dt)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve initiates the FUSE loop. Normally, callers should run Serve()
|
||||
// and wait for it to exit, but tests will want to run this in a
|
||||
// goroutine.
|
||||
//
|
||||
// Each filesystem operation executes in a separate goroutine.
|
||||
func (ms *Server) Serve() {
|
||||
ms.loops.Add(1)
|
||||
ms.loop(false)
|
||||
ms.loops.Wait()
|
||||
|
||||
ms.writeMu.Lock()
|
||||
syscall.Close(ms.mountFd)
|
||||
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:
|
||||
for {
|
||||
req, errNo := ms.readRequest(exitIdle)
|
||||
switch errNo {
|
||||
case OK:
|
||||
if req == nil {
|
||||
break exit
|
||||
}
|
||||
case ENOENT:
|
||||
continue
|
||||
case ENODEV:
|
||||
// unmount
|
||||
break exit
|
||||
default: // some other error?
|
||||
log.Printf("Failed to read from fuse conn: %v", errNo)
|
||||
break exit
|
||||
}
|
||||
|
||||
if ms.singleReader {
|
||||
go ms.handleRequest(req)
|
||||
} else {
|
||||
ms.handleRequest(req)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *Server) handleRequest(req *request) Status {
|
||||
req.parse()
|
||||
if req.handler == nil {
|
||||
req.status = ENOSYS
|
||||
}
|
||||
|
||||
if req.status.Ok() && ms.opts.Debug {
|
||||
log.Println(req.InputDebug())
|
||||
}
|
||||
|
||||
if req.status.Ok() && req.handler.Func == nil {
|
||||
log.Printf("Unimplemented opcode %v", operationName(req.inHeader.Opcode))
|
||||
req.status = ENOSYS
|
||||
}
|
||||
|
||||
if req.status.Ok() {
|
||||
req.handler.Func(ms, req)
|
||||
}
|
||||
|
||||
errNo := ms.write(req)
|
||||
if errNo != 0 {
|
||||
log.Printf("writer: Write/Writev failed, err: %v. opcode: %v",
|
||||
errNo, operationName(req.inHeader.Opcode))
|
||||
}
|
||||
ms.returnRequest(req)
|
||||
return Status(errNo)
|
||||
}
|
||||
|
||||
func (ms *Server) allocOut(req *request, size uint32) []byte {
|
||||
if cap(req.bufferPoolOutputBuf) >= int(size) {
|
||||
req.bufferPoolOutputBuf = req.bufferPoolOutputBuf[:size]
|
||||
return req.bufferPoolOutputBuf
|
||||
}
|
||||
if req.bufferPoolOutputBuf != nil {
|
||||
ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf)
|
||||
}
|
||||
req.bufferPoolOutputBuf = ms.opts.Buffers.AllocBuffer(size)
|
||||
return req.bufferPoolOutputBuf
|
||||
}
|
||||
|
||||
func (ms *Server) write(req *request) Status {
|
||||
// Forget does not wait for reply.
|
||||
if req.inHeader.Opcode == _OP_FORGET || req.inHeader.Opcode == _OP_BATCH_FORGET {
|
||||
return OK
|
||||
}
|
||||
|
||||
header := req.serializeHeader(req.flatDataSize())
|
||||
if ms.opts.Debug {
|
||||
log.Println(req.OutputDebug())
|
||||
}
|
||||
|
||||
if header == nil {
|
||||
return OK
|
||||
}
|
||||
|
||||
s := ms.systemWrite(req, header)
|
||||
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 {
|
||||
entry := &NotifyInvalInodeOut{
|
||||
Ino: node,
|
||||
Off: off,
|
||||
Length: length,
|
||||
}
|
||||
req := request{
|
||||
inHeader: &InHeader{
|
||||
Opcode: _OP_NOTIFY_INODE,
|
||||
},
|
||||
handler: operationHandlers[_OP_NOTIFY_INODE],
|
||||
status: NOTIFY_INVAL_INODE,
|
||||
}
|
||||
req.outData = unsafe.Pointer(entry)
|
||||
|
||||
// Protect against concurrent close.
|
||||
ms.writeMu.Lock()
|
||||
result := ms.write(&req)
|
||||
ms.writeMu.Unlock()
|
||||
|
||||
if ms.opts.Debug {
|
||||
log.Println("Response: INODE_NOTIFY", result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// DeleteNotify notifies the kernel that an entry is removed from a
|
||||
// directory. In many cases, this is equivalent to EntryNotify,
|
||||
// except when the directory is in use, eg. as working directory of
|
||||
// some process. You should not hold any FUSE filesystem locks, as that
|
||||
// can lead to deadlock.
|
||||
func (ms *Server) DeleteNotify(parent uint64, child uint64, name string) Status {
|
||||
if ms.kernelSettings.Minor < 18 {
|
||||
return ms.EntryNotify(parent, name)
|
||||
}
|
||||
|
||||
req := request{
|
||||
inHeader: &InHeader{
|
||||
Opcode: _OP_NOTIFY_DELETE,
|
||||
},
|
||||
handler: operationHandlers[_OP_NOTIFY_DELETE],
|
||||
status: NOTIFY_INVAL_DELETE,
|
||||
}
|
||||
entry := &NotifyInvalDeleteOut{
|
||||
Parent: parent,
|
||||
Child: child,
|
||||
NameLen: uint32(len(name)),
|
||||
}
|
||||
|
||||
// Many versions of FUSE generate stacktraces if the
|
||||
// terminating null byte is missing.
|
||||
nameBytes := make([]byte, len(name)+1)
|
||||
copy(nameBytes, name)
|
||||
nameBytes[len(nameBytes)-1] = '\000'
|
||||
req.outData = unsafe.Pointer(entry)
|
||||
req.flatData = nameBytes
|
||||
|
||||
// Protect against concurrent close.
|
||||
ms.writeMu.Lock()
|
||||
result := ms.write(&req)
|
||||
ms.writeMu.Unlock()
|
||||
|
||||
if ms.opts.Debug {
|
||||
log.Printf("Response: DELETE_NOTIFY: %v", result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// EntryNotify should be used if the existence status of an entry
|
||||
// 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 {
|
||||
req := request{
|
||||
inHeader: &InHeader{
|
||||
Opcode: _OP_NOTIFY_ENTRY,
|
||||
},
|
||||
handler: operationHandlers[_OP_NOTIFY_ENTRY],
|
||||
status: NOTIFY_INVAL_ENTRY,
|
||||
}
|
||||
entry := &NotifyInvalEntryOut{
|
||||
Parent: parent,
|
||||
NameLen: uint32(len(name)),
|
||||
}
|
||||
|
||||
// Many versions of FUSE generate stacktraces if the
|
||||
// terminating null byte is missing.
|
||||
nameBytes := make([]byte, len(name)+1)
|
||||
copy(nameBytes, name)
|
||||
nameBytes[len(nameBytes)-1] = '\000'
|
||||
req.outData = unsafe.Pointer(entry)
|
||||
req.flatData = nameBytes
|
||||
|
||||
// Protect against concurrent close.
|
||||
ms.writeMu.Lock()
|
||||
result := ms.write(&req)
|
||||
ms.writeMu.Unlock()
|
||||
|
||||
if ms.opts.Debug {
|
||||
log.Printf("Response: ENTRY_NOTIFY: %v", result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var defaultBufferPool BufferPool
|
||||
|
||||
func init() {
|
||||
defaultBufferPool = NewBufferPool()
|
||||
}
|
||||
|
||||
// WaitMount waits for the first request to be served. Use this to
|
||||
// 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
|
||||
}
|
||||
28
vendor/github.com/hanwen/go-fuse/fuse/server_darwin.go
generated
vendored
Normal file
28
vendor/github.com/hanwen/go-fuse/fuse/server_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (ms *Server) systemWrite(req *request, header []byte) Status {
|
||||
if req.flatDataSize() == 0 {
|
||||
err := handleEINTR(func() error {
|
||||
_, err := syscall.Write(ms.mountFd, header)
|
||||
return err
|
||||
})
|
||||
return ToStatus(err)
|
||||
}
|
||||
|
||||
if req.fdData != nil {
|
||||
sz := req.flatDataSize()
|
||||
buf := ms.allocOut(req, uint32(sz))
|
||||
req.flatData, req.status = req.fdData.Bytes(buf)
|
||||
header = req.serializeHeader(len(req.flatData))
|
||||
}
|
||||
|
||||
_, err := writev(int(ms.mountFd), [][]byte{header, req.flatData})
|
||||
if req.readResult != nil {
|
||||
req.readResult.Done()
|
||||
}
|
||||
return ToStatus(err)
|
||||
}
|
||||
38
vendor/github.com/hanwen/go-fuse/fuse/server_linux.go
generated
vendored
Normal file
38
vendor/github.com/hanwen/go-fuse/fuse/server_linux.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"log"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (ms *Server) systemWrite(req *request, header []byte) Status {
|
||||
if req.flatDataSize() == 0 {
|
||||
err := handleEINTR(func() error {
|
||||
_, err := syscall.Write(ms.mountFd, header)
|
||||
return err
|
||||
})
|
||||
return ToStatus(err)
|
||||
}
|
||||
|
||||
if req.fdData != nil {
|
||||
if ms.canSplice {
|
||||
err := ms.trySplice(header, req, req.fdData)
|
||||
if err == nil {
|
||||
req.readResult.Done()
|
||||
return OK
|
||||
}
|
||||
log.Println("trySplice:", err)
|
||||
}
|
||||
|
||||
sz := req.flatDataSize()
|
||||
buf := ms.allocOut(req, uint32(sz))
|
||||
req.flatData, req.status = req.fdData.Bytes(buf)
|
||||
header = req.serializeHeader(len(req.flatData))
|
||||
}
|
||||
|
||||
_, err := writev(ms.mountFd, [][]byte{header, req.flatData})
|
||||
if req.readResult != nil {
|
||||
req.readResult.Done()
|
||||
}
|
||||
return ToStatus(err)
|
||||
}
|
||||
13
vendor/github.com/hanwen/go-fuse/fuse/splice_darwin.go
generated
vendored
Normal file
13
vendor/github.com/hanwen/go-fuse/fuse/splice_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (s *Server) setSplice() {
|
||||
panic("darwin has no splice.")
|
||||
}
|
||||
|
||||
func (ms *Server) trySplice(header []byte, req *request, fdData *readResultFd) error {
|
||||
return fmt.Errorf("unimplemented")
|
||||
}
|
||||
93
vendor/github.com/hanwen/go-fuse/fuse/splice_linux.go
generated
vendored
Normal file
93
vendor/github.com/hanwen/go-fuse/fuse/splice_linux.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hanwen/go-fuse/splice"
|
||||
)
|
||||
|
||||
func (s *Server) setSplice() {
|
||||
s.canSplice = splice.Resizable()
|
||||
}
|
||||
|
||||
// trySplice: Zero-copy read from fdData.Fd into /dev/fuse
|
||||
//
|
||||
// This is a four-step process:
|
||||
//
|
||||
// 1) Splice data form fdData.Fd into the "pair1" pipe buffer --> pair1: [payload]
|
||||
// Now we know the actual payload length and can
|
||||
// construct the reply header
|
||||
// 2) Write header into the "pair2" pipe buffer --> pair2: [header]
|
||||
// 4) Splice data from "pair1" into "pair2" --> pair2: [header][payload]
|
||||
// 3) Splice the data from "pair2" into /dev/fuse
|
||||
//
|
||||
// This dance is neccessary because header and payload cannot be split across
|
||||
// two splices and we cannot seek in a pipe buffer.
|
||||
func (ms *Server) trySplice(header []byte, req *request, fdData *readResultFd) error {
|
||||
var err error
|
||||
|
||||
// Get a pair of connected pipes
|
||||
pair1, err := splice.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer splice.Done(pair1)
|
||||
|
||||
// Grow buffer pipe to requested size + one extra page
|
||||
// Without the extra page the kernel will block once the pipe is almost full
|
||||
pair1Sz := fdData.Size() + os.Getpagesize()
|
||||
if err := pair1.Grow(pair1Sz); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read data from file
|
||||
payloadLen, err := pair1.LoadFromAt(fdData.Fd, fdData.Size(), fdData.Off)
|
||||
|
||||
if err != nil {
|
||||
// TODO - extract the data from splice.
|
||||
return err
|
||||
}
|
||||
|
||||
// Get another pair of connected pipes
|
||||
pair2, err := splice.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer splice.Done(pair2)
|
||||
|
||||
// Grow pipe to header + actually read size + one extra page
|
||||
// Without the extra page the kernel will block once the pipe is almost full
|
||||
header = req.serializeHeader(payloadLen)
|
||||
total := len(header) + payloadLen
|
||||
pair2Sz := total + os.Getpagesize()
|
||||
if err := pair2.Grow(pair2Sz); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write header into pair2
|
||||
n, err := pair2.Write(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(header) {
|
||||
return fmt.Errorf("Short write into splice: wrote %d, want %d", n, len(header))
|
||||
}
|
||||
|
||||
// Write data into pair2
|
||||
n, err = pair2.LoadFrom(pair1.ReadFd(), payloadLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != payloadLen {
|
||||
return fmt.Errorf("Short splice: wrote %d, want %d", n, payloadLen)
|
||||
}
|
||||
|
||||
// Write header + data to /dev/fuse
|
||||
_, err = pair2.WriteTo(uintptr(ms.mountFd), total)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
135
vendor/github.com/hanwen/go-fuse/fuse/syscall_darwin.go
generated
vendored
Normal file
135
vendor/github.com/hanwen/go-fuse/fuse/syscall_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// TODO - move these into Go's syscall package.
|
||||
|
||||
func sys_writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, err error) {
|
||||
n1, _, e1 := syscall.Syscall(
|
||||
syscall.SYS_WRITEV,
|
||||
uintptr(fd), uintptr(unsafe.Pointer(iovecs)), uintptr(cnt))
|
||||
n = int(n1)
|
||||
if e1 != 0 {
|
||||
err = syscall.Errno(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writev(fd int, packet [][]byte) (n int, err error) {
|
||||
iovecs := make([]syscall.Iovec, 0, len(packet))
|
||||
|
||||
for _, v := range packet {
|
||||
if len(v) == 0 {
|
||||
continue
|
||||
}
|
||||
vec := syscall.Iovec{
|
||||
Base: &v[0],
|
||||
}
|
||||
vec.SetLen(len(v))
|
||||
iovecs = append(iovecs, vec)
|
||||
}
|
||||
|
||||
sysErr := handleEINTR(func() error {
|
||||
var err error
|
||||
n, err = sys_writev(fd, &iovecs[0], len(iovecs))
|
||||
return err
|
||||
})
|
||||
if sysErr != nil {
|
||||
err = os.NewSyscallError("writev", sysErr)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func getxattr(path string, attr string, dest []byte) (sz int, errno int) {
|
||||
pathBs := syscall.StringBytePtr(path)
|
||||
attrBs := syscall.StringBytePtr(attr)
|
||||
size, _, errNo := syscall.Syscall6(
|
||||
syscall.SYS_GETXATTR,
|
||||
uintptr(unsafe.Pointer(pathBs)),
|
||||
uintptr(unsafe.Pointer(attrBs)),
|
||||
uintptr(unsafe.Pointer(&dest[0])),
|
||||
uintptr(len(dest)),
|
||||
0, 0)
|
||||
return int(size), int(errNo)
|
||||
}
|
||||
|
||||
func GetXAttr(path string, attr string, dest []byte) (value []byte, errno int) {
|
||||
sz, errno := getxattr(path, attr, dest)
|
||||
|
||||
for sz > cap(dest) && errno == 0 {
|
||||
dest = make([]byte, sz)
|
||||
sz, errno = getxattr(path, attr, dest)
|
||||
}
|
||||
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
return dest[:sz], errno
|
||||
}
|
||||
|
||||
func listxattr(path string, dest []byte) (sz int, errno int) {
|
||||
pathbs := syscall.StringBytePtr(path)
|
||||
var destPointer unsafe.Pointer
|
||||
if len(dest) > 0 {
|
||||
destPointer = unsafe.Pointer(&dest[0])
|
||||
}
|
||||
size, _, errNo := syscall.Syscall(
|
||||
syscall.SYS_LISTXATTR,
|
||||
uintptr(unsafe.Pointer(pathbs)),
|
||||
uintptr(destPointer),
|
||||
uintptr(len(dest)))
|
||||
|
||||
return int(size), int(errNo)
|
||||
}
|
||||
|
||||
func ListXAttr(path string) (attributes []string, errno int) {
|
||||
dest := make([]byte, 0)
|
||||
sz, errno := listxattr(path, dest)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
for sz > cap(dest) && errno == 0 {
|
||||
dest = make([]byte, sz)
|
||||
sz, errno = listxattr(path, dest)
|
||||
}
|
||||
|
||||
// -1 to drop the final empty slice.
|
||||
dest = dest[:sz-1]
|
||||
attributesBytes := bytes.Split(dest, []byte{0})
|
||||
attributes = make([]string, len(attributesBytes))
|
||||
for i, v := range attributesBytes {
|
||||
attributes[i] = string(v)
|
||||
}
|
||||
return attributes, errno
|
||||
}
|
||||
|
||||
func Setxattr(path string, attr string, data []byte, flags int) (errno int) {
|
||||
pathbs := syscall.StringBytePtr(path)
|
||||
attrbs := syscall.StringBytePtr(attr)
|
||||
_, _, errNo := syscall.Syscall6(
|
||||
syscall.SYS_SETXATTR,
|
||||
uintptr(unsafe.Pointer(pathbs)),
|
||||
uintptr(unsafe.Pointer(attrbs)),
|
||||
uintptr(unsafe.Pointer(&data[0])),
|
||||
uintptr(len(data)),
|
||||
uintptr(flags), 0)
|
||||
|
||||
return int(errNo)
|
||||
}
|
||||
|
||||
func Removexattr(path string, attr string) (errno int) {
|
||||
pathbs := syscall.StringBytePtr(path)
|
||||
attrbs := syscall.StringBytePtr(attr)
|
||||
_, _, errNo := syscall.Syscall(
|
||||
syscall.SYS_REMOVEXATTR,
|
||||
uintptr(unsafe.Pointer(pathbs)),
|
||||
uintptr(unsafe.Pointer(attrbs)), 0)
|
||||
return int(errNo)
|
||||
}
|
||||
45
vendor/github.com/hanwen/go-fuse/fuse/syscall_linux.go
generated
vendored
Normal file
45
vendor/github.com/hanwen/go-fuse/fuse/syscall_linux.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// TODO - move these into Go's syscall package.
|
||||
|
||||
func sys_writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, err error) {
|
||||
n1, _, e1 := syscall.Syscall(
|
||||
syscall.SYS_WRITEV,
|
||||
uintptr(fd), uintptr(unsafe.Pointer(iovecs)), uintptr(cnt))
|
||||
n = int(n1)
|
||||
if e1 != 0 {
|
||||
err = syscall.Errno(e1)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func writev(fd int, packet [][]byte) (n int, err error) {
|
||||
iovecs := make([]syscall.Iovec, 0, len(packet))
|
||||
|
||||
for _, v := range packet {
|
||||
if len(v) == 0 {
|
||||
continue
|
||||
}
|
||||
vec := syscall.Iovec{
|
||||
Base: &v[0],
|
||||
}
|
||||
vec.SetLen(len(v))
|
||||
iovecs = append(iovecs, vec)
|
||||
}
|
||||
|
||||
sysErr := handleEINTR(func() error {
|
||||
var err error
|
||||
n, err = sys_writev(fd, &iovecs[0], len(iovecs))
|
||||
return err
|
||||
})
|
||||
if sysErr != nil {
|
||||
err = os.NewSyscallError("writev", sysErr)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
220
vendor/github.com/hanwen/go-fuse/fuse/test/cache_test.go
generated
vendored
Normal file
220
vendor/github.com/hanwen/go-fuse/fuse/test/cache_test.go
generated
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
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"
|
||||
)
|
||||
|
||||
type cacheFs struct {
|
||||
pathfs.FileSystem
|
||||
}
|
||||
|
||||
func (fs *cacheFs) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
||||
f, c := fs.FileSystem.Open(name, flags, context)
|
||||
if !c.Ok() {
|
||||
return f, c
|
||||
}
|
||||
return &nodefs.WithFlags{
|
||||
File: f,
|
||||
FuseFlags: fuse.FOPEN_KEEP_CACHE,
|
||||
}, c
|
||||
|
||||
}
|
||||
|
||||
func setupCacheTest(t *testing.T) (string, *pathfs.PathNodeFs, func()) {
|
||||
dir, err := ioutil.TempDir("", "go-fuse-cachetest")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
os.Mkdir(dir+"/mnt", 0755)
|
||||
os.Mkdir(dir+"/orig", 0755)
|
||||
|
||||
fs := &cacheFs{
|
||||
pathfs.NewLoopbackFileSystem(dir + "/orig"),
|
||||
}
|
||||
pfs := pathfs.NewPathNodeFs(fs, &pathfs.PathNodeFsOptions{Debug: VerboseTest()})
|
||||
|
||||
opts := nodefs.NewOptions()
|
||||
opts.Debug = VerboseTest()
|
||||
state, _, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("MountNodeFileSystem failed: %v", err)
|
||||
}
|
||||
go state.Serve()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
return dir, pfs, func() {
|
||||
err := state.Unmount()
|
||||
if err == nil {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFopenKeepCache(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
t.Skip("FOPEN_KEEP_CACHE is broken on Darwin.")
|
||||
}
|
||||
|
||||
wd, pathfs, clean := setupCacheTest(t)
|
||||
defer clean()
|
||||
|
||||
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: %v", err)
|
||||
} else if string(c) != before {
|
||||
t.Fatalf("ReadFile: got %q, want %q", c, before)
|
||||
}
|
||||
|
||||
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: %v", err)
|
||||
} else if string(c) != before {
|
||||
t.Fatalf("ReadFile: got %q, want cached %q", c, before)
|
||||
}
|
||||
|
||||
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("EntryNotify: %v", code)
|
||||
}
|
||||
|
||||
c, err = ioutil.ReadFile(wd + "/mnt/file.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
} else if string(c) != after {
|
||||
t.Fatalf("ReadFile: got %q after notify, want %q", c, after)
|
||||
}
|
||||
}
|
||||
|
||||
type nonseekFs struct {
|
||||
pathfs.FileSystem
|
||||
Length int
|
||||
}
|
||||
|
||||
func (fs *nonseekFs) GetAttr(name string, context *fuse.Context) (fi *fuse.Attr, status fuse.Status) {
|
||||
if name == "file" {
|
||||
return &fuse.Attr{Mode: fuse.S_IFREG | 0644}, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (fs *nonseekFs) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
||||
if name != "file" {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
data := bytes.Repeat([]byte{42}, fs.Length)
|
||||
f := nodefs.NewDataFile(data)
|
||||
return &nodefs.WithFlags{
|
||||
File: f,
|
||||
FuseFlags: fuse.FOPEN_NONSEEKABLE,
|
||||
}, fuse.OK
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
nfs := pathfs.NewPathNodeFs(fs, nil)
|
||||
opts := nodefs.NewOptions()
|
||||
opts.Debug = VerboseTest()
|
||||
state, _, err := nodefs.MountRoot(dir, nfs.Root(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
defer state.Unmount()
|
||||
|
||||
go state.Serve()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
|
||||
f, err := os.Open(dir + "/file")
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
b := make([]byte, 200)
|
||||
n, err := f.ReadAt(b, 20)
|
||||
if err == nil || n > 0 {
|
||||
t.Errorf("file was opened nonseekable, but seek successful")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAttrRace(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "go-fuse-cache_test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
os.Mkdir(dir+"/mnt", 0755)
|
||||
os.Mkdir(dir+"/orig", 0755)
|
||||
|
||||
fs := pathfs.NewLoopbackFileSystem(dir + "/orig")
|
||||
pfs := pathfs.NewPathNodeFs(fs, &pathfs.PathNodeFsOptions{Debug: VerboseTest()})
|
||||
state, _, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(),
|
||||
&nodefs.Options{Debug: VerboseTest()})
|
||||
if err != nil {
|
||||
t.Fatalf("MountNodeFileSystem failed: %v", err)
|
||||
}
|
||||
go state.Serve()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
|
||||
defer state.Unmount()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
n := 100
|
||||
wg.Add(n)
|
||||
var statErr error
|
||||
for i := 0; i < n; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
fn := dir + "/mnt/file"
|
||||
err := ioutil.WriteFile(fn, []byte{42}, 0644)
|
||||
if err != nil {
|
||||
statErr = err
|
||||
return
|
||||
}
|
||||
_, err = os.Lstat(fn)
|
||||
if err != nil {
|
||||
statErr = err
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
if statErr != nil {
|
||||
t.Error(statErr)
|
||||
}
|
||||
}
|
||||
70
vendor/github.com/hanwen/go-fuse/fuse/test/defaultread_test.go
generated
vendored
Normal file
70
vendor/github.com/hanwen/go-fuse/fuse/test/defaultread_test.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||
)
|
||||
|
||||
type DefaultReadFS struct {
|
||||
pathfs.FileSystem
|
||||
size uint64
|
||||
exist bool
|
||||
}
|
||||
|
||||
func (fs *DefaultReadFS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
if name == "" {
|
||||
return &fuse.Attr{Mode: fuse.S_IFDIR | 0755}, fuse.OK
|
||||
}
|
||||
if name == "file" {
|
||||
return &fuse.Attr{Mode: fuse.S_IFREG | 0644, Size: fs.size}, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (fs *DefaultReadFS) Open(name string, f uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
|
||||
return nodefs.NewDefaultFile(), fuse.OK
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
pathfs := pathfs.NewPathNodeFs(fs, nil)
|
||||
opts := nodefs.NewOptions()
|
||||
opts.Debug = VerboseTest()
|
||||
|
||||
state, _, err := nodefs.MountRoot(dir, pathfs.Root(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("MountNodeFileSystem failed: %v", err)
|
||||
}
|
||||
go state.Serve()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
return dir, func() {
|
||||
state.Unmount()
|
||||
os.Remove(dir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultRead(t *testing.T) {
|
||||
root, clean := defaultReadTest(t)
|
||||
defer clean()
|
||||
|
||||
_, err := ioutil.ReadFile(root + "/file")
|
||||
if err == nil {
|
||||
t.Fatal("should have failed read.")
|
||||
}
|
||||
}
|
||||
117
vendor/github.com/hanwen/go-fuse/fuse/test/delete_linux_test.go
generated
vendored
Normal file
117
vendor/github.com/hanwen/go-fuse/fuse/test/delete_linux_test.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
)
|
||||
|
||||
type flipNode struct {
|
||||
nodefs.Node
|
||||
ok chan int
|
||||
}
|
||||
|
||||
func (f *flipNode) GetAttr(out *fuse.Attr, file nodefs.File, c *fuse.Context) fuse.Status {
|
||||
select {
|
||||
case <-f.ok:
|
||||
// use a status that is easily recognizable.
|
||||
return fuse.Status(syscall.EXDEV)
|
||||
default:
|
||||
}
|
||||
return f.Node.GetAttr(out, file, c)
|
||||
}
|
||||
|
||||
func TestDeleteNotify(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "go-fuse-delete_test")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
root := nodefs.NewMemNodeFSRoot(dir + "/backing")
|
||||
conn := nodefs.NewFileSystemConnector(root,
|
||||
&nodefs.Options{PortableInodes: true})
|
||||
mnt := dir + "/mnt"
|
||||
err = os.Mkdir(mnt, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err := fuse.NewServer(conn.RawFS(), mnt, &fuse.MountOptions{
|
||||
Debug: VerboseTest(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go state.Serve()
|
||||
defer state.Unmount()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
|
||||
_, code := root.Mkdir("testdir", 0755, nil)
|
||||
if !code.Ok() {
|
||||
t.Fatal(code)
|
||||
}
|
||||
|
||||
ch := root.Inode().RmChild("testdir")
|
||||
ch.Node().SetInode(nil)
|
||||
flip := flipNode{
|
||||
Node: ch.Node(),
|
||||
ok: make(chan int),
|
||||
}
|
||||
root.Inode().NewChild("testdir", true, &flip)
|
||||
|
||||
err = ioutil.WriteFile(mnt+"/testdir/testfile", []byte{42}, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Do the test here, so we surely have state.KernelSettings()
|
||||
if state.KernelSettings().Minor < 18 {
|
||||
t.Log("Kernel does not support deletion notify; aborting test.")
|
||||
return
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
cmd := exec.Command("/usr/bin/tail", "-f", "testfile")
|
||||
cmd.Dir = mnt + "/testdir"
|
||||
cmd.Stdin = &buf
|
||||
cmd.Stdout = &bytes.Buffer{}
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
cmd.Process.Kill()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}()
|
||||
|
||||
// Wait until tail opened the file.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
err = os.Remove(mnt + "/testdir/testfile")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Simulate deletion+mkdir coming from the network
|
||||
close(flip.ok)
|
||||
oldCh := root.Inode().RmChild("testdir")
|
||||
_, code = root.Inode().Node().Mkdir("testdir", 0755, nil)
|
||||
if !code.Ok() {
|
||||
t.Fatal("mkdir status", code)
|
||||
}
|
||||
conn.DeleteNotify(root.Inode(), oldCh, "testdir")
|
||||
|
||||
_, err = os.Lstat(mnt + "/testdir")
|
||||
if err != nil {
|
||||
t.Fatalf("lstat after del + mkdir failed: %v", err)
|
||||
}
|
||||
}
|
||||
257
vendor/github.com/hanwen/go-fuse/fuse/test/fsetattr_test.go
generated
vendored
Normal file
257
vendor/github.com/hanwen/go-fuse/fuse/test/fsetattr_test.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
package test
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
type MutableDataFile struct {
|
||||
nodefs.File
|
||||
|
||||
data []byte
|
||||
fuse.Attr
|
||||
GetAttrCalled bool
|
||||
FsyncCalled bool
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) String() string {
|
||||
return "MutableDataFile"
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Read(buf []byte, off int64) (fuse.ReadResult, fuse.Status) {
|
||||
end := int(off) + len(buf)
|
||||
if end > len(f.data) {
|
||||
end = len(f.data)
|
||||
}
|
||||
|
||||
return fuse.ReadResultData(f.data[off:end]), fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Write(d []byte, off int64) (uint32, fuse.Status) {
|
||||
end := int64(len(d)) + off
|
||||
if int(end) > len(f.data) {
|
||||
data := make([]byte, len(f.data), end)
|
||||
copy(data, f.data)
|
||||
f.data = data[:end]
|
||||
}
|
||||
copy(f.data[off:end], d)
|
||||
f.Attr.Size = uint64(len(f.data))
|
||||
|
||||
return uint32(end - off), fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Flush() fuse.Status {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Release() {
|
||||
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) getAttr(out *fuse.Attr) {
|
||||
*out = f.Attr
|
||||
out.Size = uint64(len(f.data))
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) GetAttr(out *fuse.Attr) fuse.Status {
|
||||
f.GetAttrCalled = true
|
||||
f.getAttr(out)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Utimens(atime *time.Time, mtime *time.Time) fuse.Status {
|
||||
f.Attr.SetTimes(atime, mtime, nil)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Truncate(size uint64) fuse.Status {
|
||||
f.data = f.data[:size]
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Chown(uid uint32, gid uint32) fuse.Status {
|
||||
f.Attr.Uid = uid
|
||||
f.Attr.Gid = gid
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Chmod(perms uint32) fuse.Status {
|
||||
f.Attr.Mode = (f.Attr.Mode &^ 07777) | perms
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (f *MutableDataFile) Fsync(flags int) fuse.Status {
|
||||
f.FsyncCalled = true
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
||||
// This FS only supports a single r/w file called "/file".
|
||||
type FSetAttrFs struct {
|
||||
pathfs.FileSystem
|
||||
file *MutableDataFile
|
||||
}
|
||||
|
||||
func (fs *FSetAttrFs) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||
return nil, fuse.ENODATA
|
||||
}
|
||||
|
||||
func (fs *FSetAttrFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||
if name == "" {
|
||||
return &fuse.Attr{Mode: fuse.S_IFDIR | 0700}, fuse.OK
|
||||
}
|
||||
if name == "file" && fs.file != nil {
|
||||
var a fuse.Attr
|
||||
fs.file.getAttr(&a)
|
||||
a.Mode |= fuse.S_IFREG
|
||||
return &a, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (fs *FSetAttrFs) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
|
||||
if name == "file" {
|
||||
return fs.file, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (fs *FSetAttrFs) Create(name string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
|
||||
if name == "file" {
|
||||
f := NewFile()
|
||||
fs.file = f
|
||||
fs.file.Attr.Mode = mode
|
||||
return f, fuse.OK
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func NewFile() *MutableDataFile {
|
||||
return &MutableDataFile{File: nodefs.NewDefaultFile()}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
nfs := pathfs.NewPathNodeFs(fs, nil)
|
||||
opts := nodefs.NewOptions()
|
||||
opts.Debug = VerboseTest()
|
||||
|
||||
state, _, err := nodefs.MountRoot(dir, nfs.Root(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("MountNodeFileSystem failed: %v", err)
|
||||
}
|
||||
|
||||
go state.Serve()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
|
||||
if state.KernelSettings().Flags&fuse.CAP_FILE_OPS == 0 {
|
||||
t.Skip("Mount does not support file operations")
|
||||
}
|
||||
|
||||
return dir, func() {
|
||||
if err := state.Unmount(); err != nil {
|
||||
t.Errorf("cleanup: Unmount: %v", err)
|
||||
} else {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSetAttr(t *testing.T) {
|
||||
fSetAttrFs := &FSetAttrFs{
|
||||
FileSystem: pathfs.NewDefaultFileSystem(),
|
||||
}
|
||||
fs := pathfs.NewLockingFileSystem(fSetAttrFs)
|
||||
dir, clean := setupFAttrTest(t, fs)
|
||||
defer func() {
|
||||
if clean != nil {
|
||||
clean()
|
||||
}
|
||||
}()
|
||||
|
||||
fn := dir + "/file"
|
||||
f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("Stat failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = f.WriteString("hello")
|
||||
if err != nil {
|
||||
t.Fatalf("WriteString failed: %v", err)
|
||||
}
|
||||
|
||||
code := syscall.Ftruncate(int(f.Fd()), 3)
|
||||
if code != nil {
|
||||
t.Error("truncate retval", os.NewSyscallError("Ftruncate", code))
|
||||
}
|
||||
|
||||
if a, status := fs.GetAttr("file", nil); !status.Ok() {
|
||||
t.Fatalf("GetAttr: status %v", status)
|
||||
} else if a.Size != 3 {
|
||||
t.Errorf("truncate: size %d, status %v", a.Size, status)
|
||||
}
|
||||
|
||||
if err := f.Chmod(024); err != nil {
|
||||
t.Fatalf("Chmod failed: %v", err)
|
||||
}
|
||||
|
||||
if a, status := fs.GetAttr("file", nil); !status.Ok() {
|
||||
t.Errorf("chmod: %v", status)
|
||||
} else if a.Mode&07777 != 024 {
|
||||
t.Errorf("getattr after chmod: %o", a.Mode&0777)
|
||||
}
|
||||
|
||||
if err := os.Chtimes(fn, time.Unix(0, 100e3), time.Unix(0, 101e3)); err != nil {
|
||||
t.Fatalf("Chtimes failed: %v", err)
|
||||
}
|
||||
|
||||
if a, status := fs.GetAttr("file", nil); !status.Ok() {
|
||||
t.Errorf("GetAttr: %v", status)
|
||||
} else if a.Atimensec != 100e3 || a.Mtimensec != 101e3 {
|
||||
t.Errorf("Utimens: atime %d != 100e3 mtime %d != 101e3",
|
||||
a.Atimensec, a.Mtimensec)
|
||||
}
|
||||
|
||||
newFi, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("Stat failed: %v", err)
|
||||
}
|
||||
i1 := fuse.ToStatT(fi).Ino
|
||||
i2 := fuse.ToStatT(newFi).Ino
|
||||
if i1 != i2 {
|
||||
t.Errorf("f.Lstat().Ino = %d. Returned %d before.", i2, i1)
|
||||
}
|
||||
|
||||
if code := syscall.Fsync(int(f.Fd())); code != nil {
|
||||
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")
|
||||
}
|
||||
}
|
||||
14
vendor/github.com/hanwen/go-fuse/fuse/test/loopback_darwin_test.go
generated
vendored
Normal file
14
vendor/github.com/hanwen/go-fuse/fuse/test/loopback_darwin_test.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
120
vendor/github.com/hanwen/go-fuse/fuse/test/loopback_linux_test.go
generated
vendored
Normal file
120
vendor/github.com/hanwen/go-fuse/fuse/test/loopback_linux_test.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTouch(t *testing.T) {
|
||||
ts := NewTestCase(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
contents := []byte{1, 2, 3}
|
||||
err := ioutil.WriteFile(ts.origFile, []byte(contents), 0700)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteFile failed: %v", err)
|
||||
}
|
||||
err = os.Chtimes(ts.mountFile, time.Unix(42, 0), time.Unix(43, 0))
|
||||
if err != nil {
|
||||
t.Fatalf("Chtimes failed: %v", err)
|
||||
}
|
||||
|
||||
var stat syscall.Stat_t
|
||||
err = syscall.Lstat(ts.mountFile, &stat)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
if stat.Atim.Sec != 42 {
|
||||
t.Errorf("Got atime.sec %d, want 42. Stat_t was %#v", stat.Atim.Sec, stat)
|
||||
}
|
||||
if stat.Mtim.Sec != 43 {
|
||||
t.Errorf("Got mtime.sec %d, want 43. Stat_t was %#v", stat.Mtim.Sec, stat)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNegativeTime(t *testing.T) {
|
||||
ts := NewTestCase(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
_, err := os.Create(ts.origFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
|
||||
var stat syscall.Stat_t
|
||||
|
||||
// set negative nanosecond will occur errors on UtimesNano as invalid argument
|
||||
ut := time.Date(1960, time.January, 10, 23, 0, 0, 0, time.UTC)
|
||||
tim := []syscall.Timespec{
|
||||
syscall.NsecToTimespec(ut.UnixNano()),
|
||||
syscall.NsecToTimespec(ut.UnixNano()),
|
||||
}
|
||||
err = syscall.UtimesNano(ts.mountFile, tim)
|
||||
if err != nil {
|
||||
t.Fatalf("UtimesNano failed: %v", err)
|
||||
}
|
||||
err = syscall.Lstat(ts.mountFile, &stat)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
if stat.Atim.Sec >= 0 || stat.Mtim.Sec >= 0 {
|
||||
t.Errorf("Got wrong timestamps %v", stat)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func TestFallocate(t *testing.T) {
|
||||
ts := NewTestCase(t)
|
||||
defer ts.Cleanup()
|
||||
if ts.state.KernelSettings().Minor < 19 {
|
||||
t.Log("FUSE does not support Fallocate.")
|
||||
return
|
||||
}
|
||||
|
||||
rwFile, err := os.OpenFile(ts.mnt+"/file", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer rwFile.Close()
|
||||
err = syscall.Fallocate(int(rwFile.Fd()), 0, 1024, 4096)
|
||||
if err != nil {
|
||||
t.Fatalf("FUSE Fallocate failed: %v", err)
|
||||
}
|
||||
fi, err := os.Lstat(ts.orig + "/file")
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
if fi.Size() < (1024 + 4096) {
|
||||
t.Fatalf("fallocate should have changed file size. Got %d bytes",
|
||||
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")
|
||||
}
|
||||
}
|
||||
960
vendor/github.com/hanwen/go-fuse/fuse/test/loopback_test.go
generated
vendored
Normal file
960
vendor/github.com/hanwen/go-fuse/fuse/test/loopback_test.go
generated
vendored
Normal file
@@ -0,0 +1,960 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||
)
|
||||
|
||||
const mode uint32 = 0757
|
||||
|
||||
type testCase struct {
|
||||
tmpDir string
|
||||
orig string
|
||||
mnt string
|
||||
|
||||
mountFile string
|
||||
mountSubdir string
|
||||
origFile string
|
||||
origSubdir string
|
||||
tester *testing.T
|
||||
state *fuse.Server
|
||||
pathFs *pathfs.PathNodeFs
|
||||
connector *nodefs.FileSystemConnector
|
||||
}
|
||||
|
||||
const testTtl = 100 * time.Millisecond
|
||||
|
||||
// Mkdir is a utility wrapper for os.Mkdir, aborting the test if it fails.
|
||||
func (tc *testCase) Mkdir(name string, mode os.FileMode) {
|
||||
if err := os.Mkdir(name, mode); err != nil {
|
||||
tc.tester.Fatalf("Mkdir(%q,%v): %v", name, mode, err)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteFile is a utility wrapper for ioutil.WriteFile, aborting the
|
||||
// test if it fails.
|
||||
func (tc *testCase) WriteFile(name string, content []byte, mode os.FileMode) {
|
||||
if err := ioutil.WriteFile(name, content, mode); err != nil {
|
||||
if len(content) > 50 {
|
||||
content = append(content[:50], '.', '.', '.')
|
||||
}
|
||||
|
||||
tc.tester.Fatalf("WriteFile(%q, %q, %o): %v", name, content, mode, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create and mount filesystem.
|
||||
func NewTestCase(t *testing.T) *testCase {
|
||||
tc := &testCase{}
|
||||
tc.tester = t
|
||||
|
||||
// Make sure system setting does not affect test.
|
||||
syscall.Umask(0)
|
||||
|
||||
const name string = "hello.txt"
|
||||
const subdir string = "subdir"
|
||||
|
||||
var err error
|
||||
tc.tmpDir, err = ioutil.TempDir("", "go-fuse")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
tc.orig = tc.tmpDir + "/orig"
|
||||
tc.mnt = tc.tmpDir + "/mnt"
|
||||
|
||||
tc.Mkdir(tc.orig, 0700)
|
||||
tc.Mkdir(tc.mnt, 0700)
|
||||
|
||||
tc.mountFile = filepath.Join(tc.mnt, name)
|
||||
tc.mountSubdir = filepath.Join(tc.mnt, subdir)
|
||||
tc.origFile = filepath.Join(tc.orig, name)
|
||||
tc.origSubdir = filepath.Join(tc.orig, subdir)
|
||||
|
||||
var pfs pathfs.FileSystem
|
||||
pfs = pathfs.NewLoopbackFileSystem(tc.orig)
|
||||
pfs = pathfs.NewLockingFileSystem(pfs)
|
||||
|
||||
tc.pathFs = pathfs.NewPathNodeFs(pfs, &pathfs.PathNodeFsOptions{
|
||||
ClientInodes: true})
|
||||
tc.connector = nodefs.NewFileSystemConnector(tc.pathFs.Root(),
|
||||
&nodefs.Options{
|
||||
EntryTimeout: testTtl,
|
||||
AttrTimeout: testTtl,
|
||||
NegativeTimeout: 0.0,
|
||||
Debug: VerboseTest(),
|
||||
})
|
||||
tc.state, err = fuse.NewServer(
|
||||
fuse.NewRawFileSystem(tc.connector.RawFS()), tc.mnt, &fuse.MountOptions{
|
||||
SingleThreaded: true,
|
||||
Debug: VerboseTest(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("NewServer:", err)
|
||||
}
|
||||
|
||||
go tc.state.Serve()
|
||||
if err := tc.state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
return tc
|
||||
}
|
||||
|
||||
// Unmount and del.
|
||||
func (tc *testCase) Cleanup() {
|
||||
err := tc.state.Unmount()
|
||||
if err != nil {
|
||||
tc.tester.Fatalf("Unmount failed: %v", err)
|
||||
}
|
||||
os.RemoveAll(tc.tmpDir)
|
||||
}
|
||||
|
||||
func (tc *testCase) rootNode() *nodefs.Inode {
|
||||
return tc.pathFs.Root().Inode()
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Tests.
|
||||
|
||||
func TestOpenUnreadable(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
_, err := os.Open(tc.mnt + "/doesnotexist")
|
||||
if err == nil {
|
||||
t.Errorf("open non-existent should raise error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadThrough(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
content := randomData(125)
|
||||
tc.WriteFile(tc.origFile, content, 0700)
|
||||
err := os.Chmod(tc.mountFile, os.FileMode(mode))
|
||||
if err != nil {
|
||||
t.Fatalf("Chmod failed: %v", err)
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(tc.mountFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
if uint32(fi.Mode().Perm()) != mode {
|
||||
t.Errorf("Wrong mode %o != %o", int(fi.Mode().Perm()), mode)
|
||||
}
|
||||
|
||||
// Open (for read), read.
|
||||
f, err := os.Open(tc.mountFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var buf [1024]byte
|
||||
slice := buf[:]
|
||||
n, err := f.Read(slice)
|
||||
CompareSlices(t, slice[:n], content)
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
contents := []byte{1, 2, 3}
|
||||
tc.WriteFile(tc.origFile, []byte(contents), 0700)
|
||||
|
||||
err := os.Remove(tc.mountFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Remove failed: %v", err)
|
||||
}
|
||||
_, err = os.Lstat(tc.origFile)
|
||||
if err == nil {
|
||||
t.Errorf("Lstat() after delete should have generated error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteThrough(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
// Create (for write), write.
|
||||
f, err := os.OpenFile(tc.mountFile, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
content := randomData(125)
|
||||
n, err := f.Write(content)
|
||||
if err != nil {
|
||||
t.Fatalf("Write failed: %v", err)
|
||||
}
|
||||
if n != len(content) {
|
||||
t.Errorf("Write mismatch: %v of %v", n, len(content))
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(tc.origFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat(%q): %v", tc.origFile, err)
|
||||
}
|
||||
if fi.Mode().Perm() != 0644 {
|
||||
t.Errorf("create mode error %o", fi.Mode()&0777)
|
||||
}
|
||||
|
||||
f, err = os.Open(tc.origFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var buf [1024]byte
|
||||
slice := buf[:]
|
||||
n, err = f.Read(slice)
|
||||
if err != nil {
|
||||
t.Fatalf("Read failed: %v", err)
|
||||
}
|
||||
CompareSlices(t, slice[:n], content)
|
||||
}
|
||||
|
||||
func TestMkdirRmdir(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
// Mkdir/Rmdir.
|
||||
if err := os.Mkdir(tc.mountSubdir, 0777); err != nil {
|
||||
t.Fatalf("Mkdir failed: %v", err)
|
||||
}
|
||||
|
||||
if fi, err := os.Lstat(tc.origSubdir); err != nil {
|
||||
t.Fatalf("Lstat(%q): %v", tc.origSubdir, err)
|
||||
} else if !fi.IsDir() {
|
||||
t.Errorf("Not a directory: %v", fi)
|
||||
}
|
||||
|
||||
if err := os.Remove(tc.mountSubdir); err != nil {
|
||||
t.Fatalf("Remove failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkCreate(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
content := randomData(125)
|
||||
tc.WriteFile(tc.origFile, content, 0700)
|
||||
|
||||
tc.Mkdir(tc.origSubdir, 0777)
|
||||
|
||||
// Link.
|
||||
mountSubfile := filepath.Join(tc.mountSubdir, "subfile")
|
||||
err := os.Link(tc.mountFile, mountSubfile)
|
||||
if err != nil {
|
||||
t.Fatalf("Link failed: %v", err)
|
||||
}
|
||||
|
||||
var subStat, stat syscall.Stat_t
|
||||
err = syscall.Lstat(mountSubfile, &subStat)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
err = syscall.Lstat(tc.mountFile, &stat)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
if stat.Nlink != 2 {
|
||||
t.Errorf("Expect 2 links: %v", stat)
|
||||
}
|
||||
if stat.Ino != subStat.Ino {
|
||||
t.Errorf("Link succeeded, but inode numbers different: %v %v", stat.Ino, subStat.Ino)
|
||||
}
|
||||
readback, err := ioutil.ReadFile(mountSubfile)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile failed: %v", err)
|
||||
}
|
||||
CompareSlices(t, readback, content)
|
||||
|
||||
err = os.Remove(tc.mountFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Remove failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = ioutil.ReadFile(mountSubfile)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func randomData(size int) []byte {
|
||||
return bytes.Repeat([]byte{'x'}, size)
|
||||
}
|
||||
|
||||
// Deal correctly with hard links implied by matching client inode
|
||||
// numbers.
|
||||
func TestLinkExisting(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
c := randomData(5)
|
||||
|
||||
tc.WriteFile(tc.orig+"/file1", c, 0644)
|
||||
|
||||
err := os.Link(tc.orig+"/file1", tc.orig+"/file2")
|
||||
if err != nil {
|
||||
t.Fatalf("Link failed: %v", err)
|
||||
}
|
||||
|
||||
var s1, s2 syscall.Stat_t
|
||||
err = syscall.Lstat(tc.mnt+"/file1", &s1)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
err = syscall.Lstat(tc.mnt+"/file2", &s2)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
if s1.Ino != s2.Ino {
|
||||
t.Errorf("linked files should have identical inodes %v %v", s1.Ino, s2.Ino)
|
||||
}
|
||||
|
||||
back, err := ioutil.ReadFile(tc.mnt + "/file1")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile failed: %v", err)
|
||||
}
|
||||
CompareSlices(t, back, c)
|
||||
}
|
||||
|
||||
// Deal correctly with hard links implied by matching client inode
|
||||
// numbers.
|
||||
func TestLinkForget(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
c := "hello"
|
||||
|
||||
tc.WriteFile(tc.orig+"/file1", []byte(c), 0644)
|
||||
err := os.Link(tc.orig+"/file1", tc.orig+"/file2")
|
||||
if err != nil {
|
||||
t.Fatalf("Link failed: %v", err)
|
||||
}
|
||||
|
||||
for _, fn := range []string{"file1", "file2"} {
|
||||
var s syscall.Stat_t
|
||||
err = syscall.Lstat(tc.mnt+"/"+fn, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
tc.pathFs.ForgetClientInodes()
|
||||
}
|
||||
|
||||
// Now, the backing files are still hardlinked, but go-fuse's
|
||||
// view of them should not be because of the
|
||||
// ForgetClientInodes call. To prove this, we swap out the
|
||||
// files in the backing store, and prove that they are
|
||||
// distinct by truncating to different lengths.
|
||||
for _, fn := range []string{"file1", "file2"} {
|
||||
fn = tc.orig + "/" + fn
|
||||
if err := os.Remove(fn); err != nil {
|
||||
t.Fatalf("Remove", err)
|
||||
}
|
||||
tc.WriteFile(fn, []byte(c), 0644)
|
||||
}
|
||||
for i, fn := range []string{"file1", "file2"} {
|
||||
fn = tc.mnt + "/" + fn
|
||||
if err := os.Truncate(fn, int64(i)); err != nil {
|
||||
t.Fatalf("Truncate", err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, fn := range []string{"file1", "file2"} {
|
||||
var s syscall.Stat_t
|
||||
err = syscall.Lstat(tc.mnt+"/"+fn, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
if s.Size != int64(i) {
|
||||
t.Errorf("Lstat(%q): got size %d, want %d", fn, s.Size, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSymlink(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
contents := []byte{1, 2, 3}
|
||||
tc.WriteFile(tc.origFile, []byte(contents), 0700)
|
||||
|
||||
linkFile := "symlink-file"
|
||||
orig := "hello.txt"
|
||||
err := os.Symlink(orig, filepath.Join(tc.mnt, linkFile))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Symlink failed: %v", err)
|
||||
}
|
||||
|
||||
origLink := filepath.Join(tc.orig, linkFile)
|
||||
fi, err := os.Lstat(origLink)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeSymlink == 0 {
|
||||
t.Errorf("not a symlink: %v", fi)
|
||||
return
|
||||
}
|
||||
|
||||
read, err := os.Readlink(filepath.Join(tc.mnt, linkFile))
|
||||
if err != nil {
|
||||
t.Fatalf("Readlink failed: %v", err)
|
||||
}
|
||||
|
||||
if read != orig {
|
||||
t.Errorf("unexpected symlink value '%v'", read)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRename(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
contents := []byte{1, 2, 3}
|
||||
tc.WriteFile(tc.origFile, []byte(contents), 0700)
|
||||
sd := tc.mnt + "/testRename"
|
||||
tc.Mkdir(sd, 0777)
|
||||
|
||||
subFile := sd + "/subfile"
|
||||
if err := os.Rename(tc.mountFile, subFile); err != nil {
|
||||
t.Fatalf("Rename failed: %v", err)
|
||||
}
|
||||
f, _ := os.Lstat(tc.origFile)
|
||||
if f != nil {
|
||||
t.Errorf("original %v still exists.", tc.origFile)
|
||||
}
|
||||
if _, err := os.Lstat(subFile); err != nil {
|
||||
t.Errorf("destination %q does not exist: %v", subFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Flaky test, due to rename race condition.
|
||||
func TestDelRename(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
sd := tc.mnt + "/testDelRename"
|
||||
tc.Mkdir(sd, 0755)
|
||||
|
||||
d := sd + "/dest"
|
||||
tc.WriteFile(d, []byte("blabla"), 0644)
|
||||
|
||||
f, err := os.Open(d)
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := os.Remove(d); err != nil {
|
||||
t.Fatalf("Remove failed: %v", err)
|
||||
}
|
||||
|
||||
s := sd + "/src"
|
||||
tc.WriteFile(s, []byte("blabla"), 0644)
|
||||
if err := os.Rename(s, d); err != nil {
|
||||
t.Fatalf("Rename failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverwriteRename(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
sd := tc.mnt + "/testOverwriteRename"
|
||||
tc.Mkdir(sd, 0755)
|
||||
|
||||
d := sd + "/dest"
|
||||
tc.WriteFile(d, []byte("blabla"), 0644)
|
||||
|
||||
s := sd + "/src"
|
||||
tc.WriteFile(s, []byte("blabla"), 0644)
|
||||
|
||||
if err := os.Rename(s, d); err != nil {
|
||||
t.Fatalf("Rename failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccess(t *testing.T) {
|
||||
if os.Geteuid() == 0 {
|
||||
t.Log("Skipping TestAccess() as root.")
|
||||
return
|
||||
}
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
contents := []byte{1, 2, 3}
|
||||
tc.WriteFile(tc.origFile, []byte(contents), 0700)
|
||||
if err := os.Chmod(tc.origFile, 0); err != nil {
|
||||
t.Fatalf("Chmod failed: %v", err)
|
||||
}
|
||||
// Ugh - copied from unistd.h
|
||||
const W_OK uint32 = 2
|
||||
|
||||
if errCode := syscall.Access(tc.mountFile, W_OK); errCode != syscall.EACCES {
|
||||
t.Errorf("Expected EACCES for non-writable, %v %v", errCode, syscall.EACCES)
|
||||
}
|
||||
|
||||
if err := os.Chmod(tc.origFile, 0222); err != nil {
|
||||
t.Fatalf("Chmod failed: %v", err)
|
||||
}
|
||||
|
||||
if errCode := syscall.Access(tc.mountFile, W_OK); errCode != nil {
|
||||
t.Errorf("Expected no error code for writable. %v", errCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMknod(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
if errNo := syscall.Mknod(tc.mountFile, syscall.S_IFIFO|0777, 0); errNo != nil {
|
||||
t.Errorf("Mknod %v", errNo)
|
||||
}
|
||||
|
||||
if fi, err := os.Lstat(tc.origFile); err != nil {
|
||||
t.Errorf("Lstat(%q): %v", tc.origFile, err)
|
||||
} else if fi.Mode()&os.ModeNamedPipe == 0 {
|
||||
t.Errorf("Expected FIFO filetype, got %x", fi.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaddir(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
contents := []byte{1, 2, 3}
|
||||
tc.WriteFile(tc.origFile, []byte(contents), 0700)
|
||||
tc.Mkdir(tc.origSubdir, 0777)
|
||||
|
||||
dir, err := os.Open(tc.mnt)
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %v", err)
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
infos, err := dir.Readdir(10)
|
||||
if err != nil {
|
||||
t.Fatalf("Readdir failed: %v", err)
|
||||
}
|
||||
|
||||
wanted := map[string]bool{
|
||||
"hello.txt": true,
|
||||
"subdir": true,
|
||||
}
|
||||
if len(wanted) != len(infos) {
|
||||
t.Errorf("Length mismatch %v", infos)
|
||||
} else {
|
||||
for _, v := range infos {
|
||||
_, ok := wanted[v.Name()]
|
||||
if !ok {
|
||||
t.Errorf("Unexpected name %v", v.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSync(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
contents := []byte{1, 2, 3}
|
||||
tc.WriteFile(tc.origFile, []byte(contents), 0700)
|
||||
|
||||
f, err := os.OpenFile(tc.mountFile, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile(%q): %v", tc.mountFile, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.WriteString("hello there"); err != nil {
|
||||
t.Fatalf("WriteString failed: %v", err)
|
||||
}
|
||||
|
||||
// How to really test fsync ?
|
||||
err = syscall.Fsync(int(f.Fd()))
|
||||
if err != nil {
|
||||
t.Errorf("fsync returned: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadZero(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
tc.WriteFile(tc.origFile, []byte{}, 0644)
|
||||
|
||||
back, err := ioutil.ReadFile(tc.mountFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile(%q): %v", tc.mountFile, err)
|
||||
} else if len(back) != 0 {
|
||||
t.Errorf("content length: got %d want %d", len(back), 0)
|
||||
}
|
||||
}
|
||||
|
||||
func CompareSlices(t *testing.T, got, want []byte) {
|
||||
if len(got) != len(want) {
|
||||
t.Errorf("content length: got %d want %d", len(got), len(want))
|
||||
return
|
||||
}
|
||||
|
||||
for i := range want {
|
||||
if want[i] != got[i] {
|
||||
t.Errorf("content mismatch byte %d, got %d want %d.", i, got[i], want[i])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that reading large files doesn't lead to large allocations.
|
||||
func TestReadLargeMemCheck(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
content := randomData(385 * 1023)
|
||||
tc.WriteFile(tc.origFile, []byte(content), 0644)
|
||||
|
||||
f, err := os.Open(tc.mountFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
buf := make([]byte, len(content)+1024)
|
||||
f.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Read failed: %v", err)
|
||||
}
|
||||
f.Close()
|
||||
runtime.GC()
|
||||
var before, after runtime.MemStats
|
||||
|
||||
N := 100
|
||||
runtime.ReadMemStats(&before)
|
||||
for i := 0; i < N; i++ {
|
||||
f, _ := os.Open(tc.mountFile)
|
||||
f.Read(buf)
|
||||
f.Close()
|
||||
}
|
||||
runtime.ReadMemStats(&after)
|
||||
delta := int((after.TotalAlloc - before.TotalAlloc))
|
||||
delta = (delta - 40000) / N
|
||||
t.Logf("bytes per read loop: %d", delta)
|
||||
}
|
||||
|
||||
func TestReadLarge(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
content := randomData(385 * 1023)
|
||||
tc.WriteFile(tc.origFile, []byte(content), 0644)
|
||||
|
||||
back, err := ioutil.ReadFile(tc.mountFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile failed: %v", err)
|
||||
}
|
||||
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)
|
||||
|
||||
b := make([]byte, r)
|
||||
for i := 0; i < r; i++ {
|
||||
b[i] = byte(i%10) + byte('0')
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func TestLargeDirRead(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
created := 100
|
||||
|
||||
names := make([]string, created)
|
||||
|
||||
subdir := filepath.Join(tc.orig, "readdirSubdir")
|
||||
|
||||
tc.Mkdir(subdir, 0700)
|
||||
|
||||
longname := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
nameSet := make(map[string]bool)
|
||||
for i := 0; i < created; i++ {
|
||||
// Should vary file name length.
|
||||
base := fmt.Sprintf("file%d%s", i,
|
||||
randomLengthString(len(longname)))
|
||||
name := filepath.Join(subdir, base)
|
||||
|
||||
nameSet[base] = true
|
||||
|
||||
tc.WriteFile(name, []byte("bla"), 0777)
|
||||
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
dir, err := os.Open(filepath.Join(tc.mnt, "readdirSubdir"))
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %v", err)
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
// Chunked read.
|
||||
total := 0
|
||||
readSet := make(map[string]bool)
|
||||
for {
|
||||
namesRead, err := dir.Readdirnames(200)
|
||||
if len(namesRead) == 0 || err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Readdirnames failed: %v", err)
|
||||
}
|
||||
for _, v := range namesRead {
|
||||
readSet[v] = true
|
||||
}
|
||||
total += len(namesRead)
|
||||
}
|
||||
|
||||
if total != created {
|
||||
t.Errorf("readdir mismatch got %v wanted %v", total, created)
|
||||
}
|
||||
for k := range nameSet {
|
||||
_, ok := readSet[k]
|
||||
if !ok {
|
||||
t.Errorf("Name %v not found in output", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootDir(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
d, err := os.Open(tc.mnt)
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %v", err)
|
||||
}
|
||||
if _, err := d.Readdirnames(-1); err != nil {
|
||||
t.Fatalf("Readdirnames failed: %v", err)
|
||||
}
|
||||
|
||||
if err := d.Close(); err != nil {
|
||||
t.Fatalf("Close failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ioctl(fd int, cmd int, arg uintptr) (int, int) {
|
||||
r0, _, e1 := syscall.Syscall(
|
||||
syscall.SYS_IOCTL, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||
val := int(r0)
|
||||
errno := int(e1)
|
||||
return val, errno
|
||||
}
|
||||
|
||||
func TestIoctl(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
f, err := os.OpenFile(filepath.Join(tc.mnt, "hello.txt"),
|
||||
os.O_WRONLY|os.O_CREATE, 0777)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
ioctl(int(f.Fd()), 0x5401, 42)
|
||||
}
|
||||
|
||||
// This test is racy. If an external process consumes space while this
|
||||
// runs, we may see spurious differences between the two statfs() calls.
|
||||
func TestStatFs(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
empty := syscall.Statfs_t{}
|
||||
s1 := empty
|
||||
if err := syscall.Statfs(tc.orig, &s1); err != nil {
|
||||
t.Fatal("statfs orig", err)
|
||||
}
|
||||
|
||||
s2 := syscall.Statfs_t{}
|
||||
if err := syscall.Statfs(tc.mnt, &s2); err != nil {
|
||||
t.Fatal("statfs mnt", err)
|
||||
}
|
||||
|
||||
clearStatfs(&s1)
|
||||
clearStatfs(&s2)
|
||||
if !reflect.DeepEqual(s1, s2) {
|
||||
t.Errorf("statfs mismatch %#v != %#v", s1, s2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFStatFs(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
fOrig, err := os.OpenFile(tc.orig+"/file", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer fOrig.Close()
|
||||
|
||||
empty := syscall.Statfs_t{}
|
||||
s1 := empty
|
||||
if errno := syscall.Fstatfs(int(fOrig.Fd()), &s1); errno != nil {
|
||||
t.Fatal("statfs orig", err)
|
||||
}
|
||||
|
||||
fMnt, err := os.OpenFile(tc.mnt+"/file", os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer fMnt.Close()
|
||||
s2 := empty
|
||||
|
||||
if errno := syscall.Fstatfs(int(fMnt.Fd()), &s2); errno != nil {
|
||||
t.Fatal("statfs mnt", err)
|
||||
}
|
||||
|
||||
clearStatfs(&s1)
|
||||
clearStatfs(&s2)
|
||||
if !reflect.DeepEqual(s1, s2) {
|
||||
t.Errorf("statfs mismatch: %#v != %#v", s1, s2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOriginalIsSymlink(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "go-fuse-loopback_test")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
orig := tmpDir + "/orig"
|
||||
err = os.Mkdir(orig, 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("Mkdir failed: %v", err)
|
||||
}
|
||||
link := tmpDir + "/link"
|
||||
mnt := tmpDir + "/mnt"
|
||||
if err := os.Mkdir(mnt, 0755); err != nil {
|
||||
t.Fatalf("Mkdir failed: %v", err)
|
||||
}
|
||||
if err := os.Symlink("orig", link); err != nil {
|
||||
t.Fatalf("Symlink failed: %v", err)
|
||||
}
|
||||
|
||||
fs := pathfs.NewLoopbackFileSystem(link)
|
||||
nfs := pathfs.NewPathNodeFs(fs, nil)
|
||||
state, _, err := nodefs.MountRoot(mnt, nfs.Root(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("MountNodeFileSystem failed: %v", err)
|
||||
}
|
||||
defer state.Unmount()
|
||||
|
||||
go state.Serve()
|
||||
if err := state.WaitMount(); err != nil {
|
||||
t.Fatal("WaitMount", err)
|
||||
}
|
||||
|
||||
if _, err := os.Lstat(mnt); err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoubleOpen(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
tc.WriteFile(tc.orig+"/file", []byte("blabla"), 0644)
|
||||
|
||||
roFile, err := os.Open(tc.mnt + "/file")
|
||||
if err != nil {
|
||||
t.Fatalf(" failed: %v", err)
|
||||
}
|
||||
defer roFile.Close()
|
||||
|
||||
rwFile, err := os.OpenFile(tc.mnt+"/file", os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenFile failed: %v", err)
|
||||
}
|
||||
defer rwFile.Close()
|
||||
}
|
||||
|
||||
func TestUmask(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
// Make sure system setting does not affect test.
|
||||
fn := tc.mnt + "/file"
|
||||
mask := 020
|
||||
cmd := exec.Command("/bin/sh", "-c",
|
||||
fmt.Sprintf("umask %o && mkdir %s", mask, fn))
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Fatalf("cmd.Run: %v", err)
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(fn)
|
||||
if err != nil {
|
||||
t.Fatalf("Lstat failed: %v", err)
|
||||
}
|
||||
|
||||
expect := mask ^ 0777
|
||||
got := int(fi.Mode().Perm())
|
||||
if got != expect {
|
||||
t.Errorf("got %o, expect mode %o for file %s", got, expect, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that chgrp(1) works
|
||||
func TestChgrp(t *testing.T) {
|
||||
tc := NewTestCase(t)
|
||||
defer tc.Cleanup()
|
||||
|
||||
f, err := os.Create(tc.mnt + "/file")
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = f.Chown(-1, os.Getgid())
|
||||
if err != nil {
|
||||
t.Errorf("Chown failed: %v", err)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user