From 2a57c26cec59d874ab3ca3468f60243d15efe333 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 10:32:25 +1100 Subject: [PATCH 01/25] Began on a file system to help test flush/fsync support. --- samples/flushfs/flush_fs.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 samples/flushfs/flush_fs.go diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go new file mode 100644 index 0000000..6a2416b --- /dev/null +++ b/samples/flushfs/flush_fs.go @@ -0,0 +1,26 @@ +// 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 flushfs + +import "github.com/jacobsa/fuse" + +// Create a file system containing a single file named "foo". +// +// The file may be opened for reading and/or writing. Its initial contents are +// empty. Whenever a flush or fsync is received, the supplied function will be +// called with the current contents of the file. +func NewFileSystem( + reportFlush func([]byte), + reportFsync func([]byte)) (fs fuse.FileSystem, err error) From 76f61fa467cbe8cffa020a044ffcc99610c1f9c9 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:17:05 +1100 Subject: [PATCH 02/25] Added a test stub. --- samples/flushfs/flush_fs_test.go | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 samples/flushfs/flush_fs_test.go diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go new file mode 100644 index 0000000..b21cf0b --- /dev/null +++ b/samples/flushfs/flush_fs_test.go @@ -0,0 +1,47 @@ +// 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 flushfs_test + +import ( + "testing" + + "github.com/jacobsa/fuse/samples" + . "github.com/jacobsa/ogletest" +) + +func TestFlushFS(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////////////////// +// Boilerplate +//////////////////////////////////////////////////////////////////////// + +type FlushFSTest struct { + samples.SampleTest + + flushes []string + fsyncs []string +} + +func init() { RegisterTestSuite(&FlushFSTest{}) } + +func (t *FlushFSTest) SetUp(ti *TestInfo) + +//////////////////////////////////////////////////////////////////////// +// Test functions +//////////////////////////////////////////////////////////////////////// + +func (t *FlushFSTest) DoesFoo() { + AssertTrue(false, "TODO") +} From ffa094be50fda6f05da03c8906e7bc68e524bf48 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:18:56 +1100 Subject: [PATCH 03/25] Switched to string for fool-proofness. --- samples/flushfs/flush_fs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index 6a2416b..ae5875c 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -22,5 +22,5 @@ import "github.com/jacobsa/fuse" // empty. Whenever a flush or fsync is received, the supplied function will be // called with the current contents of the file. func NewFileSystem( - reportFlush func([]byte), - reportFsync func([]byte)) (fs fuse.FileSystem, err error) + reportFlush func(string), + reportFsync func(string)) (fs fuse.FileSystem, err error) From 2fe7597c4ef9d0b1f2d039dba9376bfb334a5add Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:20:29 +1100 Subject: [PATCH 04/25] FlushFSTest.SetUp --- samples/flushfs/flush_fs_test.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index b21cf0b..aaa5998 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -15,9 +15,11 @@ package flushfs_test import ( + "sync" "testing" "github.com/jacobsa/fuse/samples" + "github.com/jacobsa/fuse/samples/flushfs" . "github.com/jacobsa/ogletest" ) @@ -30,13 +32,31 @@ func TestFlushFS(t *testing.T) { RunTests(t) } type FlushFSTest struct { samples.SampleTest + mu sync.Mutex + + // GUARDED_BY(mu) flushes []string - fsyncs []string + + // GUARDED_BY(mu) + fsyncs []string } func init() { RegisterTestSuite(&FlushFSTest{}) } -func (t *FlushFSTest) SetUp(ti *TestInfo) +func (t *FlushFSTest) SetUp(ti *TestInfo) { + // Set up a file system. + reportTo := func(slice *[]string) func(string) { + return func(s string) { + t.mu.Lock() + defer t.mu.Unlock() + *slice = append(*slice, s) + } + } + + t.FileSystem = flushfs.NewFileSystem( + reportTo(&t.flushes), + reportTo(&t.fsyncs)) +} //////////////////////////////////////////////////////////////////////// // Test functions From e1375f33da2ce400e27b1bdcb6e32909dff3f5a9 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:21:38 +1100 Subject: [PATCH 05/25] NewFileSystem --- samples/flushfs/flush_fs.go | 14 ++++++++++++-- samples/flushfs/flush_fs_test.go | 11 ++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index ae5875c..4f48bc5 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -14,7 +14,10 @@ package flushfs -import "github.com/jacobsa/fuse" +import ( + "github.com/jacobsa/fuse" + "github.com/jacobsa/fuse/fuseutil" +) // Create a file system containing a single file named "foo". // @@ -23,4 +26,11 @@ import "github.com/jacobsa/fuse" // called with the current contents of the file. func NewFileSystem( reportFlush func(string), - reportFsync func(string)) (fs fuse.FileSystem, err error) + reportFsync func(string)) (fs fuse.FileSystem, err error) { + fs = &flushFS{} + return +} + +type flushFS struct { + fuseutil.NotImplementedFileSystem +} diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index aaa5998..694669f 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -44,6 +44,8 @@ type FlushFSTest struct { func init() { RegisterTestSuite(&FlushFSTest{}) } func (t *FlushFSTest) SetUp(ti *TestInfo) { + var err error + // Set up a file system. reportTo := func(slice *[]string) func(string) { return func(s string) { @@ -53,9 +55,16 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { } } - t.FileSystem = flushfs.NewFileSystem( + t.FileSystem, err = flushfs.NewFileSystem( reportTo(&t.flushes), reportTo(&t.fsyncs)) + + if err != nil { + panic(err) + } + + // Mount it. + t.SampleTest.SetUp(ti) } //////////////////////////////////////////////////////////////////////// From 20b5ac76344433e52faefebe1b8f74cc8191a77e Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:22:16 +1100 Subject: [PATCH 06/25] flushFS.Init --- samples/flushfs/flush_fs.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index 4f48bc5..07e492f 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -17,6 +17,7 @@ package flushfs import ( "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fuseutil" + "golang.org/x/net/context" ) // Create a file system containing a single file named "foo". @@ -34,3 +35,11 @@ func NewFileSystem( type flushFS struct { fuseutil.NotImplementedFileSystem } + +func (fs *flushFS) Init( + ctx context.Context, + req *fuse.InitRequest) ( + resp *fuse.InitResponse, err error) { + resp = &fuse.InitResponse{} + return +} From 87a08d4388b2e2ff9efc3711f47929f5a175629c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:23:15 +1100 Subject: [PATCH 07/25] Added some test names. --- samples/flushfs/flush_fs_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 694669f..fdb1b8e 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -71,6 +71,14 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { // Test functions //////////////////////////////////////////////////////////////////////// -func (t *FlushFSTest) DoesFoo() { +func (t *FlushFSTest) FlushesOnClose_NonOverlappingFileHandles() { + AssertTrue(false, "TODO") +} + +func (t *FlushFSTest) FlushesOnClose_OverlappingFileHandles() { + AssertTrue(false, "TODO") +} + +func (t *FlushFSTest) FlushesOnFsync() { AssertTrue(false, "TODO") } From d6eb065a4d019214fec54e4edbfa54f3c1e70908 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:25:15 +1100 Subject: [PATCH 08/25] Set up more test structure. --- samples/flushfs/flush_fs_test.go | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index fdb1b8e..6c8eca4 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -68,17 +68,39 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { } //////////////////////////////////////////////////////////////////////// -// Test functions +// Helpers //////////////////////////////////////////////////////////////////////// -func (t *FlushFSTest) FlushesOnClose_NonOverlappingFileHandles() { +// Return a copy of the current contents of t.flushes. +// +// LOCKS_EXCLUDED(t.mu) +func (t *FlushFSTest) getFlushes() []string + +// Return a copy of the current contents of t.fsyncs. +// +// LOCKS_EXCLUDED(t.mu) +func (t *FlushFSTest) getFsyncs() []string + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *FlushFSTest) Close_NonOverlappingFileHandles() { AssertTrue(false, "TODO") } -func (t *FlushFSTest) FlushesOnClose_OverlappingFileHandles() { +func (t *FlushFSTest) Close_OverlappingFileHandles() { AssertTrue(false, "TODO") } -func (t *FlushFSTest) FlushesOnFsync() { +func (t *FlushFSTest) Close_ReadOnly() { + AssertTrue(false, "TODO") +} + +func (t *FlushFSTest) Close_WriteOnly() { + AssertTrue(false, "TODO") +} + +func (t *FlushFSTest) Fsync() { AssertTrue(false, "TODO") } From dfa04520035368ac4c97a55aaf9f005d0a2da3f9 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:26:32 +1100 Subject: [PATCH 09/25] Implemented helpers. --- samples/flushfs/flush_fs_test.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 6c8eca4..d1b0460 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -74,12 +74,26 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { // Return a copy of the current contents of t.flushes. // // LOCKS_EXCLUDED(t.mu) -func (t *FlushFSTest) getFlushes() []string +func (t *FlushFSTest) getFlushes() (p []string) { + t.mu.Lock() + defer t.mu.Unlock() + + p = make([]string, len(t.flushes)) + copy(p, t.flushes) + return +} // Return a copy of the current contents of t.fsyncs. // // LOCKS_EXCLUDED(t.mu) -func (t *FlushFSTest) getFsyncs() []string +func (t *FlushFSTest) getFsyncs() (p []string) { + t.mu.Lock() + defer t.mu.Unlock() + + p = make([]string, len(t.fsyncs)) + copy(p, t.fsyncs) + return +} //////////////////////////////////////////////////////////////////////// // Tests From 1c062d495e6399d428b58a636b710ae2120548e7 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:33:15 +1100 Subject: [PATCH 10/25] FlushFSTest.CloseReports_ReadWrite --- samples/flushfs/flush_fs_test.go | 66 +++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index d1b0460..9913d0e 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -15,11 +15,15 @@ package flushfs_test import ( + "io" + "os" + "path" "sync" "testing" "github.com/jacobsa/fuse/samples" "github.com/jacobsa/fuse/samples/flushfs" + . "github.com/jacobsa/oglematchers" . "github.com/jacobsa/ogletest" ) @@ -99,22 +103,74 @@ func (t *FlushFSTest) getFsyncs() (p []string) { // Tests //////////////////////////////////////////////////////////////////////// -func (t *FlushFSTest) Close_NonOverlappingFileHandles() { +func (t *FlushFSTest) CloseReports_ReadWrite() { + var n int + var off int64 + var err error + buf := make([]byte, 1024) + + // Open the file. + f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0) + AssertEq(nil, err) + + defer func() { + if f != nil { + ExpectEq(nil, f.Close()) + } + }() + + // Write some contents to the file. + n, err = f.Write([]byte("taco")) + AssertEq(nil, err) + AssertEq(4, n) + + // Seek and read them back. + off, err = f.Seek(0, 0) + AssertEq(nil, err) + AssertEq(4, off) + + n, err = f.Read(buf) + AssertThat(err, AnyOf(nil, io.EOF)) + AssertEq("taco", buf[:n]) + + // At this point, no flushes or fsyncs should have happened. + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Close the file. + err = f.Close() + f = nil + AssertEq(nil, err) + + // Now we should have received the flush operation (but still no fsync). + ExpectThat(t.getFlushes(), ElementsAre("taco")) + ExpectThat(t.getFsyncs(), ElementsAre()) +} + +func (t *FlushFSTest) CloseReports_ReadOnly() { AssertTrue(false, "TODO") } -func (t *FlushFSTest) Close_OverlappingFileHandles() { +func (t *FlushFSTest) CloseReports_WriteOnly() { AssertTrue(false, "TODO") } -func (t *FlushFSTest) Close_ReadOnly() { +func (t *FlushFSTest) CloseReports_MultipleTimes_NonOverlappingFileHandles() { AssertTrue(false, "TODO") } -func (t *FlushFSTest) Close_WriteOnly() { +func (t *FlushFSTest) CloseReports_MultipleTimes_OverlappingFileHandles() { AssertTrue(false, "TODO") } -func (t *FlushFSTest) Fsync() { +func (t *FlushFSTest) CloseError() { + AssertTrue(false, "TODO") +} + +func (t *FlushFSTest) FsyncReports() { + AssertTrue(false, "TODO") +} + +func (t *FlushFSTest) FsyncError() { AssertTrue(false, "TODO") } From 604281b1570291db35d35778a2f9360a88cb7409 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:34:16 +1100 Subject: [PATCH 11/25] Changed the contents of flushFS. --- samples/flushfs/flush_fs.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index 07e492f..2d54a17 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -15,6 +15,8 @@ package flushfs import ( + "os" + "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fuseutil" "golang.org/x/net/context" @@ -34,6 +36,9 @@ func NewFileSystem( type flushFS struct { fuseutil.NotImplementedFileSystem + + mu sync.Mu + foo *os.File // GUARDED_BY(mu) } func (fs *flushFS) Init( From d7fec86069c714f8218dfe63dec3c80291956088 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:38:40 +1100 Subject: [PATCH 12/25] flushFS.LookUpInode --- samples/flushfs/flush_fs.go | 44 ++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index 2d54a17..924566b 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -16,6 +16,7 @@ package flushfs import ( "os" + "sync" "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fuseutil" @@ -34,13 +35,31 @@ func NewFileSystem( return } +const fooID = fuse.RootInodeID + 1 + type flushFS struct { fuseutil.NotImplementedFileSystem - mu sync.Mu + mu sync.Mutex foo *os.File // GUARDED_BY(mu) } +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +// LOCKS_REQUIRED(fs.mu) +func (fs *flushFS) fooAttributes() fuse.InodeAttributes { + return fuse.InodeAttributes{ + Nlink: 1, + Mode: 0777, + } +} + +//////////////////////////////////////////////////////////////////////// +// File system methods +//////////////////////////////////////////////////////////////////////// + func (fs *flushFS) Init( ctx context.Context, req *fuse.InitRequest) ( @@ -48,3 +67,26 @@ func (fs *flushFS) Init( resp = &fuse.InitResponse{} return } + +func (fs *flushFS) LookUpInode( + ctx context.Context, + req *fuse.LookUpInodeRequest) ( + resp *fuse.LookUpInodeResponse, err error) { + resp = &fuse.LookUpInodeResponse{} + + fs.mu.Lock() + defer fs.mu.Unlock() + + // Sanity check. + if req.Parent != fuse.RootInodeID || req.Name != "foo" { + err = fuse.ENOENT + return + } + + resp.Entry = fuse.ChildInodeEntry{ + Child: fooID, + Attributes: fs.fooAttributes(), + } + + return +} From 38e2aaff58cd1ad287ba9cb4b1cf6b3028065421 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:40:14 +1100 Subject: [PATCH 13/25] flushFS.GetInodeAttributes --- samples/flushfs/flush_fs.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index 924566b..6ca14c9 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -48,6 +48,14 @@ type flushFS struct { // Helpers //////////////////////////////////////////////////////////////////////// +// LOCKS_REQUIRED(fs.mu) +func (fs *flushFS) rootAttributes() fuse.InodeAttributes { + return fuse.InodeAttributes{ + Nlink: 1, + Mode: 0777 | os.ModeDir, + } +} + // LOCKS_REQUIRED(fs.mu) func (fs *flushFS) fooAttributes() fuse.InodeAttributes { return fuse.InodeAttributes{ @@ -90,3 +98,27 @@ func (fs *flushFS) LookUpInode( return } + +func (fs *flushFS) GetInodeAttributes( + ctx context.Context, + req *fuse.GetInodeAttributesRequest) ( + resp *fuse.GetInodeAttributesResponse, err error) { + resp = &fuse.GetInodeAttributesResponse{} + + fs.mu.Lock() + defer fs.mu.Unlock() + + switch req.Inode { + case fuse.RootInodeID: + resp.Attributes = fs.rootAttributes() + return + + case fooID: + resp.Attributes = fs.fooAttributes() + return + + default: + err = fuse.ENOENT + return + } +} From b87740f7f2c9731932a2266a27ab325a61115c42 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:40:53 +1100 Subject: [PATCH 14/25] flushFS.OpenFile. --- samples/flushfs/flush_fs.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index 6ca14c9..de77a20 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -122,3 +122,21 @@ func (fs *flushFS) GetInodeAttributes( return } } + +func (fs *flushFS) OpenFile( + ctx context.Context, + req *fuse.OpenFileRequest) ( + resp *fuse.OpenFileResponse, err error) { + resp = &fuse.OpenFileResponse{} + + fs.mu.Lock() + defer fs.mu.Unlock() + + // Sanity check. + if req.Inode != fooID { + err = fuse.ENOSYS + return + } + + return +} From 0698d680322eacaf148dcd01c7a350a49c63bf22 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:49:45 +1100 Subject: [PATCH 15/25] flushFS.WriteFile --- samples/flushfs/flush_fs.go | 32 ++++++++++++++++++++++++++++++-- samples/flushfs/flush_fs_test.go | 9 ++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index de77a20..223012c 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -15,6 +15,7 @@ package flushfs import ( + "fmt" "os" "sync" @@ -40,8 +41,8 @@ const fooID = fuse.RootInodeID + 1 type flushFS struct { fuseutil.NotImplementedFileSystem - mu sync.Mutex - foo *os.File // GUARDED_BY(mu) + mu sync.Mutex + fooContents []byte // GUARDED_BY(mu) } //////////////////////////////////////////////////////////////////////// @@ -140,3 +141,30 @@ func (fs *flushFS) OpenFile( return } + +func (fs *flushFS) WriteFile( + ctx context.Context, + req *fuse.WriteFileRequest) ( + resp *fuse.WriteFileResponse, err error) { + resp = &fuse.WriteFileResponse{} + + fs.mu.Lock() + defer fs.mu.Unlock() + + // Ensure that the contents slice is long enough. + newLen := int(req.Offset) + len(req.Data) + if len(fs.fooContents) < newLen { + padding := make([]byte, newLen-len(fs.fooContents)) + fs.fooContents = append(fs.fooContents, padding...) + } + + // Copy in the data. + n := copy(fs.fooContents[req.Offset:], req.Data) + + // Sanity check. + if n != len(req.Data) { + panic(fmt.Sprintf("Unexpected short copy: %v", n)) + } + + return +} diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 9913d0e..0984d21 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -75,6 +75,9 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { // Helpers //////////////////////////////////////////////////////////////////////// +// Match byte slices equal to the supplied string. +func byteSliceEq(expected string) Matcher + // Return a copy of the current contents of t.flushes. // // LOCKS_EXCLUDED(t.mu) @@ -127,11 +130,11 @@ func (t *FlushFSTest) CloseReports_ReadWrite() { // Seek and read them back. off, err = f.Seek(0, 0) AssertEq(nil, err) - AssertEq(4, off) + AssertEq(0, off) n, err = f.Read(buf) AssertThat(err, AnyOf(nil, io.EOF)) - AssertEq("taco", buf[:n]) + AssertEq("taco", string(buf[:n])) // At this point, no flushes or fsyncs should have happened. AssertThat(t.getFlushes(), ElementsAre()) @@ -143,7 +146,7 @@ func (t *FlushFSTest) CloseReports_ReadWrite() { AssertEq(nil, err) // Now we should have received the flush operation (but still no fsync). - ExpectThat(t.getFlushes(), ElementsAre("taco")) + ExpectThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) ExpectThat(t.getFsyncs(), ElementsAre()) } From 4eda8330e86a7d2282197c58af6b172592ec18f6 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:51:26 +1100 Subject: [PATCH 16/25] byteSliceEq --- samples/flushfs/flush_fs_test.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 0984d21..4b961e9 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -15,6 +15,8 @@ package flushfs_test import ( + "errors" + "fmt" "io" "os" "path" @@ -76,7 +78,24 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { //////////////////////////////////////////////////////////////////////// // Match byte slices equal to the supplied string. -func byteSliceEq(expected string) Matcher +func byteSliceEq(expected string) Matcher { + pred := func(c interface{}) error { + slice, ok := c.([]byte) + if !ok { + return errors.New("which is not []byte") + } + + if string(slice) != expected { + return fmt.Errorf("which is string \"%s\"", string(slice)) + } + + return nil + } + + return NewMatcher( + pred, + fmt.Sprintf("byte slice equal to string \"%s\"", expected)) +} // Return a copy of the current contents of t.flushes. // From a932a6549e526046199f950d796866db3c217d6d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:52:54 +1100 Subject: [PATCH 17/25] Support flush/fsync errors. --- samples/flushfs/flush_fs.go | 6 +++--- samples/flushfs/flush_fs_test.go | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index 223012c..2cf3ecf 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -28,10 +28,10 @@ import ( // // The file may be opened for reading and/or writing. Its initial contents are // empty. Whenever a flush or fsync is received, the supplied function will be -// called with the current contents of the file. +// called with the current contents of the file and its status returned. func NewFileSystem( - reportFlush func(string), - reportFsync func(string)) (fs fuse.FileSystem, err error) { + reportFlush func(string) error, + reportFsync func(string) error) (fs fuse.FileSystem, err error) { fs = &flushFS{} return } diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 4b961e9..aec7f56 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -41,10 +41,12 @@ type FlushFSTest struct { mu sync.Mutex // GUARDED_BY(mu) - flushes []string + flushes []string + flushErr error // GUARDED_BY(mu) - fsyncs []string + fsyncs []string + fsyncErr error } func init() { RegisterTestSuite(&FlushFSTest{}) } @@ -53,17 +55,19 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { var err error // Set up a file system. - reportTo := func(slice *[]string) func(string) { - return func(s string) { + reportTo := func(slice *[]string, err *error) func(string) error { + return func(s string) error { t.mu.Lock() defer t.mu.Unlock() + *slice = append(*slice, s) + return *err } } t.FileSystem, err = flushfs.NewFileSystem( - reportTo(&t.flushes), - reportTo(&t.fsyncs)) + reportTo(&t.flushes, &t.flushErr), + reportTo(&t.fsyncs, &t.fsyncErr)) if err != nil { panic(err) From 7f5d9a43f25a52e4d7ce628c17cd4084a98c68e5 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:54:09 +1100 Subject: [PATCH 18/25] FlushFSTest.CloseReports_ReadOnly --- samples/flushfs/flush_fs_test.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index aec7f56..6dd73af 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -174,7 +174,30 @@ func (t *FlushFSTest) CloseReports_ReadWrite() { } func (t *FlushFSTest) CloseReports_ReadOnly() { - AssertTrue(false, "TODO") + var err error + + // Open the file. + f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDONLY, 0) + AssertEq(nil, err) + + defer func() { + if f != nil { + ExpectEq(nil, f.Close()) + } + }() + + // At this point, no flushes or fsyncs should have happened. + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Close the file. + err = f.Close() + f = nil + AssertEq(nil, err) + + // Now we should have received the flush operation (but still no fsync). + ExpectThat(t.getFlushes(), ElementsAre(byteSliceEq(""))) + ExpectThat(t.getFsyncs(), ElementsAre()) } func (t *FlushFSTest) CloseReports_WriteOnly() { From 59c28230054ddec07e6769874be3d558dacad1cc Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:54:43 +1100 Subject: [PATCH 19/25] FlushFSTest.CloseReports_WriteOnly --- samples/flushfs/flush_fs_test.go | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 6dd73af..346a00c 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -201,7 +201,36 @@ func (t *FlushFSTest) CloseReports_ReadOnly() { } func (t *FlushFSTest) CloseReports_WriteOnly() { - AssertTrue(false, "TODO") + var n int + var err error + + // Open the file. + f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_WRONLY, 0) + AssertEq(nil, err) + + defer func() { + if f != nil { + ExpectEq(nil, f.Close()) + } + }() + + // Write some contents to the file. + n, err = f.Write([]byte("taco")) + AssertEq(nil, err) + AssertEq(4, n) + + // At this point, no flushes or fsyncs should have happened. + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Close the file. + err = f.Close() + f = nil + AssertEq(nil, err) + + // Now we should have received the flush operation (but still no fsync). + ExpectThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) + ExpectThat(t.getFsyncs(), ElementsAre()) } func (t *FlushFSTest) CloseReports_MultipleTimes_NonOverlappingFileHandles() { From b8ce0bcb8d7ef7a2ca186a815647f8a5218d76ca Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:56:38 +1100 Subject: [PATCH 20/25] FlushFSTest.CloseReports_MultipleTimes_NonOverlappingFileHandles --- samples/flushfs/flush_fs_test.go | 51 +++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 346a00c..c72b5be 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -234,7 +234,56 @@ func (t *FlushFSTest) CloseReports_WriteOnly() { } func (t *FlushFSTest) CloseReports_MultipleTimes_NonOverlappingFileHandles() { - AssertTrue(false, "TODO") + var n int + var err error + + // Open the file. + f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_WRONLY, 0) + AssertEq(nil, err) + + defer func() { + if f != nil { + ExpectEq(nil, f.Close()) + } + }() + + // Write some contents to the file. + n, err = f.Write([]byte("taco")) + AssertEq(nil, err) + AssertEq(4, n) + + // At this point, no flushes or fsyncs should have happened. + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Close the file. + err = f.Close() + f = nil + AssertEq(nil, err) + + // Now we should have received the flush operation (but still no fsync). + AssertThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Open the file again. + f, err = os.OpenFile(path.Join(t.Dir, "foo"), os.O_WRONLY, 0) + AssertEq(nil, err) + + // Write again; expect no further flushes. + n, err = f.Write([]byte("p")) + AssertEq(nil, err) + AssertEq(1, n) + + AssertThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Close the file. Now the new contents should be flushed. + err = f.Close() + f = nil + AssertEq(nil, err) + + AssertThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"), byteSliceEq("paco"))) + AssertThat(t.getFsyncs(), ElementsAre()) } func (t *FlushFSTest) CloseReports_MultipleTimes_OverlappingFileHandles() { From a8648b2868d608103ec332232e316eede6a7dfa1 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:57:21 +1100 Subject: [PATCH 21/25] We're not using byte slices. --- samples/flushfs/flush_fs_test.go | 34 ++++++-------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index c72b5be..818271e 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -15,8 +15,6 @@ package flushfs_test import ( - "errors" - "fmt" "io" "os" "path" @@ -81,26 +79,6 @@ func (t *FlushFSTest) SetUp(ti *TestInfo) { // Helpers //////////////////////////////////////////////////////////////////////// -// Match byte slices equal to the supplied string. -func byteSliceEq(expected string) Matcher { - pred := func(c interface{}) error { - slice, ok := c.([]byte) - if !ok { - return errors.New("which is not []byte") - } - - if string(slice) != expected { - return fmt.Errorf("which is string \"%s\"", string(slice)) - } - - return nil - } - - return NewMatcher( - pred, - fmt.Sprintf("byte slice equal to string \"%s\"", expected)) -} - // Return a copy of the current contents of t.flushes. // // LOCKS_EXCLUDED(t.mu) @@ -169,7 +147,7 @@ func (t *FlushFSTest) CloseReports_ReadWrite() { AssertEq(nil, err) // Now we should have received the flush operation (but still no fsync). - ExpectThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) + ExpectThat(t.getFlushes(), ElementsAre("taco")) ExpectThat(t.getFsyncs(), ElementsAre()) } @@ -196,7 +174,7 @@ func (t *FlushFSTest) CloseReports_ReadOnly() { AssertEq(nil, err) // Now we should have received the flush operation (but still no fsync). - ExpectThat(t.getFlushes(), ElementsAre(byteSliceEq(""))) + ExpectThat(t.getFlushes(), ElementsAre("")) ExpectThat(t.getFsyncs(), ElementsAre()) } @@ -229,7 +207,7 @@ func (t *FlushFSTest) CloseReports_WriteOnly() { AssertEq(nil, err) // Now we should have received the flush operation (but still no fsync). - ExpectThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) + ExpectThat(t.getFlushes(), ElementsAre("taco")) ExpectThat(t.getFsyncs(), ElementsAre()) } @@ -262,7 +240,7 @@ func (t *FlushFSTest) CloseReports_MultipleTimes_NonOverlappingFileHandles() { AssertEq(nil, err) // Now we should have received the flush operation (but still no fsync). - AssertThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) + AssertThat(t.getFlushes(), ElementsAre("taco")) AssertThat(t.getFsyncs(), ElementsAre()) // Open the file again. @@ -274,7 +252,7 @@ func (t *FlushFSTest) CloseReports_MultipleTimes_NonOverlappingFileHandles() { AssertEq(nil, err) AssertEq(1, n) - AssertThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"))) + AssertThat(t.getFlushes(), ElementsAre("taco")) AssertThat(t.getFsyncs(), ElementsAre()) // Close the file. Now the new contents should be flushed. @@ -282,7 +260,7 @@ func (t *FlushFSTest) CloseReports_MultipleTimes_NonOverlappingFileHandles() { f = nil AssertEq(nil, err) - AssertThat(t.getFlushes(), ElementsAre(byteSliceEq("taco"), byteSliceEq("paco"))) + AssertThat(t.getFlushes(), ElementsAre("taco", "paco")) AssertThat(t.getFsyncs(), ElementsAre()) } From 4b0c37786501517cbd97249c20c1b31dbfbff48d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 11:59:52 +1100 Subject: [PATCH 22/25] FlushFSTest.CloseReports_MultipleTimes_OverlappingFileHandles --- samples/flushfs/flush_fs_test.go | 57 +++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 818271e..290630e 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -265,7 +265,62 @@ func (t *FlushFSTest) CloseReports_MultipleTimes_NonOverlappingFileHandles() { } func (t *FlushFSTest) CloseReports_MultipleTimes_OverlappingFileHandles() { - AssertTrue(false, "TODO") + var n int + var err error + + // Open the file with two handles. + f1, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_WRONLY, 0) + AssertEq(nil, err) + + f2, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_WRONLY, 0) + AssertEq(nil, err) + + defer func() { + if f1 != nil { + ExpectEq(nil, f1.Close()) + } + + if f2 != nil { + ExpectEq(nil, f2.Close()) + } + }() + + // Write some contents with each handle. + n, err = f1.Write([]byte("taco")) + AssertEq(nil, err) + AssertEq(4, n) + + n, err = f2.Write([]byte("p")) + AssertEq(nil, err) + AssertEq(1, n) + + // At this point, no flushes or fsyncs should have happened. + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Close one handle. The current contents should be flushed. + err = f1.Close() + f1 = nil + AssertEq(nil, err) + + AssertThat(t.getFlushes(), ElementsAre("paco")) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Write some more contents via the other handle. Again, no further flushes. + n, err = f2.Write([]byte("orp")) + AssertEq(nil, err) + AssertEq(3, n) + + AssertThat(t.getFlushes(), ElementsAre("paco")) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Close the handle. Now the new contents should be flushed. + err = f2.Close() + f2 = nil + AssertEq(nil, err) + + AssertThat(t.getFlushes(), ElementsAre("paco", "porp")) + AssertThat(t.getFsyncs(), ElementsAre()) } func (t *FlushFSTest) CloseError() { From f3c4cc1048ffe310036e9af8a1fb2f57c39a7696 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 12:01:59 +1100 Subject: [PATCH 23/25] FlushFSTest.CloseError --- samples/flushfs/flush_fs_test.go | 37 +++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 290630e..ce155d7 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -21,6 +21,7 @@ import ( "sync" "testing" + "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/samples" "github.com/jacobsa/fuse/samples/flushfs" . "github.com/jacobsa/oglematchers" @@ -103,6 +104,22 @@ func (t *FlushFSTest) getFsyncs() (p []string) { return } +// LOCKS_EXCLUDED(t.mu) +func (t *FlushFSTest) setFlushError(err error) { + t.mu.Lock() + defer t.mu.Unlock() + + t.flushErr = err +} + +// LOCKS_EXCLUDED(t.mu) +func (t *FlushFSTest) setFsyncError(err error) { + t.mu.Lock() + defer t.mu.Unlock() + + t.fsyncErr = err +} + //////////////////////////////////////////////////////////////////////// // Tests //////////////////////////////////////////////////////////////////////// @@ -324,7 +341,25 @@ func (t *FlushFSTest) CloseReports_MultipleTimes_OverlappingFileHandles() { } func (t *FlushFSTest) CloseError() { - AssertTrue(false, "TODO") + // Open a file. + f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0) + AssertEq(nil, err) + + defer func() { + if f != nil { + ExpectEq(nil, f.Close()) + } + }() + + // Configure a flush error. + t.setFlushError(fuse.ENOENT) + + // Close the file. + err = f.Close() + f = nil + + AssertNe(nil, err) + ExpectThat(err, Error(HasSubstr("TODO"))) } func (t *FlushFSTest) FsyncReports() { From 2f3b25c19a973f73441a5fd24c1bc47cef174c2d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 12:03:25 +1100 Subject: [PATCH 24/25] FlushFSTest.FsyncReports --- samples/flushfs/flush_fs_test.go | 45 ++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index ce155d7..1ab3c47 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -341,7 +341,7 @@ func (t *FlushFSTest) CloseReports_MultipleTimes_OverlappingFileHandles() { } func (t *FlushFSTest) CloseError() { - // Open a file. + // Open the file. f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0) AssertEq(nil, err) @@ -363,7 +363,48 @@ func (t *FlushFSTest) CloseError() { } func (t *FlushFSTest) FsyncReports() { - AssertTrue(false, "TODO") + var n int + var err error + + // Open the file. + f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_WRONLY, 0) + AssertEq(nil, err) + + defer func() { + if f != nil { + ExpectEq(nil, f.Close()) + } + }() + + // Write some contents to the file. + n, err = f.Write([]byte("taco")) + AssertEq(nil, err) + AssertEq(4, n) + + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre()) + + // Fsync. + err = f.Sync() + AssertEq(nil, err) + + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre("taco")) + + // Write some more contents. + n, err = f.Write([]byte("s")) + AssertEq(nil, err) + AssertEq(1, n) + + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre("taco")) + + // Fsync. + err = f.Sync() + AssertEq(nil, err) + + AssertThat(t.getFlushes(), ElementsAre()) + AssertThat(t.getFsyncs(), ElementsAre("taco", "tacos")) } func (t *FlushFSTest) FsyncError() { From 0451a1bafe86615905bb4ee8d502dcaeb1ff060e Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 20 Mar 2015 12:04:25 +1100 Subject: [PATCH 25/25] FlushFSTest.FsyncError --- samples/flushfs/flush_fs_test.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index 1ab3c47..4c6eda0 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -408,5 +408,22 @@ func (t *FlushFSTest) FsyncReports() { } func (t *FlushFSTest) FsyncError() { - AssertTrue(false, "TODO") + // Open the file. + f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0) + AssertEq(nil, err) + + defer func() { + if f != nil { + ExpectEq(nil, f.Close()) + } + }() + + // Configure an fsync error. + t.setFsyncError(fuse.ENOENT) + + // Fsync. + err = f.Sync() + + AssertNe(nil, err) + ExpectThat(err, Error(HasSubstr("TODO"))) }