From 81a6d3d4661161314846fcfc8933859f962d7420 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:24:16 +1000 Subject: [PATCH 01/14] Declared errorfs.FS and a factory. --- samples/errorfs/error_fs.go | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 samples/errorfs/error_fs.go diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go new file mode 100644 index 0000000..6e1a2e1 --- /dev/null +++ b/samples/errorfs/error_fs.go @@ -0,0 +1,41 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errorfs + +import ( + "reflect" + "syscall" + + "github.com/jacobsa/fuse/fuseutil" +) + +const FooContents = "xxxx" + +// A file system whose sole contents are a file named "foo" containing the +// string defined by FooContents. +// +// The file system can be configured to returned canned errors for particular +// operations using the method SetError. +type FS interface { + fuseutil.FileSystem + + // Cause the file system to return the supplied error for all future + // operations matching the supplied type. + SetError(t reflect.Type, err syscall.Errno) +} + +func New() (fs FS) { + panic("TODO") +} From 4df87deabd7028600a2f54fceac8e61ddbbaafb1 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:27:55 +1000 Subject: [PATCH 02/14] Added a test stub. --- samples/errorfs/error_fs.go | 6 ++-- samples/errorfs/error_fs_test.go | 61 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 samples/errorfs/error_fs_test.go diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index 6e1a2e1..8fe401c 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -15,6 +15,7 @@ package errorfs import ( + "errors" "reflect" "syscall" @@ -36,6 +37,7 @@ type FS interface { SetError(t reflect.Type, err syscall.Errno) } -func New() (fs FS) { - panic("TODO") +func New() (fs FS, err error) { + err = errors.New("TODO") + return } diff --git a/samples/errorfs/error_fs_test.go b/samples/errorfs/error_fs_test.go new file mode 100644 index 0000000..e4f45ef --- /dev/null +++ b/samples/errorfs/error_fs_test.go @@ -0,0 +1,61 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errorfs_test + +import ( + "testing" + + "github.com/jacobsa/fuse/fuseutil" + "github.com/jacobsa/fuse/samples" + "github.com/jacobsa/fuse/samples/errorfs" + . "github.com/jacobsa/ogletest" +) + +func TestErrorFS(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////////////////// +// Boilerplate +//////////////////////////////////////////////////////////////////////// + +type ErrorFSTest struct { + samples.SampleTest + fs errorfs.FS +} + +func init() { RegisterTestSuite(&ErrorFSTest{}) } + +var _ SetUpInterface = &ErrorFSTest{} +var _ TearDownInterface = &ErrorFSTest{} + +func (t *ErrorFSTest) SetUp(ti *TestInfo) { + var err error + + // Create the file system. + t.fs, err = errorfs.New() + AssertEq(nil, err) + + t.Server = fuseutil.NewFileSystemServer(t.fs) + + // Mount it. + t.SampleTest.SetUp(ti) +} + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *ErrorFSTest) DoesFoo() { + AssertTrue(false, "TODO") +} From e1bfc5006b130c3244c2d34ebf0371c992a8af40 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:29:38 +1000 Subject: [PATCH 03/14] Added test names. --- samples/errorfs/error_fs_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/samples/errorfs/error_fs_test.go b/samples/errorfs/error_fs_test.go index e4f45ef..34d13e5 100644 --- a/samples/errorfs/error_fs_test.go +++ b/samples/errorfs/error_fs_test.go @@ -56,6 +56,18 @@ func (t *ErrorFSTest) SetUp(ti *TestInfo) { // Tests //////////////////////////////////////////////////////////////////////// -func (t *ErrorFSTest) DoesFoo() { +func (t *ErrorFSTest) OpenFile() { + AssertTrue(false, "TODO") +} + +func (t *ErrorFSTest) ReadFile() { + AssertTrue(false, "TODO") +} + +func (t *ErrorFSTest) OpenDir() { + AssertTrue(false, "TODO") +} + +func (t *ErrorFSTest) ReadDir() { AssertTrue(false, "TODO") } From 8aecb35541d7a883e800551c101593a3bc678733 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:31:51 +1000 Subject: [PATCH 04/14] ErrorFSTest.OpenFile --- samples/errorfs/error_fs_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/samples/errorfs/error_fs_test.go b/samples/errorfs/error_fs_test.go index 34d13e5..726ffa1 100644 --- a/samples/errorfs/error_fs_test.go +++ b/samples/errorfs/error_fs_test.go @@ -15,11 +15,17 @@ package errorfs_test import ( + "os" + "path" + "reflect" + "syscall" "testing" + "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/fuseutil" "github.com/jacobsa/fuse/samples" "github.com/jacobsa/fuse/samples/errorfs" + . "github.com/jacobsa/oglematchers" . "github.com/jacobsa/ogletest" ) @@ -57,7 +63,10 @@ func (t *ErrorFSTest) SetUp(ti *TestInfo) { //////////////////////////////////////////////////////////////////////// func (t *ErrorFSTest) OpenFile() { - AssertTrue(false, "TODO") + t.fs.SetError(reflect.TypeOf(&fuseops.OpenFileOp{}), syscall.EOWNERDEAD) + + _, err := os.Open(path.Join(t.Dir, "foo")) + ExpectThat(err, Error(HasSubstr("TODO"))) } func (t *ErrorFSTest) ReadFile() { From 978399a2685e32c0aa8a8c97234407d00687e1d0 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:35:59 +1000 Subject: [PATCH 05/14] errorFS.SetError --- samples/errorfs/error_fs.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index 8fe401c..e2f32f5 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -15,8 +15,8 @@ package errorfs import ( - "errors" "reflect" + "sync" "syscall" "github.com/jacobsa/fuse/fuseutil" @@ -38,6 +38,28 @@ type FS interface { } func New() (fs FS, err error) { - err = errors.New("TODO") + fs = &errorFS{ + errors: make(map[string]syscall.Errno), + } + return } + +type errorFS struct { + fuseutil.NotImplementedFileSystem + + mu sync.Mutex + + // Keys are reflect.Type.Name strings. + // + // GUARDED_BY(mu) + errors map[string]syscall.Errno +} + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) SetError(t reflect.Type, err syscall.Errno) { + fs.mu.Lock() + defer fs.mu.Unlock() + + fs.errors[t.Name()] = err +} From c3601bff1c9d739e3d3422ef2e26a5cb3ff660e2 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:40:38 +1000 Subject: [PATCH 06/14] errorFS.GetInodeAttributes --- samples/errorfs/error_fs.go | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index e2f32f5..d9df677 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -15,15 +15,22 @@ package errorfs import ( + "fmt" + "os" "reflect" "sync" "syscall" + "golang.org/x/net/context" + + "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/fuseutil" ) const FooContents = "xxxx" +const fooInodeID = fuseops.RootInodeID + 1 + // A file system whose sole contents are a file named "foo" containing the // string defined by FooContents. // @@ -63,3 +70,47 @@ func (fs *errorFS) SetError(t reflect.Type, err syscall.Errno) { fs.errors[t.Name()] = err } + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) transformError(op interface{}, err *error) bool { + fs.mu.Lock() + defer fs.mu.Unlock() + + var ok bool + *err, ok = fs.errors[reflect.TypeOf(op).Name()] + return ok +} + +//////////////////////////////////////////////////////////////////////// +// File system methods +//////////////////////////////////////////////////////////////////////// + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) GetInodeAttributes( + ctx context.Context, + op *fuseops.GetInodeAttributesOp) (err error) { + if fs.transformError(op, &err) { + return + } + + // Figure out which inode the request is for. + switch { + case op.Inode == fuseops.RootInodeID: + op.Attributes = fuseops.InodeAttributes{ + Mode: os.ModeDir | 0777, + } + + case op.Inode == fooInodeID: + op.Attributes = fuseops.InodeAttributes{ + Nlink: 1, + Size: uint64(len(FooContents)), + Mode: 0444, + } + + default: + err = fmt.Errorf("Unknown inode: %d", op.Inode) + return + } + + return +} From c71b2cbf33326ca368ba774c9a4e9829695dd2af Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:42:51 +1000 Subject: [PATCH 07/14] Fixed two bugs. --- samples/errorfs/error_fs.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index d9df677..a5f4a73 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -46,7 +46,7 @@ type FS interface { func New() (fs FS, err error) { fs = &errorFS{ - errors: make(map[string]syscall.Errno), + errors: make(map[reflect.Type]syscall.Errno), } return @@ -57,10 +57,8 @@ type errorFS struct { mu sync.Mutex - // Keys are reflect.Type.Name strings. - // // GUARDED_BY(mu) - errors map[string]syscall.Errno + errors map[reflect.Type]syscall.Errno } // LOCKS_EXCLUDED(fs.mu) @@ -68,7 +66,7 @@ func (fs *errorFS) SetError(t reflect.Type, err syscall.Errno) { fs.mu.Lock() defer fs.mu.Unlock() - fs.errors[t.Name()] = err + fs.errors[t] = err } // LOCKS_EXCLUDED(fs.mu) @@ -76,9 +74,13 @@ func (fs *errorFS) transformError(op interface{}, err *error) bool { fs.mu.Lock() defer fs.mu.Unlock() - var ok bool - *err, ok = fs.errors[reflect.TypeOf(op).Name()] - return ok + cannedErr, ok := fs.errors[reflect.TypeOf(op)] + if ok { + *err = cannedErr + return true + } + + return false } //////////////////////////////////////////////////////////////////////// From 54db03b63d4d22ec67ce3a5c0c0918a315814df8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:45:51 +1000 Subject: [PATCH 08/14] errorFS.LookUpInode --- samples/errorfs/error_fs.go | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index a5f4a73..7c1a315 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -31,6 +31,12 @@ const FooContents = "xxxx" const fooInodeID = fuseops.RootInodeID + 1 +var fooAttrs = fuseops.InodeAttributes{ + Nlink: 1, + Size: uint64(len(FooContents)), + Mode: 0444, +} + // A file system whose sole contents are a file named "foo" containing the // string defined by FooContents. // @@ -103,11 +109,7 @@ func (fs *errorFS) GetInodeAttributes( } case op.Inode == fooInodeID: - op.Attributes = fuseops.InodeAttributes{ - Nlink: 1, - Size: uint64(len(FooContents)), - Mode: 0444, - } + op.Attributes = fooAttrs default: err = fmt.Errorf("Unknown inode: %d", op.Inode) @@ -116,3 +118,23 @@ func (fs *errorFS) GetInodeAttributes( return } + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) LookUpInode( + ctx context.Context, + op *fuseops.LookUpInodeOp) (err error) { + if fs.transformError(op, &err) { + return + } + + // Is this a known inode? + if !(op.Parent == fuseops.RootInodeID && op.Name == "foo") { + err = syscall.ENOENT + return + } + + op.Entry.Child = fooInodeID + op.Entry.Attributes = fooAttrs + + return +} From 1ea2e8216595e04c7bf062a763a108ee9e8c6dd6 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:48:03 +1000 Subject: [PATCH 09/14] errorFS.OpenFile --- samples/errorfs/error_fs.go | 16 ++++++++++++++++ samples/errorfs/error_fs_test.go | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index 7c1a315..db9e76f 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -138,3 +138,19 @@ func (fs *errorFS) LookUpInode( return } + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) OpenFile( + ctx context.Context, + op *fuseops.OpenFileOp) (err error) { + if fs.transformError(op, &err) { + return + } + + if op.Inode != fooInodeID { + err = fmt.Errorf("Unsupported inode ID: %d", op.Inode) + return + } + + return +} diff --git a/samples/errorfs/error_fs_test.go b/samples/errorfs/error_fs_test.go index 726ffa1..85fb500 100644 --- a/samples/errorfs/error_fs_test.go +++ b/samples/errorfs/error_fs_test.go @@ -66,7 +66,7 @@ func (t *ErrorFSTest) OpenFile() { t.fs.SetError(reflect.TypeOf(&fuseops.OpenFileOp{}), syscall.EOWNERDEAD) _, err := os.Open(path.Join(t.Dir, "foo")) - ExpectThat(err, Error(HasSubstr("TODO"))) + ExpectThat(err, Error(MatchesRegexp("open.*: .*owner died"))) } func (t *ErrorFSTest) ReadFile() { From e5d377b5318eee2eaca98b529d093cb7a967d413 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 08:59:34 +1000 Subject: [PATCH 10/14] ErrorFSTest.ReadFile --- samples/errorfs/error_fs_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/samples/errorfs/error_fs_test.go b/samples/errorfs/error_fs_test.go index 85fb500..b8953d2 100644 --- a/samples/errorfs/error_fs_test.go +++ b/samples/errorfs/error_fs_test.go @@ -15,6 +15,7 @@ package errorfs_test import ( + "io/ioutil" "os" "path" "reflect" @@ -70,7 +71,15 @@ func (t *ErrorFSTest) OpenFile() { } func (t *ErrorFSTest) ReadFile() { - AssertTrue(false, "TODO") + t.fs.SetError(reflect.TypeOf(&fuseops.ReadFileOp{}), syscall.EOWNERDEAD) + + // Open + f, err := os.Open(path.Join(t.Dir, "foo")) + AssertEq(nil, err) + + // Read + _, err = ioutil.ReadAll(f) + ExpectThat(err, Error(MatchesRegexp("read.*: .*owner died"))) } func (t *ErrorFSTest) OpenDir() { From e6a4db9920bc47cd1ddbcb6a454a367d57d9f725 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 09:00:38 +1000 Subject: [PATCH 11/14] errorFS.ReadFile --- samples/errorfs/error_fs.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index db9e76f..8f01f3e 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -154,3 +154,21 @@ func (fs *errorFS) OpenFile( return } + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) ReadFile( + ctx context.Context, + op *fuseops.ReadFileOp) (err error) { + if fs.transformError(op, &err) { + return + } + + if op.Inode != fooInodeID || op.Offset != 0 { + err = fmt.Errorf("Unexpected request: %#v", op) + return + } + + op.BytesRead = copy(op.Dst, FooContents) + + return +} From e22f698dd31d154fe19a978f628c7dd69fc2e0f3 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 09:01:02 +1000 Subject: [PATCH 12/14] ErrorFSTest.OpenDir --- samples/errorfs/error_fs_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/errorfs/error_fs_test.go b/samples/errorfs/error_fs_test.go index b8953d2..666c836 100644 --- a/samples/errorfs/error_fs_test.go +++ b/samples/errorfs/error_fs_test.go @@ -83,7 +83,10 @@ func (t *ErrorFSTest) ReadFile() { } func (t *ErrorFSTest) OpenDir() { - AssertTrue(false, "TODO") + t.fs.SetError(reflect.TypeOf(&fuseops.OpenDirOp{}), syscall.EOWNERDEAD) + + _, err := os.Open(t.Dir) + ExpectThat(err, Error(MatchesRegexp("open.*: .*owner died"))) } func (t *ErrorFSTest) ReadDir() { From 7943ab5bf4a3775f5d7e1c3552da5ad084e52c7f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 09:01:53 +1000 Subject: [PATCH 13/14] ErrorFSTest.ReadDir --- samples/errorfs/error_fs_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/samples/errorfs/error_fs_test.go b/samples/errorfs/error_fs_test.go index 666c836..063a44c 100644 --- a/samples/errorfs/error_fs_test.go +++ b/samples/errorfs/error_fs_test.go @@ -90,5 +90,13 @@ func (t *ErrorFSTest) OpenDir() { } func (t *ErrorFSTest) ReadDir() { - AssertTrue(false, "TODO") + t.fs.SetError(reflect.TypeOf(&fuseops.ReadDirOp{}), syscall.EOWNERDEAD) + + // Open + f, err := os.Open(t.Dir) + AssertEq(nil, err) + + // Read + _, err = f.Readdirnames(1) + ExpectThat(err, Error(MatchesRegexp("read.*: .*owner died"))) } From ad3a4634880e760fa7ce8f7a316b3daff63a0f07 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 4 Aug 2015 09:03:22 +1000 Subject: [PATCH 14/14] Implemented dir methods. --- samples/errorfs/error_fs.go | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/samples/errorfs/error_fs.go b/samples/errorfs/error_fs.go index 8f01f3e..359af08 100644 --- a/samples/errorfs/error_fs.go +++ b/samples/errorfs/error_fs.go @@ -172,3 +172,44 @@ func (fs *errorFS) ReadFile( return } + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) OpenDir( + ctx context.Context, + op *fuseops.OpenDirOp) (err error) { + if fs.transformError(op, &err) { + return + } + + if op.Inode != fuseops.RootInodeID { + err = fmt.Errorf("Unsupported inode ID: %d", op.Inode) + return + } + + return +} + +// LOCKS_EXCLUDED(fs.mu) +func (fs *errorFS) ReadDir( + ctx context.Context, + op *fuseops.ReadDirOp) (err error) { + if fs.transformError(op, &err) { + return + } + + if op.Inode != fuseops.RootInodeID || op.Offset != 0 { + err = fmt.Errorf("Unexpected request: %#v", op) + return + } + + op.BytesRead = fuseutil.WriteDirent( + op.Dst, + fuseutil.Dirent{ + Offset: 0, + Inode: fooInodeID, + Name: "foo", + Type: fuseutil.DT_File, + }) + + return +}