updated bazil.org/fuse
Juan Batiz-Benet committed
Jan 2, 2015 at 04:48 UTC
56867c8b7035c7c76b5328481065a9dfbeefd8fb
21 files changed
+736
-137
Godeps/Godeps.json
+1
-1
@@ -7,7 +7,7 @@
7
"Deps": [
8
{
9
"ImportPath": "bazil.org/fuse",
10
- "Rev": "a04507d54fc3610d38ee951402d8c4acab56c7b1"
10
+ "Rev": "d62a1291477b51b24becf4def173bd843138c4b6"
11
},
12
{
13
"ImportPath": "bitbucket.org/kardianos/osext",
Godeps/_workspace/src/bazil.org/fuse/debug.go
+7
@@ -11,4 +11,11 @@ func stack() string {
11
12
func nop(msg interface{}) {}
13
14
+// Debug is called to output debug messages, including protocol
15
+// traces. The default behavior is to do nothing.
16
+//
17
+// The messages have human-friendly string representations and are
18
+// safe to marshal to JSON.
19
+//
20
+// Implementations must not retain msg.
21
var Debug func(msg interface{}) = nop
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/doc.go
new
+1
@@ -0,0 +1 @@
1
+package fstestutil
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mounted.go
+4
-4
@@ -55,12 +55,12 @@ func (mnt *Mount) Close() {
55
// workaround).
56
//
57
// After successful return, caller must clean up by calling Close.
58
-func Mounted(srv *fs.Server) (*Mount, error) {
58
+func Mounted(srv *fs.Server, options ...fuse.MountOption) (*Mount, error) {
59
dir, err := ioutil.TempDir("", "fusetest")
60
if err != nil {
61
return nil, err
62
}
63
- c, err := fuse.Mount(dir)
63
+ c, err := fuse.Mount(dir, options...)
64
if err != nil {
65
return nil, err
66
}
@@ -100,7 +100,7 @@ func Mounted(srv *fs.Server) (*Mount, error) {
100
//
101
// The debug log is not enabled by default. Use `-fuse.debug` or call
102
// DebugByDefault to enable.
103
-func MountedT(t testing.TB, filesys fs.FS) (*Mount, error) {
103
+func MountedT(t testing.TB, filesys fs.FS, options ...fuse.MountOption) (*Mount, error) {
104
srv := &fs.Server{
105
FS: filesys,
106
}
@@ -109,5 +109,5 @@ func MountedT(t testing.TB, filesys fs.FS) (*Mount, error) {
109
t.Logf("FUSE: %s", msg)
110
}
111
}
112
- return Mounted(srv)
112
+ return Mounted(srv, options...)
113
}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo.go
new
+14
@@ -0,0 +1,14 @@
1
+package fstestutil
2
+
3
+// MountInfo describes a mounted file system.
4
+type MountInfo struct {
5
+ FSName string
6
+ Type string
7
+}
8
+
9
+// GetMountInfo finds information about the mount at mnt. It is
10
+// intended for use by tests only, and only fetches information
11
+// relevant to the current tests.
12
+func GetMountInfo(mnt string) (*MountInfo, error) {
13
+ return getMountInfo(mnt)
14
+}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo_darwin.go
new
+41
@@ -0,0 +1,41 @@
1
+package fstestutil
2
+
3
+import (
4
+ "regexp"
5
+ "syscall"
6
+)
7
+
8
+// cstr converts a nil-terminated C string into a Go string
9
+func cstr(ca []int8) string {
10
+ s := make([]byte, 0, len(ca))
11
+ for _, c := range ca {
12
+ if c == 0x00 {
13
+ break
14
+ }
15
+ s = append(s, byte(c))
16
+ }
17
+ return string(s)
18
+}
19
+
20
+var re = regexp.MustCompile(`\\(.)`)
21
+
22
+// unescape removes backslash-escaping. The escaped characters are not
23
+// mapped in any way; that is, unescape(`\n` ) == `n`.
24
+func unescape(s string) string {
25
+ return re.ReplaceAllString(s, `$1`)
26
+}
27
+
28
+func getMountInfo(mnt string) (*MountInfo, error) {
29
+ var st syscall.Statfs_t
30
+ err := syscall.Statfs(mnt, &st)
31
+ if err != nil {
32
+ return nil, err
33
+ }
34
+ i := &MountInfo{
35
+ // osx getmntent(3) fails to un-escape the data, so we do it..
36
+ // this might lead to double-unescaping in the future. fun.
37
+ // TestMountOptionFSNameEvilBackslashDouble checks for that.
38
+ FSName: unescape(cstr(st.Mntfromname[:])),
39
+ }
40
+ return i, nil
41
+}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo_linux.go
new
+51
@@ -0,0 +1,51 @@
1
+package fstestutil
2
+
3
+import (
4
+ "errors"
5
+ "io/ioutil"
6
+ "strings"
7
+)
8
+
9
+// Linux /proc/mounts shows current mounts.
10
+// Same format as /etc/fstab. Quoting getmntent(3):
11
+//
12
+// Since fields in the mtab and fstab files are separated by whitespace,
13
+// octal escapes are used to represent the four characters space (\040),
14
+// tab (\011), newline (\012) and backslash (\134) in those files when
15
+// they occur in one of the four strings in a mntent structure.
16
+//
17
+// http://linux.die.net/man/3/getmntent
18
+
19
+var fstabUnescape = strings.NewReplacer(
20
+ `\040`, "\040",
21
+ `\011`, "\011",
22
+ `\012`, "\012",
23
+ `\134`, "\134",
24
+)
25
+
26
+var errNotFound = errors.New("mount not found")
27
+
28
+func getMountInfo(mnt string) (*MountInfo, error) {
29
+ data, err := ioutil.ReadFile("/proc/mounts")
30
+ if err != nil {
31
+ return nil, err
32
+ }
33
+ for _, line := range strings.Split(string(data), "\n") {
34
+ fields := strings.Fields(line)
35
+ if len(fields) < 3 {
36
+ continue
37
+ }
38
+ // Fields are: fsname dir type opts freq passno
39
+ fsname := fstabUnescape.Replace(fields[0])
40
+ dir := fstabUnescape.Replace(fields[1])
41
+ fstype := fstabUnescape.Replace(fields[2])
42
+ if mnt == dir {
43
+ info := &MountInfo{
44
+ FSName: fsname,
45
+ Type: fstype,
46
+ }
47
+ return info, nil
48
+ }
49
+ }
50
+ return nil, errNotFound
51
+}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/testfs.go
new
+29
@@ -0,0 +1,29 @@
1
+package fstestutil
2
+
3
+import (
4
+ "os"
5
+
6
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
8
+)
9
+
10
+// SimpleFS is a trivial FS that just implements the Root method.
11
+type SimpleFS struct {
12
+ Node fs.Node
13
+}
14
+
15
+var _ = fs.FS(SimpleFS{})
16
+
17
+func (f SimpleFS) Root() (fs.Node, fuse.Error) {
18
+ return f.Node, nil
19
+}
20
+
21
+// File can be embedded in a struct to make it look like a file.
22
+type File struct{}
23
+
24
+func (f File) Attr() fuse.Attr { return fuse.Attr{Mode: 0666} }
25
+
26
+// Dir can be embedded in a struct to make it look like a directory.
27
+type Dir struct{}
28
+
29
+func (f Dir) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeDir | 0777} }
Godeps/_workspace/src/bazil.org/fuse/fs/serve.go
+3
-2
@@ -10,7 +10,6 @@ import (
10
"reflect"
11
"strings"
12
"sync"
13
- "syscall"
13
"time"
14
)
15
@@ -295,6 +294,8 @@ type Server struct {
294
// Function to send debug log messages to. If nil, use fuse.Debug.
295
// Note that changing this or fuse.Debug may not affect existing
296
// calls to Serve.
297
+ //
298
+ // See fuse.Debug for the rules that log functions must follow.
299
Debug func(msg interface{})
300
}
301
@@ -317,7 +318,7 @@ func (s *Server) Serve(c *fuse.Conn) error {
318
319
root, err := sc.fs.Root()
320
if err != nil {
320
- return fmt.Errorf("cannot obtain root node: %v", syscall.Errno(err.(fuse.Errno)).Error())
321
+ return fmt.Errorf("cannot obtain root node: %v", err)
322
}
323
sc.node = append(sc.node, nil, &serveNode{inode: 1, node: root, refs: 1})
324
sc.handle = append(sc.handle, nil)
Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go
+77
-73
@@ -87,27 +87,6 @@ func (f childMapFS) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
87
return child, nil
88
}
89
90
-// simpleFS is a trivial FS that just implements the Root method.
91
-type simpleFS struct {
92
- node fs.Node
93
-}
94
-
95
-var _ = fs.FS(simpleFS{})
96
-
97
-func (f simpleFS) Root() (fs.Node, fuse.Error) {
98
- return f.node, nil
99
-}
100
-
101
-// file can be embedded in a struct to make it look like a file.
102
-type file struct{}
103
-
104
-func (f file) Attr() fuse.Attr { return fuse.Attr{Mode: 0666} }
105
-
106
-// dir can be embedded in a struct to make it look like a directory.
107
-type dir struct{}
108
-
109
-func (f dir) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeDir | 0777} }
110
-
90
// symlink can be embedded in a struct to make it look like a symlink.
91
type symlink struct {
92
target string
@@ -261,7 +240,9 @@ func TestStatRoot(t *testing.T) {
240
241
// Test Read calling ReadAll.
242
264
-type readAll struct{ file }
243
+type readAll struct {
244
+ fstestutil.File
245
+}
246
247
const hi = "hello, world"
248
@@ -299,7 +280,9 @@ func TestReadAll(t *testing.T) {
280
281
// Test Read.
282
302
-type readWithHandleRead struct{ file }
283
+type readWithHandleRead struct {
284
+ fstestutil.File
285
+}
286
287
func (readWithHandleRead) Attr() fuse.Attr {
288
return fuse.Attr{
@@ -327,7 +310,7 @@ func TestReadAllWithHandleRead(t *testing.T) {
310
// Test Release.
311
312
type release struct {
330
- file
313
+ fstestutil.File
314
record.ReleaseWaiter
315
}
316
@@ -353,7 +336,7 @@ func TestRelease(t *testing.T) {
336
// Test Write calling basic Write, with an fsync thrown in too.
337
338
type write struct {
356
- file
339
+ fstestutil.File
340
record.Writes
341
record.Fsyncs
342
}
@@ -401,7 +384,7 @@ func TestWrite(t *testing.T) {
384
// Test Write of a larger buffer.
385
386
type writeLarge struct {
404
- file
387
+ fstestutil.File
388
record.Writes
389
}
390
@@ -446,7 +429,7 @@ func TestWriteLarge(t *testing.T) {
429
// Test Write calling Setattr+Write+Flush.
430
431
type writeTruncateFlush struct {
449
- file
432
+ fstestutil.File
433
record.Writes
434
record.Setattrs
435
record.Flushes
@@ -479,7 +462,7 @@ func TestWriteTruncateFlush(t *testing.T) {
462
// Test Mkdir.
463
464
type mkdir1 struct {
482
- dir
465
+ fstestutil.Dir
466
record.Mkdirs
467
}
468
@@ -491,7 +474,7 @@ func (f *mkdir1) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Erro
474
func TestMkdir(t *testing.T) {
475
t.Parallel()
476
f := &mkdir1{}
494
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
477
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
478
if err != nil {
479
t.Fatal(err)
480
}
@@ -513,12 +496,12 @@ func TestMkdir(t *testing.T) {
496
// Test Create (and fsync)
497
498
type create1file struct {
516
- file
499
+ fstestutil.File
500
record.Fsyncs
501
}
502
503
type create1 struct {
521
- dir
504
+ fstestutil.Dir
505
f create1file
506
}
507
@@ -553,7 +536,7 @@ func (f *create1) Create(req *fuse.CreateRequest, resp *fuse.CreateResponse, int
536
func TestCreate(t *testing.T) {
537
t.Parallel()
538
f := &create1{}
556
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
539
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
540
if err != nil {
541
t.Fatal(err)
542
}
@@ -583,12 +566,12 @@ func TestCreate(t *testing.T) {
566
// Test Create + Write + Remove
567
568
type create3file struct {
586
- file
569
+ fstestutil.File
570
record.Writes
571
}
572
573
type create3 struct {
591
- dir
574
+ fstestutil.Dir
575
f create3file
576
fooCreated record.MarkRecorder
577
fooRemoved record.MarkRecorder
@@ -622,7 +605,7 @@ func (f *create3) Remove(r *fuse.RemoveRequest, intr fs.Intr) fuse.Error {
605
func TestCreateWriteRemove(t *testing.T) {
606
t.Parallel()
607
f := &create3{}
625
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
608
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
609
if err != nil {
610
t.Fatal(err)
611
}
@@ -659,7 +642,7 @@ func (f symlink1link) Readlink(*fuse.ReadlinkRequest, fs.Intr) (string, fuse.Err
642
}
643
644
type symlink1 struct {
662
- dir
645
+ fstestutil.Dir
646
record.Symlinks
647
}
648
@@ -671,7 +654,7 @@ func (f *symlink1) Symlink(req *fuse.SymlinkRequest, intr fs.Intr) (fs.Node, fus
654
func TestSymlink(t *testing.T) {
655
t.Parallel()
656
f := &symlink1{}
674
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
657
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
658
if err != nil {
659
t.Fatal(err)
660
}
@@ -701,26 +684,26 @@ func TestSymlink(t *testing.T) {
684
// Test link
685
686
type link1 struct {
704
- dir
687
+ fstestutil.Dir
688
record.Links
689
}
690
691
func (f *link1) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
692
if name == "old" {
710
- return file{}, nil
693
+ return fstestutil.File{}, nil
694
}
695
return nil, fuse.ENOENT
696
}
697
698
func (f *link1) Link(r *fuse.LinkRequest, old fs.Node, intr fs.Intr) (fs.Node, fuse.Error) {
699
f.Links.Link(r, old, intr)
717
- return file{}, nil
700
+ return fstestutil.File{}, nil
701
}
702
703
func TestLink(t *testing.T) {
704
t.Parallel()
705
f := &link1{}
723
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
706
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
707
if err != nil {
708
t.Fatal(err)
709
}
@@ -745,13 +728,13 @@ func TestLink(t *testing.T) {
728
// Test Rename
729
730
type rename1 struct {
748
- dir
731
+ fstestutil.Dir
732
renamed record.Counter
733
}
734
735
func (f *rename1) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
736
if name == "old" {
754
- return file{}, nil
737
+ return fstestutil.File{}, nil
738
}
739
return nil, fuse.ENOENT
740
}
@@ -767,7 +750,7 @@ func (f *rename1) Rename(r *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fu
750
func TestRename(t *testing.T) {
751
t.Parallel()
752
f := &rename1{}
770
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
753
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
754
if err != nil {
755
t.Fatal(err)
756
}
@@ -789,7 +772,7 @@ func TestRename(t *testing.T) {
772
// Test mknod
773
774
type mknod1 struct {
792
- dir
775
+ fstestutil.Dir
776
record.Mknods
777
}
778
@@ -805,7 +788,7 @@ func TestMknod(t *testing.T) {
788
}
789
790
f := &mknod1{}
808
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
791
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
792
if err != nil {
793
t.Fatal(err)
794
}
@@ -836,7 +819,7 @@ func TestMknod(t *testing.T) {
819
// Test Read served with DataHandle.
820
821
type dataHandleTest struct {
839
- file
822
+ fstestutil.File
823
}
824
825
func (dataHandleTest) Attr() fuse.Attr {
@@ -872,7 +855,7 @@ func TestDataHandle(t *testing.T) {
855
// Test interrupt
856
857
type interrupt struct {
875
- file
858
+ fstestutil.File
859
860
// strobes to signal we have a read hanging
861
hanging chan struct{}
@@ -955,7 +938,7 @@ func TestInterrupt(t *testing.T) {
938
// Test truncate
939
940
type truncate struct {
958
- file
941
+ fstestutil.File
942
record.Setattrs
943
}
944
@@ -996,7 +979,7 @@ func TestTruncate0(t *testing.T) {
979
// Test ftruncate
980
981
type ftruncate struct {
999
- file
982
+ fstestutil.File
983
record.Setattrs
984
}
985
@@ -1046,7 +1029,7 @@ func TestFtruncate0(t *testing.T) {
1029
// Test opening existing file truncates
1030
1031
type truncateWithOpen struct {
1049
- file
1032
+ fstestutil.File
1033
record.Setattrs
1034
}
1035
@@ -1083,7 +1066,7 @@ func TestTruncateWithOpen(t *testing.T) {
1066
// Test readdir
1067
1068
type readdir struct {
1086
- dir
1069
+ fstestutil.Dir
1070
}
1071
1072
func (d *readdir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
@@ -1097,7 +1080,7 @@ func (d *readdir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
1080
func TestReadDir(t *testing.T) {
1081
t.Parallel()
1082
f := &readdir{}
1100
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
1083
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
1084
if err != nil {
1085
t.Fatal(err)
1086
}
@@ -1133,7 +1116,7 @@ func TestReadDir(t *testing.T) {
1116
// Test Chmod.
1117
1118
type chmod struct {
1136
- file
1119
+ fstestutil.File
1120
record.Setattrs
1121
}
1122
@@ -1169,7 +1152,7 @@ func TestChmod(t *testing.T) {
1152
// Test open
1153
1154
type open struct {
1172
- file
1155
+ fstestutil.File
1156
record.Opens
1157
}
1158
@@ -1233,14 +1216,14 @@ func TestOpen(t *testing.T) {
1216
// Test Fsync on a dir
1217
1218
type fsyncDir struct {
1236
- dir
1219
+ fstestutil.Dir
1220
record.Fsyncs
1221
}
1222
1223
func TestFsyncDir(t *testing.T) {
1224
t.Parallel()
1225
f := &fsyncDir{}
1243
- mnt, err := fstestutil.MountedT(t, simpleFS{f})
1226
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
1227
if err != nil {
1228
t.Fatal(err)
1229
}
@@ -1278,7 +1261,7 @@ func TestFsyncDir(t *testing.T) {
1261
// Test Getxattr
1262
1263
type getxattr struct {
1281
- file
1264
+ fstestutil.File
1265
record.Getxattrs
1266
}
1267
@@ -1316,7 +1299,7 @@ func TestGetxattr(t *testing.T) {
1299
// Test Getxattr that has no space to return value
1300
1301
type getxattrTooSmall struct {
1319
- file
1302
+ fstestutil.File
1303
}
1304
1305
func (f *getxattrTooSmall) Getxattr(req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse, intr fs.Intr) fuse.Error {
@@ -1347,7 +1330,7 @@ func TestGetxattrTooSmall(t *testing.T) {
1330
// Test Getxattr used to probe result size
1331
1332
type getxattrSize struct {
1350
- file
1333
+ fstestutil.File
1334
}
1335
1336
func (f *getxattrSize) Getxattr(req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse, intr fs.Intr) fuse.Error {
@@ -1377,7 +1360,7 @@ func TestGetxattrSize(t *testing.T) {
1360
// Test Listxattr
1361
1362
type listxattr struct {
1380
- file
1363
+ fstestutil.File
1364
record.Listxattrs
1365
}
1366
@@ -1418,7 +1401,7 @@ func TestListxattr(t *testing.T) {
1401
// Test Listxattr that has no space to return value
1402
1403
type listxattrTooSmall struct {
1421
- file
1404
+ fstestutil.File
1405
}
1406
1407
func (f *listxattrTooSmall) Listxattr(req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse, intr fs.Intr) fuse.Error {
@@ -1449,7 +1432,7 @@ func TestListxattrTooSmall(t *testing.T) {
1432
// Test Listxattr used to probe result size
1433
1434
type listxattrSize struct {
1452
- file
1435
+ fstestutil.File
1436
}
1437
1438
func (f *listxattrSize) Listxattr(req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse, intr fs.Intr) fuse.Error {
@@ -1479,11 +1462,16 @@ func TestListxattrSize(t *testing.T) {
1462
// Test Setxattr
1463
1464
type setxattr struct {
1482
- file
1465
+ fstestutil.File
1466
record.Setxattrs
1467
}
1468
1486
-func TestSetxattr(t *testing.T) {
1469
+func testSetxattr(t *testing.T, size int) {
1470
+ const linux_XATTR_NAME_MAX = 64 * 1024
1471
+ if size > linux_XATTR_NAME_MAX && runtime.GOOS == "linux" {
1472
+ t.Skip("large xattrs are not supported by linux")
1473
+ }
1474
+
1475
t.Parallel()
1476
f := &setxattr{}
1477
mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
@@ -1492,7 +1480,9 @@ func TestSetxattr(t *testing.T) {
1480
}
1481
defer mnt.Close()
1482
1495
- err = syscallx.Setxattr(mnt.Dir+"/child", "greeting", []byte("hello, world"), 0)
1483
+ const g = "hello, world"
1484
+ greeting := strings.Repeat(g, size/len(g)+1)[:size]
1485
+ err = syscallx.Setxattr(mnt.Dir+"/child", "greeting", []byte(greeting), 0)
1486
if err != nil {
1487
t.Errorf("unexpected error: %v", err)
1488
return
@@ -1510,15 +1500,27 @@ func TestSetxattr(t *testing.T) {
1500
t.Errorf("Setxattr incorrect flags: %d != %d", g, e)
1501
}
1502
1513
- if g, e := string(got.Xattr), "hello, world"; g != e {
1503
+ if g, e := string(got.Xattr), greeting; g != e {
1504
t.Errorf("Setxattr incorrect data: %q != %q", g, e)
1505
}
1506
}
1507
1508
+func TestSetxattr(t *testing.T) {
1509
+ testSetxattr(t, 20)
1510
+}
1511
+
1512
+func TestSetxattr64kB(t *testing.T) {
1513
+ testSetxattr(t, 64*1024)
1514
+}
1515
+
1516
+func TestSetxattr16MB(t *testing.T) {
1517
+ testSetxattr(t, 16*1024*1024)
1518
+}
1519
+
1520
// Test Removexattr
1521
1522
type removexattr struct {
1521
- file
1523
+ fstestutil.File
1524
record.Removexattrs
1525
}
1526
@@ -1546,7 +1548,7 @@ func TestRemovexattr(t *testing.T) {
1548
// Test default error.
1549
1550
type defaultErrno struct {
1549
- dir
1551
+ fstestutil.Dir
1552
}
1553
1554
func (f defaultErrno) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
@@ -1555,7 +1557,7 @@ func (f defaultErrno) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
1557
1558
func TestDefaultErrno(t *testing.T) {
1559
t.Parallel()
1558
- mnt, err := fstestutil.MountedT(t, simpleFS{defaultErrno{}})
1560
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{defaultErrno{}})
1561
if err != nil {
1562
t.Fatal(err)
1563
}
@@ -1580,7 +1582,7 @@ func TestDefaultErrno(t *testing.T) {
1582
// Test custom error.
1583
1584
type customErrNode struct {
1583
- dir
1585
+ fstestutil.Dir
1586
}
1587
1588
type myCustomError struct {
@@ -1601,7 +1603,7 @@ func (f customErrNode) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
1603
1604
func TestCustomErrno(t *testing.T) {
1605
t.Parallel()
1604
- mnt, err := fstestutil.MountedT(t, simpleFS{customErrNode{}})
1606
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{customErrNode{}})
1607
if err != nil {
1608
t.Fatal(err)
1609
}
@@ -1736,7 +1738,9 @@ func TestMmap(t *testing.T) {
1738
1739
// Test direct Read.
1740
1739
-type directRead struct{ file }
1741
+type directRead struct {
1742
+ fstestutil.File
1743
+}
1744
1745
// explicitly not defining Attr and setting Size
1746
Godeps/_workspace/src/bazil.org/fuse/fuse.go
+185
-48
@@ -74,7 +74,8 @@
74
//
75
// Mount Options
76
//
77
-// XXX
77
+// Behavior and metadata of the mounted file system can be changed by
78
+// passing MountOption values to Mount.
79
//
80
package fuse
81
@@ -120,13 +121,21 @@ type Conn struct {
121
// visible until after Conn.Ready is closed. See Conn.MountError for
122
// possible errors. Incoming requests on Conn must be served to make
123
// progress.
123
-func Mount(dir string) (*Conn, error) {
124
- // TODO(rsc): mount options (...string?)
124
+func Mount(dir string, options ...MountOption) (*Conn, error) {
125
+ conf := MountConfig{
126
+ options: make(map[string]string),
127
+ }
128
+ for _, option := range options {
129
+ if err := option(&conf); err != nil {
130
+ return nil, err
131
+ }
132
+ }
133
+
134
ready := make(chan struct{}, 1)
135
c := &Conn{
136
Ready: ready,
137
}
129
- f, err := mount(dir, ready, &c.MountError)
138
+ f, err := mount(dir, &conf, ready, &c.MountError)
139
if err != nil {
140
return nil, err
141
}
@@ -170,6 +179,9 @@ type Header struct {
179
Uid uint32 // user ID of process making request
180
Gid uint32 // group ID of process making request
181
Pid uint32 // process ID of process making request
182
+
183
+ // for returning to reqPool
184
+ msg *message
185
}
186
187
func (h *Header) String() string {
@@ -180,6 +192,20 @@ func (h *Header) Hdr() *Header {
192
return h
193
}
194
195
+func (h *Header) noResponse() {
196
+ putMessage(h.msg)
197
+}
198
+
199
+func (h *Header) respond(out *outHeader, n uintptr) {
200
+ h.Conn.respond(out, n)
201
+ putMessage(h.msg)
202
+}
203
+
204
+func (h *Header) respondData(out *outHeader, n uintptr, data []byte) {
205
+ h.Conn.respondData(out, n, data)
206
+ putMessage(h.msg)
207
+}
208
+
209
// An Error is a FUSE error.
210
//
211
// Errors messages will be visible in the debug log as part of the
@@ -279,17 +305,48 @@ func (h *Header) RespondError(err Error) {
305
// FUSE uses negative errors!
306
// TODO: File bug report against OSXFUSE: positive error causes kernel panic.
307
out := &outHeader{Error: -int32(errno), Unique: uint64(h.ID)}
282
- h.Conn.respond(out, unsafe.Sizeof(*out))
308
+ h.respond(out, unsafe.Sizeof(*out))
309
}
310
311
// Maximum file write size we are prepared to receive from the kernel.
286
-const maxWrite = 128 * 1024
312
+const maxWrite = 16 * 1024 * 1024
313
314
// All requests read from the kernel, without data, are shorter than
315
// this.
316
var maxRequestSize = syscall.Getpagesize()
317
var bufSize = maxRequestSize + maxWrite
318
319
+// reqPool is a pool of messages.
320
+//
321
+// Lifetime of a logical message is from getMessage to putMessage.
322
+// getMessage is called by ReadRequest. putMessage is called by
323
+// Conn.ReadRequest, Request.Respond, or Request.RespondError.
324
+//
325
+// Messages in the pool are guaranteed to have conn and off zeroed,
326
+// buf allocated and len==bufSize, and hdr set.
327
+var reqPool = sync.Pool{
328
+ New: allocMessage,
329
+}
330
+
331
+func allocMessage() interface{} {
332
+ m := &message{buf: make([]byte, bufSize)}
333
+ m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0]))
334
+ return m
335
+}
336
+
337
+func getMessage(c *Conn) *message {
338
+ m := reqPool.Get().(*message)
339
+ m.conn = c
340
+ return m
341
+}
342
+
343
+func putMessage(m *message) {
344
+ m.buf = m.buf[:bufSize]
345
+ m.conn = nil
346
+ m.off = 0
347
+ reqPool.Put(m)
348
+}
349
+
350
// a message represents the bytes of a single FUSE message
351
type message struct {
352
conn *Conn
@@ -298,12 +355,6 @@ type message struct {
355
off int // offset for reading additional fields
356
}
357
301
-func newMessage(c *Conn) *message {
302
- m := &message{conn: c, buf: make([]byte, bufSize)}
303
- m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0]))
304
- return m
305
-}
306
-
358
func (m *message) len() uintptr {
359
return uintptr(len(m.buf) - m.off)
360
}
@@ -322,7 +373,16 @@ func (m *message) bytes() []byte {
373
374
func (m *message) Header() Header {
375
h := m.hdr
325
- return Header{Conn: m.conn, ID: RequestID(h.Unique), Node: NodeID(h.Nodeid), Uid: h.Uid, Gid: h.Gid, Pid: h.Pid}
376
+ return Header{
377
+ Conn: m.conn,
378
+ ID: RequestID(h.Unique),
379
+ Node: NodeID(h.Nodeid),
380
+ Uid: h.Uid,
381
+ Gid: h.Gid,
382
+ Pid: h.Pid,
383
+
384
+ msg: m,
385
+ }
386
}
387
388
// fileMode returns a Go os.FileMode from a Unix mode.
@@ -385,9 +445,12 @@ func (c *Conn) fd() int {
445
return int(c.dev.Fd())
446
}
447
448
+// ReadRequest returns the next FUSE request from the kernel.
449
+//
450
+// Caller must call either Request.Respond or Request.RespondError in
451
+// a reasonable time. Caller must not retain Request after that call.
452
func (c *Conn) ReadRequest() (Request, error) {
389
- // TODO: Some kind of buffer reuse.
390
- m := newMessage(c)
453
+ m := getMessage(c)
454
loop:
455
c.rio.RLock()
456
n, err := syscall.Read(c.fd(), m.buf)
@@ -398,14 +461,17 @@ loop:
461
goto loop
462
}
463
if err != nil && err != syscall.ENODEV {
464
+ putMessage(m)
465
return nil, err
466
}
467
if n <= 0 {
468
+ putMessage(m)
469
return nil, io.EOF
470
}
471
m.buf = m.buf[:n]
472
473
if n < inHeaderSize {
474
+ putMessage(m)
475
return nil, errors.New("fuse: message too short")
476
}
477
@@ -421,7 +487,10 @@ loop:
487
}
488
489
if m.hdr.Len != uint32(n) {
424
- return nil, fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len)
490
+ // prepare error message before returning m to pool
491
+ err := fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len)
492
+ putMessage(m)
493
+ return nil, err
494
}
495
496
m.off = inHeaderSize
@@ -821,6 +890,7 @@ loop:
890
891
corrupt:
892
Debug(malformedMessage{})
893
+ putMessage(m)
894
return nil, fmt.Errorf("fuse: malformed message")
895
896
unrecognized:
@@ -886,6 +956,8 @@ type InitRequest struct {
956
Flags InitFlags
957
}
958
959
+var _ = Request(&InitRequest{})
960
+
961
func (r *InitRequest) String() string {
962
return fmt.Sprintf("Init [%s] %d.%d ra=%d fl=%v", &r.Header, r.Major, r.Minor, r.MaxReadahead, r.Flags)
963
}
@@ -920,7 +992,7 @@ func (r *InitRequest) Respond(resp *InitResponse) {
992
if out.MaxWrite > maxWrite {
993
out.MaxWrite = maxWrite
994
}
923
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
995
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
996
}
997
998
// A StatfsRequest requests information about the mounted file system.
@@ -928,8 +1000,10 @@ type StatfsRequest struct {
1000
Header `json:"-"`
1001
}
1002
1003
+var _ = Request(&StatfsRequest{})
1004
+
1005
func (r *StatfsRequest) String() string {
932
- return fmt.Sprintf("Statfs [%s]\n", &r.Header)
1006
+ return fmt.Sprintf("Statfs [%s]", &r.Header)
1007
}
1008
1009
// Respond replies to the request with the given response.
@@ -946,7 +1020,7 @@ func (r *StatfsRequest) Respond(resp *StatfsResponse) {
1020
Frsize: resp.Frsize,
1021
},
1022
}
949
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1023
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1024
}
1025
1026
// A StatfsResponse is the response to a StatfsRequest.
@@ -972,6 +1046,8 @@ type AccessRequest struct {
1046
Mask uint32
1047
}
1048
1049
+var _ = Request(&AccessRequest{})
1050
+
1051
func (r *AccessRequest) String() string {
1052
return fmt.Sprintf("Access [%s] mask=%#x", &r.Header, r.Mask)
1053
}
@@ -980,7 +1056,7 @@ func (r *AccessRequest) String() string {
1056
// To deny access, use RespondError.
1057
func (r *AccessRequest) Respond() {
1058
out := &outHeader{Unique: uint64(r.ID)}
983
- r.Conn.respond(out, unsafe.Sizeof(*out))
1059
+ r.respond(out, unsafe.Sizeof(*out))
1060
}
1061
1062
// An Attr is the metadata for a single file or directory.
@@ -1057,6 +1133,8 @@ type GetattrRequest struct {
1133
Header `json:"-"`
1134
}
1135
1136
+var _ = Request(&GetattrRequest{})
1137
+
1138
func (r *GetattrRequest) String() string {
1139
return fmt.Sprintf("Getattr [%s]", &r.Header)
1140
}
@@ -1069,7 +1147,7 @@ func (r *GetattrRequest) Respond(resp *GetattrResponse) {
1147
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
1148
Attr: resp.Attr.attr(),
1149
}
1072
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1150
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1151
}
1152
1153
// A GetattrResponse is the response to a GetattrRequest.
@@ -1099,6 +1177,8 @@ type GetxattrRequest struct {
1177
Position uint32
1178
}
1179
1180
+var _ = Request(&GetxattrRequest{})
1181
+
1182
func (r *GetxattrRequest) String() string {
1183
return fmt.Sprintf("Getxattr [%s] %q %d @%d", &r.Header, r.Name, r.Size, r.Position)
1184
}
@@ -1110,10 +1190,10 @@ func (r *GetxattrRequest) Respond(resp *GetxattrResponse) {
1190
outHeader: outHeader{Unique: uint64(r.ID)},
1191
Size: uint32(len(resp.Xattr)),
1192
}
1113
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1193
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1194
} else {
1195
out := &outHeader{Unique: uint64(r.ID)}
1116
- r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
1196
+ r.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
1197
}
1198
}
1199
@@ -1138,6 +1218,8 @@ type ListxattrRequest struct {
1218
Position uint32 // offset within attribute list
1219
}
1220
1221
+var _ = Request(&ListxattrRequest{})
1222
+
1223
func (r *ListxattrRequest) String() string {
1224
return fmt.Sprintf("Listxattr [%s] %d @%d", &r.Header, r.Size, r.Position)
1225
}
@@ -1149,10 +1231,10 @@ func (r *ListxattrRequest) Respond(resp *ListxattrResponse) {
1231
outHeader: outHeader{Unique: uint64(r.ID)},
1232
Size: uint32(len(resp.Xattr)),
1233
}
1152
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1234
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1235
} else {
1236
out := &outHeader{Unique: uint64(r.ID)}
1155
- r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
1237
+ r.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
1238
}
1239
}
1240
@@ -1179,6 +1261,8 @@ type RemovexattrRequest struct {
1261
Name string // name of extended attribute
1262
}
1263
1264
+var _ = Request(&RemovexattrRequest{})
1265
+
1266
func (r *RemovexattrRequest) String() string {
1267
return fmt.Sprintf("Removexattr [%s] %q", &r.Header, r.Name)
1268
}
@@ -1186,7 +1270,7 @@ func (r *RemovexattrRequest) String() string {
1270
// Respond replies to the request, indicating that the attribute was removed.
1271
func (r *RemovexattrRequest) Respond() {
1272
out := &outHeader{Unique: uint64(r.ID)}
1189
- r.Conn.respond(out, unsafe.Sizeof(*out))
1273
+ r.respond(out, unsafe.Sizeof(*out))
1274
}
1275
1276
func (r *RemovexattrRequest) RespondError(err Error) {
@@ -1219,14 +1303,24 @@ type SetxattrRequest struct {
1303
Xattr []byte
1304
}
1305
1306
+var _ = Request(&SetxattrRequest{})
1307
+
1308
+func trunc(b []byte, max int) ([]byte, string) {
1309
+ if len(b) > max {
1310
+ return b[:max], "..."
1311
+ }
1312
+ return b, ""
1313
+}
1314
+
1315
func (r *SetxattrRequest) String() string {
1223
- return fmt.Sprintf("Setxattr [%s] %q %x fl=%v @%#x", &r.Header, r.Name, r.Xattr, r.Flags, r.Position)
1316
+ xattr, tail := trunc(r.Xattr, 16)
1317
+ return fmt.Sprintf("Setxattr [%s] %q %x%s fl=%v @%#x", &r.Header, r.Name, xattr, tail, r.Flags, r.Position)
1318
}
1319
1320
// Respond replies to the request, indicating that the extended attribute was set.
1321
func (r *SetxattrRequest) Respond() {
1322
out := &outHeader{Unique: uint64(r.ID)}
1229
- r.Conn.respond(out, unsafe.Sizeof(*out))
1323
+ r.respond(out, unsafe.Sizeof(*out))
1324
}
1325
1326
func (r *SetxattrRequest) RespondError(err Error) {
@@ -1240,6 +1334,8 @@ type LookupRequest struct {
1334
Name string
1335
}
1336
1337
+var _ = Request(&LookupRequest{})
1338
+
1339
func (r *LookupRequest) String() string {
1340
return fmt.Sprintf("Lookup [%s] %q", &r.Header, r.Name)
1341
}
@@ -1256,7 +1352,7 @@ func (r *LookupRequest) Respond(resp *LookupResponse) {
1352
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
1353
Attr: resp.Attr.attr(),
1354
}
1259
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1355
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1356
}
1357
1358
// A LookupResponse is the response to a LookupRequest.
@@ -1279,6 +1375,8 @@ type OpenRequest struct {
1375
Flags OpenFlags
1376
}
1377
1378
+var _ = Request(&OpenRequest{})
1379
+
1380
func (r *OpenRequest) String() string {
1381
return fmt.Sprintf("Open [%s] dir=%v fl=%v", &r.Header, r.Dir, r.Flags)
1382
}
@@ -1290,7 +1388,7 @@ func (r *OpenRequest) Respond(resp *OpenResponse) {
1388
Fh: uint64(resp.Handle),
1389
OpenFlags: uint32(resp.Flags),
1390
}
1293
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1391
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1392
}
1393
1394
// A OpenResponse is the response to a OpenRequest.
@@ -1311,6 +1409,8 @@ type CreateRequest struct {
1409
Mode os.FileMode
1410
}
1411
1412
+var _ = Request(&CreateRequest{})
1413
+
1414
func (r *CreateRequest) String() string {
1415
return fmt.Sprintf("Create [%s] %q fl=%v mode=%v", &r.Header, r.Name, r.Flags, r.Mode)
1416
}
@@ -1331,7 +1431,7 @@ func (r *CreateRequest) Respond(resp *CreateResponse) {
1431
Fh: uint64(resp.Handle),
1432
OpenFlags: uint32(resp.Flags),
1433
}
1334
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1434
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1435
}
1436
1437
// A CreateResponse is the response to a CreateRequest.
@@ -1352,6 +1452,8 @@ type MkdirRequest struct {
1452
Mode os.FileMode
1453
}
1454
1455
+var _ = Request(&MkdirRequest{})
1456
+
1457
func (r *MkdirRequest) String() string {
1458
return fmt.Sprintf("Mkdir [%s] %q mode=%v", &r.Header, r.Name, r.Mode)
1459
}
@@ -1368,7 +1470,7 @@ func (r *MkdirRequest) Respond(resp *MkdirResponse) {
1470
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
1471
Attr: resp.Attr.attr(),
1472
}
1371
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1473
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1474
}
1475
1476
// A MkdirResponse is the response to a MkdirRequest.
@@ -1389,6 +1491,8 @@ type ReadRequest struct {
1491
Size int
1492
}
1493
1494
+var _ = Request(&ReadRequest{})
1495
+
1496
func (r *ReadRequest) String() string {
1497
return fmt.Sprintf("Read [%s] %#x %d @%#x dir=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir)
1498
}
@@ -1396,7 +1500,7 @@ func (r *ReadRequest) String() string {
1500
// Respond replies to the request with the given response.
1501
func (r *ReadRequest) Respond(resp *ReadResponse) {
1502
out := &outHeader{Unique: uint64(r.ID)}
1399
- r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Data)
1503
+ r.respondData(out, unsafe.Sizeof(*out), resp.Data)
1504
}
1505
1506
// A ReadResponse is the response to a ReadRequest.
@@ -1429,6 +1533,8 @@ type ReleaseRequest struct {
1533
LockOwner uint32
1534
}
1535
1536
+var _ = Request(&ReleaseRequest{})
1537
+
1538
func (r *ReleaseRequest) String() string {
1539
return fmt.Sprintf("Release [%s] %#x fl=%v rfl=%v owner=%#x", &r.Header, r.Handle, r.Flags, r.ReleaseFlags, r.LockOwner)
1540
}
@@ -1436,7 +1542,7 @@ func (r *ReleaseRequest) String() string {
1542
// Respond replies to the request, indicating that the handle has been released.
1543
func (r *ReleaseRequest) Respond() {
1544
out := &outHeader{Unique: uint64(r.ID)}
1439
- r.Conn.respond(out, unsafe.Sizeof(*out))
1545
+ r.respond(out, unsafe.Sizeof(*out))
1546
}
1547
1548
// A DestroyRequest is sent by the kernel when unmounting the file system.
@@ -1446,6 +1552,8 @@ type DestroyRequest struct {
1552
Header `json:"-"`
1553
}
1554
1555
+var _ = Request(&DestroyRequest{})
1556
+
1557
func (r *DestroyRequest) String() string {
1558
return fmt.Sprintf("Destroy [%s]", &r.Header)
1559
}
@@ -1453,7 +1561,7 @@ func (r *DestroyRequest) String() string {
1561
// Respond replies to the request.
1562
func (r *DestroyRequest) Respond() {
1563
out := &outHeader{Unique: uint64(r.ID)}
1456
- r.Conn.respond(out, unsafe.Sizeof(*out))
1564
+ r.respond(out, unsafe.Sizeof(*out))
1565
}
1566
1567
// A ForgetRequest is sent by the kernel when forgetting about r.Node
@@ -1463,6 +1571,8 @@ type ForgetRequest struct {
1571
N uint64
1572
}
1573
1574
+var _ = Request(&ForgetRequest{})
1575
+
1576
func (r *ForgetRequest) String() string {
1577
return fmt.Sprintf("Forget [%s] %d", &r.Header, r.N)
1578
}
@@ -1470,6 +1580,7 @@ func (r *ForgetRequest) String() string {
1580
// Respond replies to the request, indicating that the forgetfulness has been recorded.
1581
func (r *ForgetRequest) Respond() {
1582
// Don't reply to forget messages.
1583
+ r.noResponse()
1584
}
1585
1586
// A Dirent represents a single directory entry.
@@ -1562,6 +1673,8 @@ type WriteRequest struct {
1673
Flags WriteFlags
1674
}
1675
1676
+var _ = Request(&WriteRequest{})
1677
+
1678
func (r *WriteRequest) String() string {
1679
return fmt.Sprintf("Write [%s] %#x %d @%d fl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags)
1680
}
@@ -1589,7 +1702,7 @@ func (r *WriteRequest) Respond(resp *WriteResponse) {
1702
outHeader: outHeader{Unique: uint64(r.ID)},
1703
Size: uint32(resp.Size),
1704
}
1592
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1705
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1706
}
1707
1708
// A WriteResponse replies to a write indicating how many bytes were written.
@@ -1621,6 +1734,8 @@ type SetattrRequest struct {
1734
Flags uint32 // see chflags(2)
1735
}
1736
1737
+var _ = Request(&SetattrRequest{})
1738
+
1739
func (r *SetattrRequest) String() string {
1740
var buf bytes.Buffer
1741
fmt.Fprintf(&buf, "Setattr [%s]", &r.Header)
@@ -1680,7 +1795,7 @@ func (r *SetattrRequest) Respond(resp *SetattrResponse) {
1795
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
1796
Attr: resp.Attr.attr(),
1797
}
1683
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1798
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1799
}
1800
1801
// A SetattrResponse is the response to a SetattrRequest.
@@ -1703,6 +1818,8 @@ type FlushRequest struct {
1818
LockOwner uint64
1819
}
1820
1821
+var _ = Request(&FlushRequest{})
1822
+
1823
func (r *FlushRequest) String() string {
1824
return fmt.Sprintf("Flush [%s] %#x fl=%#x lk=%#x", &r.Header, r.Handle, r.Flags, r.LockOwner)
1825
}
@@ -1710,16 +1827,19 @@ func (r *FlushRequest) String() string {
1827
// Respond replies to the request, indicating that the flush succeeded.
1828
func (r *FlushRequest) Respond() {
1829
out := &outHeader{Unique: uint64(r.ID)}
1713
- r.Conn.respond(out, unsafe.Sizeof(*out))
1830
+ r.respond(out, unsafe.Sizeof(*out))
1831
}
1832
1716
-// A RemoveRequest asks to remove a file or directory.
1833
+// A RemoveRequest asks to remove a file or directory from the
1834
+// directory r.Node.
1835
type RemoveRequest struct {
1836
Header `json:"-"`
1719
- Name string // name of extended attribute
1837
+ Name string // name of the entry to remove
1838
Dir bool // is this rmdir?
1839
}
1840
1841
+var _ = Request(&RemoveRequest{})
1842
+
1843
func (r *RemoveRequest) String() string {
1844
return fmt.Sprintf("Remove [%s] %q dir=%v", &r.Header, r.Name, r.Dir)
1845
}
@@ -1727,7 +1847,7 @@ func (r *RemoveRequest) String() string {
1847
// Respond replies to the request, indicating that the file was removed.
1848
func (r *RemoveRequest) Respond() {
1849
out := &outHeader{Unique: uint64(r.ID)}
1730
- r.Conn.respond(out, unsafe.Sizeof(*out))
1850
+ r.respond(out, unsafe.Sizeof(*out))
1851
}
1852
1853
// A SymlinkRequest is a request to create a symlink making NewName point to Target.
@@ -1736,6 +1856,8 @@ type SymlinkRequest struct {
1856
NewName, Target string
1857
}
1858
1859
+var _ = Request(&SymlinkRequest{})
1860
+
1861
func (r *SymlinkRequest) String() string {
1862
return fmt.Sprintf("Symlink [%s] from %q to target %q", &r.Header, r.NewName, r.Target)
1863
}
@@ -1752,7 +1874,7 @@ func (r *SymlinkRequest) Respond(resp *SymlinkResponse) {
1874
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
1875
Attr: resp.Attr.attr(),
1876
}
1755
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1877
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1878
}
1879
1880
// A SymlinkResponse is the response to a SymlinkRequest.
@@ -1765,13 +1887,15 @@ type ReadlinkRequest struct {
1887
Header `json:"-"`
1888
}
1889
1890
+var _ = Request(&ReadlinkRequest{})
1891
+
1892
func (r *ReadlinkRequest) String() string {
1893
return fmt.Sprintf("Readlink [%s]", &r.Header)
1894
}
1895
1896
func (r *ReadlinkRequest) Respond(target string) {
1897
out := &outHeader{Unique: uint64(r.ID)}
1774
- r.Conn.respondData(out, unsafe.Sizeof(*out), []byte(target))
1898
+ r.respondData(out, unsafe.Sizeof(*out), []byte(target))
1899
}
1900
1901
// A LinkRequest is a request to create a hard link.
@@ -1781,6 +1905,8 @@ type LinkRequest struct {
1905
NewName string
1906
}
1907
1908
+var _ = Request(&LinkRequest{})
1909
+
1910
func (r *LinkRequest) String() string {
1911
return fmt.Sprintf("Link [%s] node %d to %q", &r.Header, r.OldNode, r.NewName)
1912
}
@@ -1796,7 +1922,7 @@ func (r *LinkRequest) Respond(resp *LookupResponse) {
1922
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
1923
Attr: resp.Attr.attr(),
1924
}
1799
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1925
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1926
}
1927
1928
// A RenameRequest is a request to rename a file.
@@ -1806,13 +1932,15 @@ type RenameRequest struct {
1932
OldName, NewName string
1933
}
1934
1935
+var _ = Request(&RenameRequest{})
1936
+
1937
func (r *RenameRequest) String() string {
1938
return fmt.Sprintf("Rename [%s] from %q to dirnode %d %q", &r.Header, r.OldName, r.NewDir, r.NewName)
1939
}
1940
1941
func (r *RenameRequest) Respond() {
1942
out := &outHeader{Unique: uint64(r.ID)}
1815
- r.Conn.respond(out, unsafe.Sizeof(*out))
1943
+ r.respond(out, unsafe.Sizeof(*out))
1944
}
1945
1946
type MknodRequest struct {
@@ -1822,6 +1950,8 @@ type MknodRequest struct {
1950
Rdev uint32
1951
}
1952
1953
+var _ = Request(&MknodRequest{})
1954
+
1955
func (r *MknodRequest) String() string {
1956
return fmt.Sprintf("Mknod [%s] Name %q mode %v rdev %d", &r.Header, r.Name, r.Mode, r.Rdev)
1957
}
@@ -1837,7 +1967,7 @@ func (r *MknodRequest) Respond(resp *LookupResponse) {
1967
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
1968
Attr: resp.Attr.attr(),
1969
}
1840
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
1970
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
1971
}
1972
1973
type FsyncRequest struct {
@@ -1848,13 +1978,15 @@ type FsyncRequest struct {
1978
Dir bool
1979
}
1980
1981
+var _ = Request(&FsyncRequest{})
1982
+
1983
func (r *FsyncRequest) String() string {
1984
return fmt.Sprintf("Fsync [%s] Handle %v Flags %v", &r.Header, r.Handle, r.Flags)
1985
}
1986
1987
func (r *FsyncRequest) Respond() {
1988
out := &outHeader{Unique: uint64(r.ID)}
1857
- r.Conn.respond(out, unsafe.Sizeof(*out))
1989
+ r.respond(out, unsafe.Sizeof(*out))
1990
}
1991
1992
// An InterruptRequest is a request to interrupt another pending request. The
@@ -1864,8 +1996,11 @@ type InterruptRequest struct {
1996
IntrID RequestID // ID of the request to be interrupt.
1997
}
1998
1999
+var _ = Request(&InterruptRequest{})
2000
+
2001
func (r *InterruptRequest) Respond() {
2002
// nothing to do here
2003
+ r.noResponse()
2004
}
2005
2006
func (r *InterruptRequest) String() string {
@@ -1880,6 +2015,8 @@ type XXXRequest struct {
2015
xxx
2016
}
2017
2018
+var _ = Request(&XXXRequest{})
2019
+
2020
func (r *XXXRequest) String() string {
2021
return fmt.Sprintf("XXX [%s] xxx", &r.Header)
2022
}
@@ -1890,7 +2027,7 @@ func (r *XXXRequest) Respond(resp *XXXResponse) {
2027
outHeader: outHeader{Unique: uint64(r.ID)},
2028
xxx,
2029
}
1893
- r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
2030
+ r.respond(&out.outHeader, unsafe.Sizeof(*out))
2031
}
2032
2033
// A XXXResponse is the response to a XXXRequest.
Godeps/_workspace/src/bazil.org/fuse/fuse_kernel.go
+1
-1
@@ -513,7 +513,7 @@ type writeOut struct {
513
Padding uint32
514
}
515
516
-// The WriteFlags are returned in the WriteResponse.
516
+// The WriteFlags are passed in WriteRequest.
517
type WriteFlags uint32
518
519
func (fl WriteFlags) String() string {
Godeps/_workspace/src/bazil.org/fuse/hellofs/hello.go
+7
-1
@@ -28,7 +28,13 @@ func main() {
28
}
29
mountpoint := flag.Arg(0)
30
31
- c, err := fuse.Mount(mountpoint)
31
+ c, err := fuse.Mount(
32
+ mountpoint,
33
+ fuse.FSName("helloworld"),
34
+ fuse.Subtype("hellofs"),
35
+ fuse.LocalVolume(),
36
+ fuse.VolumeName("Hello world!"),
37
+ )
38
if err != nil {
39
log.Fatal(err)
40
}
Godeps/_workspace/src/bazil.org/fuse/hellofs/hellofs
Binary files /dev/null and b/Godeps/_workspace/src/bazil.org/fuse/hellofs/hellofs differ
Godeps/_workspace/src/bazil.org/fuse/mount_darwin.go
+14
-5
@@ -3,9 +3,11 @@ package fuse
3
import (
4
"bytes"
5
"errors"
6
+ "fmt"
7
"os"
8
"os/exec"
9
"strconv"
10
+ "strings"
11
"syscall"
12
)
13
@@ -50,15 +52,22 @@ func openOSXFUSEDev() (*os.File, error) {
52
}
53
}
54
53
-func callMount(dir string, f *os.File, ready chan<- struct{}, errp *error) error {
55
+func callMount(dir string, conf *MountConfig, f *os.File, ready chan<- struct{}, errp *error) error {
56
bin := "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
57
+
58
+ for k, v := range conf.options {
59
+ if strings.Contains(k, ",") || strings.Contains(v, ",") {
60
+ // Silly limitation but the mount helper does not
61
+ // understand any escaping. See TestMountOptionCommaError.
62
+ return fmt.Errorf("mount options cannot contain commas on OS X: %q=%q", k, v)
63
+ }
64
+ }
65
cmd := exec.Command(
66
bin,
67
+ "-o", conf.getOptions(),
68
// Tell osxfuse-kext how large our buffer is. It must split
69
// writes larger than this into multiple writes.
70
//
60
- // TODO add buffer reuse, bump this up significantly
61
- //
71
// OSXFUSE seems to ignore InitResponse.MaxWrite, and uses
72
// this instead.
73
"-o", "iosize="+strconv.FormatUint(maxWrite, 10),
@@ -95,7 +104,7 @@ func callMount(dir string, f *os.File, ready chan<- struct{}, errp *error) error
104
return err
105
}
106
98
-func mount(dir string, ready chan<- struct{}, errp *error) (*os.File, error) {
107
+func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
108
f, err := openOSXFUSEDev()
109
if err == errNotLoaded {
110
err = loadOSXFUSE()
@@ -108,7 +117,7 @@ func mount(dir string, ready chan<- struct{}, errp *error) (*os.File, error) {
117
if err != nil {
118
return nil, err
119
}
111
- err = callMount(dir, f, ready, errp)
120
+ err = callMount(dir, conf, f, ready, errp)
121
if err != nil {
122
f.Close()
123
return nil, err
Godeps/_workspace/src/bazil.org/fuse/mount_linux.go
+7
-2
@@ -8,7 +8,7 @@ import (
8
"syscall"
9
)
10
11
-func mount(dir string, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
11
+func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
12
// linux mount is never delayed
13
close(ready)
14
@@ -19,7 +19,12 @@ func mount(dir string, ready chan<- struct{}, errp *error) (fusefd *os.File, err
19
defer syscall.Close(fds[0])
20
defer syscall.Close(fds[1])
21
22
- cmd := exec.Command("fusermount", "--", dir)
22
+ cmd := exec.Command(
23
+ "fusermount",
24
+ "-o", conf.getOptions(),
25
+ "--",
26
+ dir,
27
+ )
28
cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3")
29
30
writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")
Godeps/_workspace/src/bazil.org/fuse/options.go
new
+100
@@ -0,0 +1,100 @@
1
+package fuse
2
+
3
+import (
4
+ "errors"
5
+ "strings"
6
+)
7
+
8
+// MountConfig holds the configuration for a mount operation.
9
+// Use it by passing MountOption values to Mount.
10
+type MountConfig struct {
11
+ options map[string]string
12
+}
13
+
14
+func escapeComma(s string) string {
15
+ s = strings.Replace(s, `\`, `\\`, -1)
16
+ s = strings.Replace(s, `,`, `\,`, -1)
17
+ return s
18
+}
19
+
20
+// getOptions makes a string of options suitable for passing to FUSE
21
+// mount flag `-o`. Returns an empty string if no options were set.
22
+// Any platform specific adjustments should happen before the call.
23
+func (m *MountConfig) getOptions() string {
24
+ var opts []string
25
+ for k, v := range m.options {
26
+ k = escapeComma(k)
27
+ if v != "" {
28
+ k += "=" + escapeComma(v)
29
+ }
30
+ opts = append(opts, k)
31
+ }
32
+ return strings.Join(opts, ",")
33
+}
34
+
35
+// MountOption is passed to Mount to change the behavior of the mount.
36
+type MountOption func(*MountConfig) error
37
+
38
+// FSName sets the file system name (also called source) that is
39
+// visible in the list of mounted file systems.
40
+func FSName(name string) MountOption {
41
+ return func(conf *MountConfig) error {
42
+ conf.options["fsname"] = name
43
+ return nil
44
+ }
45
+}
46
+
47
+// Subtype sets the subtype of the mount. The main type is always
48
+// `fuse`. The type in a list of mounted file systems will look like
49
+// `fuse.foo`.
50
+//
51
+// OS X ignores this option.
52
+func Subtype(fstype string) MountOption {
53
+ return func(conf *MountConfig) error {
54
+ conf.options["subtype"] = fstype
55
+ return nil
56
+ }
57
+}
58
+
59
+// LocalVolume sets the volume to be local (instead of network),
60
+// changing the behavior of Finder, Spotlight, and such.
61
+//
62
+// OS X only. Others ignore this option.
63
+func LocalVolume() MountOption {
64
+ return localVolume
65
+}
66
+
67
+// VolumeName sets the volume name shown in Finder.
68
+//
69
+// OS X only. Others ignore this option.
70
+func VolumeName(name string) MountOption {
71
+ return volumeName(name)
72
+}
73
+
74
+var ErrCannotCombineAllowOtherAndAllowRoot = errors.New("cannot combine AllowOther and AllowRoot")
75
+
76
+// AllowOther allows other users to access the file system.
77
+//
78
+// Only one of AllowOther or AllowRoot can be used.
79
+func AllowOther() MountOption {
80
+ return func(conf *MountConfig) error {
81
+ if _, ok := conf.options["allow_root"]; ok {
82
+ return ErrCannotCombineAllowOtherAndAllowRoot
83
+ }
84
+ conf.options["allow_other"] = ""
85
+ return nil
86
+ }
87
+}
88
+
89
+// AllowRoot allows other users to access the file system.
90
+//
91
+// Only one of AllowOther or AllowRoot can be used.
92
+func AllowRoot() MountOption {
93
+ return func(conf *MountConfig) error {
94
+ if _, ok := conf.options["allow_other"]; ok {
95
+ return ErrCannotCombineAllowOtherAndAllowRoot
96
+ }
97
+ conf.options["allow_root"] = ""
98
+ return nil
99
+ }
100
+}
Godeps/_workspace/src/bazil.org/fuse/options_darwin.go
new
+13
@@ -0,0 +1,13 @@
1
+package fuse
2
+
3
+func localVolume(conf *MountConfig) error {
4
+ conf.options["local"] = ""
5
+ return nil
6
+}
7
+
8
+func volumeName(name string) MountOption {
9
+ return func(conf *MountConfig) error {
10
+ conf.options["volname"] = name
11
+ return nil
12
+ }
13
+}
Godeps/_workspace/src/bazil.org/fuse/options_darwin_test.go
new
+27
@@ -0,0 +1,27 @@
1
+package fuse_test
2
+
3
+import (
4
+ "testing"
5
+
6
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
8
+)
9
+
10
+func TestMountOptionCommaError(t *testing.T) {
11
+ t.Parallel()
12
+ // this test is not tied to FSName, but needs just some option
13
+ // with string content
14
+ var name = "FuseTest,Marker"
15
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
16
+ fuse.FSName(name),
17
+ )
18
+ switch {
19
+ case err == nil:
20
+ mnt.Close()
21
+ t.Fatal("expected an error about commas")
22
+ case err.Error() == `mount options cannot contain commas on OS X: "fsname"="FuseTest,Marker"`:
23
+ // all good
24
+ default:
25
+ t.Fatalf("expected an error about commas, got: %v", err)
26
+ }
27
+}
Godeps/_workspace/src/bazil.org/fuse/options_linux.go
new
+13
@@ -0,0 +1,13 @@
1
+package fuse
2
+
3
+func dummyOption(conf *MountConfig) error {
4
+ return nil
5
+}
6
+
7
+func localVolume(conf *MountConfig) error {
8
+ return nil
9
+}
10
+
11
+func volumeName(name string) MountOption {
12
+ return dummyOption
13
+}
Godeps/_workspace/src/bazil.org/fuse/options_test.go
new
+141
@@ -0,0 +1,141 @@
1
+package fuse_test
2
+
3
+import (
4
+ "runtime"
5
+ "testing"
6
+
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
8
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
9
+)
10
+
11
+func init() {
12
+ fstestutil.DebugByDefault()
13
+}
14
+
15
+func TestMountOptionFSName(t *testing.T) {
16
+ t.Parallel()
17
+ const name = "FuseTestMarker"
18
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
19
+ fuse.FSName(name),
20
+ )
21
+ if err != nil {
22
+ t.Fatal(err)
23
+ }
24
+ defer mnt.Close()
25
+
26
+ info, err := fstestutil.GetMountInfo(mnt.Dir)
27
+ if err != nil {
28
+ t.Fatal(err)
29
+ }
30
+ if g, e := info.FSName, name; g != e {
31
+ t.Errorf("wrong FSName: %q != %q", g, e)
32
+ }
33
+}
34
+
35
+func testMountOptionFSNameEvil(t *testing.T, evil string) {
36
+ t.Parallel()
37
+ var name = "FuseTest" + evil + "Marker"
38
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
39
+ fuse.FSName(name),
40
+ )
41
+ if err != nil {
42
+ t.Fatal(err)
43
+ }
44
+ defer mnt.Close()
45
+
46
+ info, err := fstestutil.GetMountInfo(mnt.Dir)
47
+ if err != nil {
48
+ t.Fatal(err)
49
+ }
50
+ if g, e := info.FSName, name; g != e {
51
+ t.Errorf("wrong FSName: %q != %q", g, e)
52
+ }
53
+}
54
+
55
+func TestMountOptionFSNameEvilComma(t *testing.T) {
56
+ if runtime.GOOS == "darwin" {
57
+ // see TestMountOptionCommaError for a test that enforces we
58
+ // at least give a nice error, instead of corrupting the mount
59
+ // options
60
+ t.Skip("TODO: OS X gets this wrong, commas in mount options cannot be escaped at all")
61
+ }
62
+ testMountOptionFSNameEvil(t, ",")
63
+}
64
+
65
+func TestMountOptionFSNameEvilSpace(t *testing.T) {
66
+ testMountOptionFSNameEvil(t, " ")
67
+}
68
+
69
+func TestMountOptionFSNameEvilTab(t *testing.T) {
70
+ testMountOptionFSNameEvil(t, "\t")
71
+}
72
+
73
+func TestMountOptionFSNameEvilNewline(t *testing.T) {
74
+ testMountOptionFSNameEvil(t, "\n")
75
+}
76
+
77
+func TestMountOptionFSNameEvilBackslash(t *testing.T) {
78
+ testMountOptionFSNameEvil(t, `\`)
79
+}
80
+
81
+func TestMountOptionFSNameEvilBackslashDouble(t *testing.T) {
82
+ // catch double-unescaping, if it were to happen
83
+ testMountOptionFSNameEvil(t, `\\`)
84
+}
85
+
86
+func TestMountOptionSubtype(t *testing.T) {
87
+ if runtime.GOOS == "darwin" {
88
+ t.Skip("OS X does not support Subtype")
89
+ }
90
+ t.Parallel()
91
+ const name = "FuseTestMarker"
92
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
93
+ fuse.Subtype(name),
94
+ )
95
+ if err != nil {
96
+ t.Fatal(err)
97
+ }
98
+ defer mnt.Close()
99
+
100
+ info, err := fstestutil.GetMountInfo(mnt.Dir)
101
+ if err != nil {
102
+ t.Fatal(err)
103
+ }
104
+ if g, e := info.Type, "fuse."+name; g != e {
105
+ t.Errorf("wrong Subtype: %q != %q", g, e)
106
+ }
107
+}
108
+
109
+// TODO test LocalVolume
110
+
111
+// TODO test AllowOther; hard because needs system-level authorization
112
+
113
+func TestMountOptionAllowOtherThenAllowRoot(t *testing.T) {
114
+ t.Parallel()
115
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
116
+ fuse.AllowOther(),
117
+ fuse.AllowRoot(),
118
+ )
119
+ if err == nil {
120
+ mnt.Close()
121
+ }
122
+ if g, e := err, fuse.ErrCannotCombineAllowOtherAndAllowRoot; g != e {
123
+ t.Fatalf("wrong error: %v != %v", g, e)
124
+ }
125
+}
126
+
127
+// TODO test AllowRoot; hard because needs system-level authorization
128
+
129
+func TestMountOptionAllowRootThenAllowOther(t *testing.T) {
130
+ t.Parallel()
131
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
132
+ fuse.AllowRoot(),
133
+ fuse.AllowOther(),
134
+ )
135
+ if err == nil {
136
+ mnt.Close()
137
+ }
138
+ if g, e := err, fuse.ErrCannotCombineAllowOtherAndAllowRoot; g != e {
139
+ t.Fatalf("wrong error: %v != %v", g, e)
140
+ }
141
+}