From 56f503accdd059c6e686d00c6d40ac1f3316f2c6 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:38:15 +1100 Subject: [PATCH 01/43] Declared the interface for a file system to help with testing caching. For the testing work in #1. --- samples/cachingfs/caching_fs.go | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 samples/cachingfs/caching_fs.go diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go new file mode 100644 index 0000000..323d2f9 --- /dev/null +++ b/samples/cachingfs/caching_fs.go @@ -0,0 +1,79 @@ +// 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 cachingfs + +import ( + "time" + + "github.com/jacobsa/fuse" + "github.com/jacobsa/fuse/fuseutil" +) + +// Constants that define the relative offsets of the inodes exported by the +// file system. See notes on the RenumberInodes method. +const ( + FooInodeOffset = iota + DirInodeOffset + BarInodeOffset +) + +// A file system with a fixed structure that looks like this: +// +// foo +// dir/ +// bar +// +// The file system is configured with durations that specify how long to allow +// inode entries and attributes to be cached, used when responding to fuse +// requests. It also exposes methods for renumbering inodes and updating mtimes +// that are useful in testing that these durations are honored. +type CachingFS struct { + fuseutil.NotImplementedFileSystem +} + +var _ fuse.FileSystem = &CachingFS{} + +// Create a file system that issues cacheable responses according to the +// following rules: +// +// * LookUpInodeResponse.Entry.EntryExpiration is set according to +// lookupEntryTimeout. +// +// * GetInodeAttributesResponse.AttributesExpiration is set according to +// getattrTimeout. +// +// * Nothing else is marked cacheable. (In particular, the attributes +// returned by LookUpInode are not cacheable.) +// +func NewCachingFS( + lookupEntryTimeout time.Duration, + getattrTimeout time.Duration) (fs *CachingFS, err error) + +// Cause inodes to receive IDs according to the following rules in further +// responses to fuse: +// +// * The ID of "foo" is base + FooInodeOffset. +// * The ID of "dir" is base + DirInodeOffset. +// * The ID of "dir/bar" is base + BarInodeOffset. +// +// If this method has never been called, the file system behaves as if it were +// called with base set to fuse.RootInodeID + 1. +// +// REQUIRES: base > fuse.RootInodeID +func (fs *CachingFS) RenumberInodes(base fuse.InodeID) + +// Cause further queries for the attributes of inodes to use the supplied time +// as the inode's mtime. +func (fs *CachingFS) SetMtime(mtime time.Time) From c79c1d84556b3dd4a924a2ec2ee44dabe0cb4369 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:40:51 +1100 Subject: [PATCH 02/43] Implemented public functions. --- samples/cachingfs/caching_fs.go | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 323d2f9..2405882 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -15,6 +15,7 @@ package cachingfs import ( + "sync" "time" "github.com/jacobsa/fuse" @@ -41,6 +42,13 @@ const ( // that are useful in testing that these durations are honored. type CachingFS struct { fuseutil.NotImplementedFileSystem + mu sync.Mutex + + // GUARDED_BY(mu) + inodeIDBase fuse.InodeID + + // GUARDED_BY(mu) + mtime time.Time } var _ fuse.FileSystem = &CachingFS{} @@ -59,7 +67,14 @@ var _ fuse.FileSystem = &CachingFS{} // func NewCachingFS( lookupEntryTimeout time.Duration, - getattrTimeout time.Duration) (fs *CachingFS, err error) + getattrTimeout time.Duration) (fs *CachingFS, err error) { + fs = &CachingFS{ + inodeIDBase: fuse.RootInodeID + 1, + mtime: time.Now(), + } + + return +} // Cause inodes to receive IDs according to the following rules in further // responses to fuse: @@ -72,8 +87,18 @@ func NewCachingFS( // called with base set to fuse.RootInodeID + 1. // // REQUIRES: base > fuse.RootInodeID -func (fs *CachingFS) RenumberInodes(base fuse.InodeID) +func (fs *CachingFS) RenumberInodes(base fuse.InodeID) { + fs.mu.Lock() + defer fs.mu.Unlock() + + fs.inodeIDBase = base +} // Cause further queries for the attributes of inodes to use the supplied time // as the inode's mtime. -func (fs *CachingFS) SetMtime(mtime time.Time) +func (fs *CachingFS) SetMtime(mtime time.Time) { + fs.mu.Lock() + defer fs.mu.Unlock() + + fs.mtime = mtime +} From 81b9b0e737bedb315c3e2a67a0b1a483b2e5e980 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:44:12 +1100 Subject: [PATCH 03/43] Added a test stub. --- samples/cachingfs/caching_fs_test.go | 96 ++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 samples/cachingfs/caching_fs_test.go diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go new file mode 100644 index 0000000..932c8d6 --- /dev/null +++ b/samples/cachingfs/caching_fs_test.go @@ -0,0 +1,96 @@ +// 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 cachingfs_test + +import ( + "io/ioutil" + "log" + "strings" + "testing" + "time" + + "github.com/jacobsa/fuse" + "github.com/jacobsa/fuse/samples/cachingfs" + . "github.com/jacobsa/oglematchers" + . "github.com/jacobsa/ogletest" + "golang.org/x/net/context" +) + +func TestHelloFS(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////////////////// +// Boilerplate +//////////////////////////////////////////////////////////////////////// + +type CachingFSTest struct { + mfs *fuse.MountedFileSystem +} + +var _ TearDownInterface = &CachingFSTest{} + +func init() { RegisterTestSuite(&CachingFSTest{}) } + +func (t *CachingFSTest) setUp( + lookupEntryTimeout time.Duration, + getattrTimeout time.Duration) { + var err error + + // Set up a temporary directory for mounting. + mountPoint, err := ioutil.TempDir("", "caching_fs_test") + AssertEq(nil, err) + + // Create a file system. + fs, err := cachingfs.NewCachingFS(lookupEntryTimeout, getattrTimeout) + AssertEq(nil, err) + + // Mount it. + t.mfs, err = fuse.Mount(mountPoint, fs) + AssertEq(nil, err) + + err = t.mfs.WaitForReady(context.Background()) + AssertEq(nil, err) +} + +func (t *HelloFSTest) TearDown() { + // Unmount the file system. Try again on "resource busy" errors. + delay := 10 * time.Millisecond + for { + err := t.mfs.Unmount() + if err == nil { + break + } + + if strings.Contains(err.Error(), "resource busy") { + log.Println("Resource busy error while unmounting; trying again") + time.Sleep(delay) + delay = time.Duration(1.3 * float64(delay)) + continue + } + + panic("MountedFileSystem.Unmount: " + err.Error()) + } + + if err := t.mfs.Join(context.Background()); err != nil { + panic("MountedFileSystem.Join: " + err.Error()) + } +} + +//////////////////////////////////////////////////////////////////////// +// Test functions +//////////////////////////////////////////////////////////////////////// + +func (t *CachingFSTest) DoesFoo() { + AssertTrue(false, "TODO") +} From 97dd87f56d2ebf18081ae2412bdcde921970da46 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:44:42 +1100 Subject: [PATCH 04/43] Fixed some build errors. --- samples/cachingfs/caching_fs_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 932c8d6..ee78f43 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -23,7 +23,6 @@ import ( "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/samples/cachingfs" - . "github.com/jacobsa/oglematchers" . "github.com/jacobsa/ogletest" "golang.org/x/net/context" ) @@ -63,7 +62,7 @@ func (t *CachingFSTest) setUp( AssertEq(nil, err) } -func (t *HelloFSTest) TearDown() { +func (t *CachingFSTest) TearDown() { // Unmount the file system. Try again on "resource busy" errors. delay := 10 * time.Millisecond for { From a1960e91a815b2cc9ac233579983eb9026b2409a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:45:06 +1100 Subject: [PATCH 05/43] Fixed a panic. --- samples/cachingfs/caching_fs_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index ee78f43..48e0e70 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -63,6 +63,11 @@ func (t *CachingFSTest) setUp( } func (t *CachingFSTest) TearDown() { + // Was the file system mounted? + if t.mfs == nil { + return + } + // Unmount the file system. Try again on "resource busy" errors. delay := 10 * time.Millisecond for { From f1fc462560e1202d05289cc98245497966cc8e46 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:50:58 +1100 Subject: [PATCH 06/43] Added several test names. --- samples/cachingfs/caching_fs_test.go | 65 ++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 48e0e70..dac496d 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -39,8 +39,6 @@ type CachingFSTest struct { var _ TearDownInterface = &CachingFSTest{} -func init() { RegisterTestSuite(&CachingFSTest{}) } - func (t *CachingFSTest) setUp( lookupEntryTimeout time.Duration, getattrTimeout time.Duration) { @@ -92,9 +90,68 @@ func (t *CachingFSTest) TearDown() { } //////////////////////////////////////////////////////////////////////// -// Test functions +// Basics //////////////////////////////////////////////////////////////////////// -func (t *CachingFSTest) DoesFoo() { +type BasicsTest struct { + NoCachingTest +} + +func init() { RegisterTestSuite(&BasicsTest{}) } + +func (t *BasicsTest) StatNonexistent_Root() { + AssertTrue(false, "TODO") +} + +func (t *BasicsTest) StatNonexistent_Dir() { + AssertTrue(false, "TODO") +} + +func (t *BasicsTest) StatFoo() { + AssertTrue(false, "TODO") +} + +func (t *BasicsTest) StatDir() { + AssertTrue(false, "TODO") +} + +func (t *BasicsTest) StatBar() { + AssertTrue(false, "TODO") +} + +//////////////////////////////////////////////////////////////////////// +// No caching +//////////////////////////////////////////////////////////////////////// + +type NoCachingTest struct { + CachingFSTest +} + +var _ SetUpInterface = &NoCachingTest{} + +func init() { RegisterTestSuite(&NoCachingTest{}) } + +func (t *NoCachingTest) SetUp(ti *TestInfo) { + const ( + lookupEntryTimeout = 0 + getattrTimeout = 0 + ) + + t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout) +} + +func (t *NoCachingTest) StatStat() { + AssertTrue(false, "TODO") +} + +func (t *NoCachingTest) StatRenumberStat() { + AssertTrue(false, "TODO") +} + +func (t *NoCachingTest) StatMtimeStat() { + AssertTrue(false, "TODO") +} + +func (t *NoCachingTest) StatRenumberMtimeStat() { AssertTrue(false, "TODO") } From 3e8995eb7edfa62f7692c1882a759c4a51d21d79 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:53:55 +1100 Subject: [PATCH 07/43] Made CachingFS an interface, hiding a bunch of irrelevant stuff. --- samples/cachingfs/caching_fs.go | 45 +++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 2405882..06ca702 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -40,19 +40,27 @@ const ( // inode entries and attributes to be cached, used when responding to fuse // requests. It also exposes methods for renumbering inodes and updating mtimes // that are useful in testing that these durations are honored. -type CachingFS struct { - fuseutil.NotImplementedFileSystem - mu sync.Mutex +type CachingFS interface { + fuse.FileSystem - // GUARDED_BY(mu) - inodeIDBase fuse.InodeID + // Cause inodes to receive IDs according to the following rules in further + // responses to fuse: + // + // * The ID of "foo" is base + FooInodeOffset. + // * The ID of "dir" is base + DirInodeOffset. + // * The ID of "dir/bar" is base + BarInodeOffset. + // + // If this method has never been called, the file system behaves as if it + // were called with base set to fuse.RootInodeID + 1. + // + // REQUIRES: base > fuse.RootInodeID + RenumberInodes(base fuse.InodeID) - // GUARDED_BY(mu) - mtime time.Time + // Cause further queries for the attributes of inodes to use the supplied + // time as the inode's mtime. + SetMtime(mtime time.Time) } -var _ fuse.FileSystem = &CachingFS{} - // Create a file system that issues cacheable responses according to the // following rules: // @@ -67,8 +75,8 @@ var _ fuse.FileSystem = &CachingFS{} // func NewCachingFS( lookupEntryTimeout time.Duration, - getattrTimeout time.Duration) (fs *CachingFS, err error) { - fs = &CachingFS{ + getattrTimeout time.Duration) (fs CachingFS, err error) { + fs = &cachingFS{ inodeIDBase: fuse.RootInodeID + 1, mtime: time.Now(), } @@ -76,6 +84,17 @@ func NewCachingFS( return } +type cachingFS struct { + fuseutil.NotImplementedFileSystem + mu sync.Mutex + + // GUARDED_BY(mu) + inodeIDBase fuse.InodeID + + // GUARDED_BY(mu) + mtime time.Time +} + // Cause inodes to receive IDs according to the following rules in further // responses to fuse: // @@ -87,7 +106,7 @@ func NewCachingFS( // called with base set to fuse.RootInodeID + 1. // // REQUIRES: base > fuse.RootInodeID -func (fs *CachingFS) RenumberInodes(base fuse.InodeID) { +func (fs *cachingFS) RenumberInodes(base fuse.InodeID) { fs.mu.Lock() defer fs.mu.Unlock() @@ -96,7 +115,7 @@ func (fs *CachingFS) RenumberInodes(base fuse.InodeID) { // Cause further queries for the attributes of inodes to use the supplied time // as the inode's mtime. -func (fs *CachingFS) SetMtime(mtime time.Time) { +func (fs *cachingFS) SetMtime(mtime time.Time) { fs.mu.Lock() defer fs.mu.Unlock() From 236b1ef9dfadac03bea9c1b0c494ebfc88ab3952 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:55:24 +1100 Subject: [PATCH 08/43] Implemented Init. --- samples/cachingfs/caching_fs.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 06ca702..4619196 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -20,6 +20,7 @@ import ( "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fuseutil" + "golang.org/x/net/context" ) // Constants that define the relative offsets of the inodes exported by the @@ -95,17 +96,7 @@ type cachingFS struct { mtime time.Time } -// Cause inodes to receive IDs according to the following rules in further -// responses to fuse: -// -// * The ID of "foo" is base + FooInodeOffset. -// * The ID of "dir" is base + DirInodeOffset. -// * The ID of "dir/bar" is base + BarInodeOffset. -// -// If this method has never been called, the file system behaves as if it were -// called with base set to fuse.RootInodeID + 1. -// -// REQUIRES: base > fuse.RootInodeID +// LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) RenumberInodes(base fuse.InodeID) { fs.mu.Lock() defer fs.mu.Unlock() @@ -113,11 +104,17 @@ func (fs *cachingFS) RenumberInodes(base fuse.InodeID) { fs.inodeIDBase = base } -// Cause further queries for the attributes of inodes to use the supplied time -// as the inode's mtime. +// LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) SetMtime(mtime time.Time) { fs.mu.Lock() defer fs.mu.Unlock() fs.mtime = mtime } + +func (fs *cachingFS) Init( + ctx context.Context, + req *fuse.InitRequest) (resp *fuse.InitResponse, err error) { + resp = &fuse.InitResponse{} + return +} From d3b071b61a3a4929344b78cc411d501f1389b26e Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:56:56 +1100 Subject: [PATCH 09/43] Fixed duplicate test methods. --- samples/cachingfs/caching_fs_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index dac496d..cf415d1 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -94,11 +94,22 @@ func (t *CachingFSTest) TearDown() { //////////////////////////////////////////////////////////////////////// type BasicsTest struct { - NoCachingTest + CachingFSTest } +var _ SetUpInterface = &BasicsTest{} + func init() { RegisterTestSuite(&BasicsTest{}) } +func (t *BasicsTest) SetUp(ti *TestInfo) { + const ( + lookupEntryTimeout = 0 + getattrTimeout = 0 + ) + + t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout) +} + func (t *BasicsTest) StatNonexistent_Root() { AssertTrue(false, "TODO") } From 8e194826f08e666d45cebf1a34ea3c95fe8dbc93 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 12:58:25 +1100 Subject: [PATCH 10/43] BasicsTest.StatNonexistent_Root --- samples/cachingfs/caching_fs_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index cf415d1..371ad65 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -17,6 +17,8 @@ package cachingfs_test import ( "io/ioutil" "log" + "os" + "path" "strings" "testing" "time" @@ -34,6 +36,7 @@ func TestHelloFS(t *testing.T) { RunTests(t) } //////////////////////////////////////////////////////////////////////// type CachingFSTest struct { + dir string mfs *fuse.MountedFileSystem } @@ -45,7 +48,7 @@ func (t *CachingFSTest) setUp( var err error // Set up a temporary directory for mounting. - mountPoint, err := ioutil.TempDir("", "caching_fs_test") + t.dir, err = ioutil.TempDir("", "caching_fs_test") AssertEq(nil, err) // Create a file system. @@ -53,7 +56,7 @@ func (t *CachingFSTest) setUp( AssertEq(nil, err) // Mount it. - t.mfs, err = fuse.Mount(mountPoint, fs) + t.mfs, err = fuse.Mount(t.dir, fs) AssertEq(nil, err) err = t.mfs.WaitForReady(context.Background()) @@ -111,7 +114,10 @@ func (t *BasicsTest) SetUp(ti *TestInfo) { } func (t *BasicsTest) StatNonexistent_Root() { - AssertTrue(false, "TODO") + _, err := os.Stat(path.Join(t.dir, "blah")) + + AssertNe(nil, err) + ExpectTrue(os.IsNotExist(err), "err: %v", err) } func (t *BasicsTest) StatNonexistent_Dir() { From 064452dcc9f58ef736c7a25c7dbc0aceb8ecfc7f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:14:19 +1100 Subject: [PATCH 11/43] Simplified inode renumbering. --- samples/cachingfs/caching_fs.go | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 4619196..ffe1d61 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -23,14 +23,6 @@ import ( "golang.org/x/net/context" ) -// Constants that define the relative offsets of the inodes exported by the -// file system. See notes on the RenumberInodes method. -const ( - FooInodeOffset = iota - DirInodeOffset - BarInodeOffset -) - // A file system with a fixed structure that looks like this: // // foo @@ -44,18 +36,13 @@ const ( type CachingFS interface { fuse.FileSystem - // Cause inodes to receive IDs according to the following rules in further - // responses to fuse: - // - // * The ID of "foo" is base + FooInodeOffset. - // * The ID of "dir" is base + DirInodeOffset. - // * The ID of "dir/bar" is base + BarInodeOffset. - // - // If this method has never been called, the file system behaves as if it - // were called with base set to fuse.RootInodeID + 1. - // - // REQUIRES: base > fuse.RootInodeID - RenumberInodes(base fuse.InodeID) + // Return the current inode ID of the file/directory with the given name. + FooID() fuse.InodeID + DirID() fuse.InodeID + BarID() fuse.InodeID + + // Cause the inode IDs to change to values that have never before been used. + RenumberInodes() // Cause further queries for the attributes of inodes to use the supplied // time as the inode's mtime. From 8a4e3f242d2e49fce9ca7ae25517ccac5be240d7 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:22:33 +1100 Subject: [PATCH 12/43] Reworked inode numbering. --- samples/cachingfs/caching_fs.go | 58 ++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index ffe1d61..7c48931 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -15,14 +15,20 @@ package cachingfs import ( - "sync" "time" "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fuseutil" + "github.com/jacobsa/gcloud/syncutil" "golang.org/x/net/context" ) +const ( + // Sizes of the files according to the file system. + FooSize = 123 + BarSize = 456 +) + // A file system with a fixed structure that looks like this: // // foo @@ -64,32 +70,60 @@ type CachingFS interface { func NewCachingFS( lookupEntryTimeout time.Duration, getattrTimeout time.Duration) (fs CachingFS, err error) { - fs = &cachingFS{ - inodeIDBase: fuse.RootInodeID + 1, - mtime: time.Now(), + cfs := &cachingFS{ + fooID: ((fuse.RootInodeID + 1 + numInodes - 1) / numInodes) + fooOffset, + mtime: time.Now(), } + cfs.mu = syncutil.NewInvariantMutex(cfs.checkInvariants) + + fs = cfs return } +const ( + // Inode IDs are issued such that "foo" always receives an ID that is + // congruent to fooOffset modulo numInodes, etc. + fooOffset = iota + dirOffset + barOffset + numInodes +) + type cachingFS struct { fuseutil.NotImplementedFileSystem - mu sync.Mutex + mu syncutil.InvariantMutex + // The current ID of foo. + // + // INVARIANT: FooID() > fuse.RootInodeID + // INVARIANT: DirID() > fuse.RootInodeID + // INVARIANT: BarID() > fuse.RootInodeID + // + // INVARIANT: FooID() % numInodes == fooOffset + // INVARIANT: DirID() % numInodes == dirOffset + // INVARIANT: BarID() % numInodes == barOffset + // // GUARDED_BY(mu) - inodeIDBase fuse.InodeID + fooID fuse.InodeID // GUARDED_BY(mu) mtime time.Time } -// LOCKS_EXCLUDED(fs.mu) -func (fs *cachingFS) RenumberInodes(base fuse.InodeID) { - fs.mu.Lock() - defer fs.mu.Unlock() +func (fs *cachingFS) checkInvariants() - fs.inodeIDBase = base -} +// LOCKS_EXCLUDED(fs.mu) +func (fs *cachingFS) FooID() fuse.InodeID + +// LOCKS_EXCLUDED(fs.mu) +func (fs *cachingFS) DirID() fuse.InodeID + +// LOCKS_EXCLUDED(fs.mu) +func (fs *cachingFS) BarID() fuse.InodeID + +// LOCKS_EXCLUDED(fs.mu) +func (fs *cachingFS) RenumberInodes() // LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) SetMtime(mtime time.Time) { From 19c26cc2e84e0dd2cd19c63e6a147fb81eba7546 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:23:30 +1100 Subject: [PATCH 13/43] Implemented FooID. --- samples/cachingfs/caching_fs.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 7c48931..6d910d9 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -71,8 +71,8 @@ func NewCachingFS( lookupEntryTimeout time.Duration, getattrTimeout time.Duration) (fs CachingFS, err error) { cfs := &cachingFS{ - fooID: ((fuse.RootInodeID + 1 + numInodes - 1) / numInodes) + fooOffset, - mtime: time.Now(), + baseID: (fuse.RootInodeID + 1 + numInodes - 1) / numInodes, + mtime: time.Now(), } cfs.mu = syncutil.NewInvariantMutex(cfs.checkInvariants) @@ -94,7 +94,7 @@ type cachingFS struct { fuseutil.NotImplementedFileSystem mu syncutil.InvariantMutex - // The current ID of foo. + // The current ID of the lowest numbered non-root inode. // // INVARIANT: FooID() > fuse.RootInodeID // INVARIANT: DirID() > fuse.RootInodeID @@ -105,7 +105,7 @@ type cachingFS struct { // INVARIANT: BarID() % numInodes == barOffset // // GUARDED_BY(mu) - fooID fuse.InodeID + baseID fuse.InodeID // GUARDED_BY(mu) mtime time.Time @@ -114,7 +114,12 @@ type cachingFS struct { func (fs *cachingFS) checkInvariants() // LOCKS_EXCLUDED(fs.mu) -func (fs *cachingFS) FooID() fuse.InodeID +func (fs *cachingFS) FooID() fuse.InodeID { + fs.mu.Lock() + defer fs.mu.Unlock() + + return fs.baseID + fooOffset +} // LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) DirID() fuse.InodeID From c6fab5488b2bed08439d3cbb9ee16ad2dd381b37 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:23:58 +1100 Subject: [PATCH 14/43] Implemented other ID methods. --- samples/cachingfs/caching_fs.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 6d910d9..4490cff 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -122,10 +122,20 @@ func (fs *cachingFS) FooID() fuse.InodeID { } // LOCKS_EXCLUDED(fs.mu) -func (fs *cachingFS) DirID() fuse.InodeID +func (fs *cachingFS) DirID() fuse.InodeID { + fs.mu.Lock() + defer fs.mu.Unlock() + + return fs.baseID + dirOffset +} // LOCKS_EXCLUDED(fs.mu) -func (fs *cachingFS) BarID() fuse.InodeID +func (fs *cachingFS) BarID() fuse.InodeID { + fs.mu.Lock() + defer fs.mu.Unlock() + + return fs.baseID + barOffset +} // LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) RenumberInodes() From a9ebf7fe458fdfc195605d37ad9d2810f831b056 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:25:27 +1100 Subject: [PATCH 15/43] Implemented checkInvariants. --- samples/cachingfs/caching_fs.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 4490cff..160e145 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -15,6 +15,7 @@ package cachingfs import ( + "fmt" "time" "github.com/jacobsa/fuse" @@ -96,13 +97,8 @@ type cachingFS struct { // The current ID of the lowest numbered non-root inode. // - // INVARIANT: FooID() > fuse.RootInodeID - // INVARIANT: DirID() > fuse.RootInodeID - // INVARIANT: BarID() > fuse.RootInodeID - // - // INVARIANT: FooID() % numInodes == fooOffset - // INVARIANT: DirID() % numInodes == dirOffset - // INVARIANT: BarID() % numInodes == barOffset + // INVARIANT: baseID > fuse.RootInodeID + // INVARIANT: baseID % numInodes == 0 // // GUARDED_BY(mu) baseID fuse.InodeID @@ -111,7 +107,13 @@ type cachingFS struct { mtime time.Time } -func (fs *cachingFS) checkInvariants() +func (fs *cachingFS) checkInvariants() { + // INVARIANT: baseID > fuse.RootInodeID + // INVARIANT: baseID % numInodes == 0 + if fs.baseID <= fuse.RootInodeID || fs.baseID%numInodes != 0 { + panic(fmt.Sprintf("Bad baseID: %v", fs.baseID)) + } +} // LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) FooID() fuse.InodeID { From 1b71593b3473f9ea2c9fdf8097881e0fa7776e1c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:25:46 +1100 Subject: [PATCH 16/43] Implemented RenumberInodes. --- samples/cachingfs/caching_fs.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 160e145..9014e79 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -140,7 +140,12 @@ func (fs *cachingFS) BarID() fuse.InodeID { } // LOCKS_EXCLUDED(fs.mu) -func (fs *cachingFS) RenumberInodes() +func (fs *cachingFS) RenumberInodes() { + fs.mu.Lock() + defer fs.mu.Unlock() + + fs.baseID += numInodes +} // LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) SetMtime(mtime time.Time) { From 11724230a51d16738d3f0bece8e053b4e93da6b8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:32:35 +1100 Subject: [PATCH 17/43] Implemented LookUpInode. --- samples/cachingfs/caching_fs.go | 87 +++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 9014e79..dffb33a 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -107,6 +107,10 @@ type cachingFS struct { mtime time.Time } +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + func (fs *cachingFS) checkInvariants() { // INVARIANT: baseID > fuse.RootInodeID // INVARIANT: baseID % numInodes == 0 @@ -115,6 +119,28 @@ func (fs *cachingFS) checkInvariants() { } } +// LOCKS_REQUIRED(fs.mu) +func (fs *cachingFS) fooID() fuse.InodeID + +// LOCKS_REQUIRED(fs.mu) +func (fs *cachingFS) dirID() fuse.InodeID + +// LOCKS_REQUIRED(fs.mu) +func (fs *cachingFS) barID() fuse.InodeID + +// LOCKS_REQUIRED(fs.mu) +func (fs *cachingFS) fooAttrs() fuse.InodeAttributes + +// LOCKS_REQUIRED(fs.mu) +func (fs *cachingFS) dirAttrs() fuse.InodeAttributes + +// LOCKS_REQUIRED(fs.mu) +func (fs *cachingFS) barAttrs() fuse.InodeAttributes + +//////////////////////////////////////////////////////////////////////// +// Public interface +//////////////////////////////////////////////////////////////////////// + // LOCKS_EXCLUDED(fs.mu) func (fs *cachingFS) FooID() fuse.InodeID { fs.mu.Lock() @@ -155,9 +181,70 @@ func (fs *cachingFS) SetMtime(mtime time.Time) { fs.mtime = mtime } +//////////////////////////////////////////////////////////////////////// +// FileSystem methods +//////////////////////////////////////////////////////////////////////// + func (fs *cachingFS) Init( ctx context.Context, req *fuse.InitRequest) (resp *fuse.InitResponse, err error) { resp = &fuse.InitResponse{} return } + +// LOCKS_EXCLUDED(fs.mu) +func (fs *cachingFS) LookUpInode( + ctx context.Context, + req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) { + resp = &fuse.LookUpInodeResponse{} + + fs.mu.Lock() + defer fs.mu.Unlock() + + // Find the ID and attributes. + var id fuse.InodeID + var attrs fuse.InodeAttributes + + switch req.Name { + case "foo": + // Parent must be the root. + if req.Parent != fuse.RootInodeID { + err = fuse.ENOENT + return + } + + id = fs.fooID() + attrs = fs.fooAttrs() + + case "dir": + // Parent must be the root. + if req.Parent != fuse.RootInodeID { + err = fuse.ENOENT + return + } + + id = fs.dirID() + attrs = fs.dirAttrs() + + case "bar": + // Parent must be dir. + if req.Parent%numInodes != dirOffset { + err = fuse.ENOENT + return + } + + id = fs.barID() + attrs = fs.barAttrs() + + default: + err = fuse.ENOENT + return + } + + // Fill in the response. + resp.Entry.Child = id + resp.Entry.Attributes = attrs + resp.Entry.EntryExpiration = fs.entryExpiration + + return +} From cea0fb8b4897d9526c14409e86a227fdf8debf2a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:33:32 +1100 Subject: [PATCH 18/43] Refactored ID methods. --- samples/cachingfs/caching_fs.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index dffb33a..e304a49 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -120,13 +120,19 @@ func (fs *cachingFS) checkInvariants() { } // LOCKS_REQUIRED(fs.mu) -func (fs *cachingFS) fooID() fuse.InodeID +func (fs *cachingFS) fooID() fuse.InodeID { + return fs.baseID + fooOffset +} // LOCKS_REQUIRED(fs.mu) -func (fs *cachingFS) dirID() fuse.InodeID +func (fs *cachingFS) dirID() fuse.InodeID { + return fs.baseID + dirOffset +} // LOCKS_REQUIRED(fs.mu) -func (fs *cachingFS) barID() fuse.InodeID +func (fs *cachingFS) barID() fuse.InodeID { + return fs.baseID + barOffset +} // LOCKS_REQUIRED(fs.mu) func (fs *cachingFS) fooAttrs() fuse.InodeAttributes @@ -146,7 +152,7 @@ func (fs *cachingFS) FooID() fuse.InodeID { fs.mu.Lock() defer fs.mu.Unlock() - return fs.baseID + fooOffset + return fs.fooID() } // LOCKS_EXCLUDED(fs.mu) @@ -154,7 +160,7 @@ func (fs *cachingFS) DirID() fuse.InodeID { fs.mu.Lock() defer fs.mu.Unlock() - return fs.baseID + dirOffset + return fs.dirID() } // LOCKS_EXCLUDED(fs.mu) @@ -162,7 +168,7 @@ func (fs *cachingFS) BarID() fuse.InodeID { fs.mu.Lock() defer fs.mu.Unlock() - return fs.baseID + barOffset + return fs.barID() } // LOCKS_EXCLUDED(fs.mu) From 7cddc48c2431d0d559f5b1f3ecc04bf51f02fd2f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:34:50 +1100 Subject: [PATCH 19/43] Fixed a build error. --- samples/cachingfs/caching_fs.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index e304a49..6c3cd83 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -72,8 +72,10 @@ func NewCachingFS( lookupEntryTimeout time.Duration, getattrTimeout time.Duration) (fs CachingFS, err error) { cfs := &cachingFS{ - baseID: (fuse.RootInodeID + 1 + numInodes - 1) / numInodes, - mtime: time.Now(), + lookupEntryTimeout: lookupEntryTimeout, + getattrTimeout: getattrTimeout, + baseID: (fuse.RootInodeID + 1 + numInodes - 1) / numInodes, + mtime: time.Now(), } cfs.mu = syncutil.NewInvariantMutex(cfs.checkInvariants) @@ -93,6 +95,18 @@ const ( type cachingFS struct { fuseutil.NotImplementedFileSystem + + ///////////////////////// + // Constant data + ///////////////////////// + + lookupEntryTimeout time.Duration + getattrTimeout time.Duration + + ///////////////////////// + // Mutable state + ///////////////////////// + mu syncutil.InvariantMutex // The current ID of the lowest numbered non-root inode. @@ -250,7 +264,7 @@ func (fs *cachingFS) LookUpInode( // Fill in the response. resp.Entry.Child = id resp.Entry.Attributes = attrs - resp.Entry.EntryExpiration = fs.entryExpiration + resp.Entry.EntryExpiration = time.Now().Add(fs.lookupEntryTimeout) return } From ef4c7d5c8a00070e85ad58d6dc45fa5e1001546d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:35:47 +1100 Subject: [PATCH 20/43] Implemented missing methods. --- samples/cachingfs/caching_fs.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 6c3cd83..00a6a15 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -16,6 +16,7 @@ package cachingfs import ( "fmt" + "os" "time" "github.com/jacobsa/fuse" @@ -149,13 +150,30 @@ func (fs *cachingFS) barID() fuse.InodeID { } // LOCKS_REQUIRED(fs.mu) -func (fs *cachingFS) fooAttrs() fuse.InodeAttributes +func (fs *cachingFS) fooAttrs() fuse.InodeAttributes { + return fuse.InodeAttributes{ + Size: FooSize, + Mode: 0777, + Mtime: fs.mtime, + } +} // LOCKS_REQUIRED(fs.mu) -func (fs *cachingFS) dirAttrs() fuse.InodeAttributes +func (fs *cachingFS) dirAttrs() fuse.InodeAttributes { + return fuse.InodeAttributes{ + Mode: os.ModeDir | 0777, + Mtime: fs.mtime, + } +} // LOCKS_REQUIRED(fs.mu) -func (fs *cachingFS) barAttrs() fuse.InodeAttributes +func (fs *cachingFS) barAttrs() fuse.InodeAttributes { + return fuse.InodeAttributes{ + Size: BarSize, + Mode: 0777, + Mtime: fs.mtime, + } +} //////////////////////////////////////////////////////////////////////// // Public interface From 241b9447fc6884c7ebd5caf788ce67434452cc1f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:36:23 +1100 Subject: [PATCH 21/43] Expanded BasicsTest.StatNonexistent_Root. --- samples/cachingfs/caching_fs_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 371ad65..3d4c69b 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -114,7 +114,16 @@ func (t *BasicsTest) SetUp(ti *TestInfo) { } func (t *BasicsTest) StatNonexistent_Root() { - _, err := os.Stat(path.Join(t.dir, "blah")) + var err error + + // Unknown name + _, err = os.Stat(path.Join(t.dir, "blah")) + + AssertNe(nil, err) + ExpectTrue(os.IsNotExist(err), "err: %v", err) + + // Wrong directory + _, err = os.Stat(path.Join(t.dir, "bar")) AssertNe(nil, err) ExpectTrue(os.IsNotExist(err), "err: %v", err) From 9ee0551b8cb7cea2c0af111f2c3ae99eedf6e6f4 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:36:55 +1100 Subject: [PATCH 22/43] Fixed a bug. --- samples/cachingfs/caching_fs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 00a6a15..32685c6 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -266,7 +266,7 @@ func (fs *cachingFS) LookUpInode( case "bar": // Parent must be dir. - if req.Parent%numInodes != dirOffset { + if req.Parent == fuse.RootInodeID || req.Parent%numInodes != dirOffset { err = fuse.ENOENT return } From 2051c13cc051fa7df2c1b432cbda5667ce3f2c29 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:38:07 +1100 Subject: [PATCH 23/43] Simplified BasicsTest.StatNonexistent_Root. --- samples/cachingfs/caching_fs_test.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 3d4c69b..cff680b 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -114,19 +114,17 @@ func (t *BasicsTest) SetUp(ti *TestInfo) { } func (t *BasicsTest) StatNonexistent_Root() { - var err error + names := []string{ + "blah", + "bar", // Wrong directory + } - // Unknown name - _, err = os.Stat(path.Join(t.dir, "blah")) + for _, n := range names { + _, err := os.Stat(path.Join(t.dir, n)) - AssertNe(nil, err) - ExpectTrue(os.IsNotExist(err), "err: %v", err) - - // Wrong directory - _, err = os.Stat(path.Join(t.dir, "bar")) - - AssertNe(nil, err) - ExpectTrue(os.IsNotExist(err), "err: %v", err) + AssertNe(nil, err) + ExpectTrue(os.IsNotExist(err), "n: %s, err: %v", n, err) + } } func (t *BasicsTest) StatNonexistent_Dir() { From c8b0b0e822c5c14142b45f73de99e84d3f7f341d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:38:51 +1100 Subject: [PATCH 24/43] BasicsTest.StatNonexistent --- samples/cachingfs/caching_fs_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index cff680b..829d030 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -113,10 +113,13 @@ func (t *BasicsTest) SetUp(ti *TestInfo) { t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout) } -func (t *BasicsTest) StatNonexistent_Root() { +func (t *BasicsTest) StatNonexistent() { names := []string{ "blah", - "bar", // Wrong directory + "bar", + "dir/blah", + "dir/dir", + "dir/foo", } for _, n := range names { @@ -127,10 +130,6 @@ func (t *BasicsTest) StatNonexistent_Root() { } } -func (t *BasicsTest) StatNonexistent_Dir() { - AssertTrue(false, "TODO") -} - func (t *BasicsTest) StatFoo() { AssertTrue(false, "TODO") } From bac78088be267ddb209d2592b2e389a40790a8de Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:40:11 +1100 Subject: [PATCH 25/43] BasicsTest.StatFoo --- samples/cachingfs/caching_fs_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 829d030..b2be157 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -131,7 +131,13 @@ func (t *BasicsTest) StatNonexistent() { } func (t *BasicsTest) StatFoo() { - AssertTrue(false, "TODO") + fi, err := os.Stat(path.Join(t.dir, "foo")) + AssertEq(nil, err) + + ExpectEq("foo", fi.Name()) + ExpectEq(cachingfs.FooSize, fi.Size()) + ExpectEq(0777, fi.Mode()) + ExpectFalse(fi.IsDir()) } func (t *BasicsTest) StatDir() { From 968b5dd71afbd30c98a77c3b42e304d8f9e64145 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:42:47 +1100 Subject: [PATCH 26/43] Fixed an ID allocation bug. --- samples/cachingfs/caching_fs.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 32685c6..7751f4c 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -72,10 +72,14 @@ type CachingFS interface { func NewCachingFS( lookupEntryTimeout time.Duration, getattrTimeout time.Duration) (fs CachingFS, err error) { + roundUp := func(n fuse.InodeID) fuse.InodeID { + return numInodes * ((n + numInodes - 1) / numInodes) + } + cfs := &cachingFS{ lookupEntryTimeout: lookupEntryTimeout, getattrTimeout: getattrTimeout, - baseID: (fuse.RootInodeID + 1 + numInodes - 1) / numInodes, + baseID: roundUp(fuse.RootInodeID + 1), mtime: time.Now(), } From dc5f8ae4bcf97e80f9fa8c8738d611027f3b6217 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:46:15 +1100 Subject: [PATCH 27/43] Implemented GetInodeAttributes. --- samples/cachingfs/caching_fs.go | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 7751f4c..9349b10 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -153,6 +153,14 @@ func (fs *cachingFS) barID() fuse.InodeID { return fs.baseID + barOffset } +// LOCKS_REQUIRED(fs.mu) +func (fs *cachingFS) rootAttrs() fuse.InodeAttributes { + return fuse.InodeAttributes{ + Mode: os.ModeDir | 0777, + Mtime: fs.mtime, + } +} + // LOCKS_REQUIRED(fs.mu) func (fs *cachingFS) fooAttrs() fuse.InodeAttributes { return fuse.InodeAttributes{ @@ -290,3 +298,37 @@ func (fs *cachingFS) LookUpInode( return } + +// LOCKS_EXCLUDED(fs.mu) +func (fs *cachingFS) GetInodeAttributes( + ctx context.Context, + req *fuse.GetInodeAttributesRequest) ( + resp *fuse.GetInodeAttributesResponse, err error) { + resp = &fuse.GetInodeAttributesResponse{} + + fs.mu.Lock() + defer fs.mu.Unlock() + + // Figure out which inode the request is for. + var attrs fuse.InodeAttributes + + switch { + case req.Inode == fuse.RootInodeID: + attrs = fs.rootAttrs() + + case req.Inode%numInodes == fooOffset: + attrs = fs.fooAttrs() + + case req.Inode%numInodes == dirOffset: + attrs = fs.dirAttrs() + + case req.Inode%numInodes == barOffset: + attrs = fs.barAttrs() + } + + // Fill in the response. + resp.Attributes = attrs + resp.AttributesExpiration = time.Now().Add(fs.getattrTimeout) + + return +} From f1914f74b27c5055c09a76aa408be545a47ba67d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:47:02 +1100 Subject: [PATCH 28/43] Finished writing BasicsTest. --- samples/cachingfs/caching_fs_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index b2be157..6c2a308 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -141,11 +141,22 @@ func (t *BasicsTest) StatFoo() { } func (t *BasicsTest) StatDir() { - AssertTrue(false, "TODO") + fi, err := os.Stat(path.Join(t.dir, "dir")) + AssertEq(nil, err) + + ExpectEq("dir", fi.Name()) + ExpectEq(os.ModeDir|0777, fi.Mode()) + ExpectTrue(fi.IsDir()) } func (t *BasicsTest) StatBar() { - AssertTrue(false, "TODO") + fi, err := os.Stat(path.Join(t.dir, "dir/bar")) + AssertEq(nil, err) + + ExpectEq("bar", fi.Name()) + ExpectEq(cachingfs.BarSize, fi.Size()) + ExpectEq(0777, fi.Mode()) + ExpectFalse(fi.IsDir()) } //////////////////////////////////////////////////////////////////////// From 64906024604ba8d6e7d01e90430d6607c8a55e8a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:55:04 +1100 Subject: [PATCH 29/43] NoCachingTest.StatStat --- samples/cachingfs/caching_fs_test.go | 58 +++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 6c2a308..1e40904 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -20,6 +20,7 @@ import ( "os" "path" "strings" + "syscall" "testing" "time" @@ -36,8 +37,10 @@ func TestHelloFS(t *testing.T) { RunTests(t) } //////////////////////////////////////////////////////////////////////// type CachingFSTest struct { - dir string - mfs *fuse.MountedFileSystem + dir string + fs cachingfs.CachingFS + mfs *fuse.MountedFileSystem + initialMtime time.Time } var _ TearDownInterface = &CachingFSTest{} @@ -51,16 +54,20 @@ func (t *CachingFSTest) setUp( t.dir, err = ioutil.TempDir("", "caching_fs_test") AssertEq(nil, err) - // Create a file system. - fs, err := cachingfs.NewCachingFS(lookupEntryTimeout, getattrTimeout) + // Create the file system. + t.fs, err = cachingfs.NewCachingFS(lookupEntryTimeout, getattrTimeout) AssertEq(nil, err) // Mount it. - t.mfs, err = fuse.Mount(t.dir, fs) + t.mfs, err = fuse.Mount(t.dir, t.fs) AssertEq(nil, err) err = t.mfs.WaitForReady(context.Background()) AssertEq(nil, err) + + // Set up the mtime. + t.initialMtime = time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local) + t.fs.SetMtime(t.initialMtime) } func (t *CachingFSTest) TearDown() { @@ -92,6 +99,10 @@ func (t *CachingFSTest) TearDown() { } } +func getInodeID(fi os.FileInfo) uint64 { + return fi.Sys().(*syscall.Stat_t).Ino +} + //////////////////////////////////////////////////////////////////////// // Basics //////////////////////////////////////////////////////////////////////// @@ -137,7 +148,9 @@ func (t *BasicsTest) StatFoo() { ExpectEq("foo", fi.Name()) ExpectEq(cachingfs.FooSize, fi.Size()) ExpectEq(0777, fi.Mode()) + ExpectEq(t.initialMtime, fi.ModTime()) ExpectFalse(fi.IsDir()) + ExpectEq(t.fs.FooID(), getInodeID(fi)) } func (t *BasicsTest) StatDir() { @@ -146,7 +159,9 @@ func (t *BasicsTest) StatDir() { ExpectEq("dir", fi.Name()) ExpectEq(os.ModeDir|0777, fi.Mode()) + ExpectEq(t.initialMtime, fi.ModTime()) ExpectTrue(fi.IsDir()) + ExpectEq(t.fs.DirID(), getInodeID(fi)) } func (t *BasicsTest) StatBar() { @@ -156,7 +171,9 @@ func (t *BasicsTest) StatBar() { ExpectEq("bar", fi.Name()) ExpectEq(cachingfs.BarSize, fi.Size()) ExpectEq(0777, fi.Mode()) + ExpectEq(t.initialMtime, fi.ModTime()) ExpectFalse(fi.IsDir()) + ExpectEq(t.fs.BarID(), getInodeID(fi)) } //////////////////////////////////////////////////////////////////////// @@ -181,7 +198,36 @@ func (t *NoCachingTest) SetUp(ti *TestInfo) { } func (t *NoCachingTest) StatStat() { - AssertTrue(false, "TODO") + var err error + + // Stat everything. + fooBefore, err := os.Stat(path.Join(t.dir, "foo")) + AssertEq(nil, err) + + dirBefore, err := os.Stat(path.Join(t.dir, "dir")) + AssertEq(nil, err) + + barBefore, err := os.Stat(path.Join(t.dir, "bar")) + AssertEq(nil, err) + + // Stat again. + fooAfter, err := os.Stat(path.Join(t.dir, "foo")) + AssertEq(nil, err) + + dirAfter, err := os.Stat(path.Join(t.dir, "dir")) + AssertEq(nil, err) + + barAfter, err := os.Stat(path.Join(t.dir, "bar")) + AssertEq(nil, err) + + // Make sure everything matches. + ExpectEq(fooBefore.ModTime(), fooAfter.ModTime()) + ExpectEq(dirBefore.ModTime(), dirAfter.ModTime()) + ExpectEq(barBefore.ModTime(), barAfter.ModTime()) + + ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectEq(getInodeID(barBefore), getInodeID(barAfter)) } func (t *NoCachingTest) StatRenumberStat() { From e61d2fc9f63df8e2f340dbff740233cd4e950924 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:55:42 +1100 Subject: [PATCH 30/43] Fixed some test bugs. --- samples/cachingfs/caching_fs_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 1e40904..4194e33 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -26,6 +26,7 @@ import ( "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/samples/cachingfs" + "github.com/jacobsa/gcsfuse/timeutil" . "github.com/jacobsa/ogletest" "golang.org/x/net/context" ) @@ -148,7 +149,7 @@ func (t *BasicsTest) StatFoo() { ExpectEq("foo", fi.Name()) ExpectEq(cachingfs.FooSize, fi.Size()) ExpectEq(0777, fi.Mode()) - ExpectEq(t.initialMtime, fi.ModTime()) + ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime)) ExpectFalse(fi.IsDir()) ExpectEq(t.fs.FooID(), getInodeID(fi)) } @@ -159,7 +160,7 @@ func (t *BasicsTest) StatDir() { ExpectEq("dir", fi.Name()) ExpectEq(os.ModeDir|0777, fi.Mode()) - ExpectEq(t.initialMtime, fi.ModTime()) + ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime)) ExpectTrue(fi.IsDir()) ExpectEq(t.fs.DirID(), getInodeID(fi)) } @@ -171,7 +172,7 @@ func (t *BasicsTest) StatBar() { ExpectEq("bar", fi.Name()) ExpectEq(cachingfs.BarSize, fi.Size()) ExpectEq(0777, fi.Mode()) - ExpectEq(t.initialMtime, fi.ModTime()) + ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime)) ExpectFalse(fi.IsDir()) ExpectEq(t.fs.BarID(), getInodeID(fi)) } From a3dcce8ed9264421b0faecf26d2b504bfc1aef35 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:56:37 +1100 Subject: [PATCH 31/43] Fixed more test bugs. --- samples/cachingfs/caching_fs_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 4194e33..1758d25 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -208,7 +208,7 @@ func (t *NoCachingTest) StatStat() { dirBefore, err := os.Stat(path.Join(t.dir, "dir")) AssertEq(nil, err) - barBefore, err := os.Stat(path.Join(t.dir, "bar")) + barBefore, err := os.Stat(path.Join(t.dir, "dir/bar")) AssertEq(nil, err) // Stat again. @@ -218,13 +218,13 @@ func (t *NoCachingTest) StatStat() { dirAfter, err := os.Stat(path.Join(t.dir, "dir")) AssertEq(nil, err) - barAfter, err := os.Stat(path.Join(t.dir, "bar")) + barAfter, err := os.Stat(path.Join(t.dir, "dir/bar")) AssertEq(nil, err) // Make sure everything matches. - ExpectEq(fooBefore.ModTime(), fooAfter.ModTime()) - ExpectEq(dirBefore.ModTime(), dirAfter.ModTime()) - ExpectEq(barBefore.ModTime(), barAfter.ModTime()) + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime())) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime())) ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) From b07c1f482578a2ce2106e81495612ac5681f75c5 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:58:56 +1100 Subject: [PATCH 32/43] Refactored. --- samples/cachingfs/caching_fs_test.go | 51 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 1758d25..f215aec 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -37,16 +37,16 @@ func TestHelloFS(t *testing.T) { RunTests(t) } // Boilerplate //////////////////////////////////////////////////////////////////////// -type CachingFSTest struct { +type cachingFSTest struct { dir string fs cachingfs.CachingFS mfs *fuse.MountedFileSystem initialMtime time.Time } -var _ TearDownInterface = &CachingFSTest{} +var _ TearDownInterface = &cachingFSTest{} -func (t *CachingFSTest) setUp( +func (t *cachingFSTest) setUp( lookupEntryTimeout time.Duration, getattrTimeout time.Duration) { var err error @@ -71,7 +71,7 @@ func (t *CachingFSTest) setUp( t.fs.SetMtime(t.initialMtime) } -func (t *CachingFSTest) TearDown() { +func (t *cachingFSTest) TearDown() { // Was the file system mounted? if t.mfs == nil { return @@ -100,6 +100,21 @@ func (t *CachingFSTest) TearDown() { } } +func (t *cachingFSTest) statAll() (foo, dir, bar os.FileInfo) { + var err error + + foo, err = os.Stat(path.Join(t.dir, "foo")) + AssertEq(nil, err) + + dir, err = os.Stat(path.Join(t.dir, "dir")) + AssertEq(nil, err) + + bar, err = os.Stat(path.Join(t.dir, "dir/bar")) + AssertEq(nil, err) + + return +} + func getInodeID(fi os.FileInfo) uint64 { return fi.Sys().(*syscall.Stat_t).Ino } @@ -109,7 +124,7 @@ func getInodeID(fi os.FileInfo) uint64 { //////////////////////////////////////////////////////////////////////// type BasicsTest struct { - CachingFSTest + cachingFSTest } var _ SetUpInterface = &BasicsTest{} @@ -122,7 +137,7 @@ func (t *BasicsTest) SetUp(ti *TestInfo) { getattrTimeout = 0 ) - t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout) + t.cachingFSTest.setUp(lookupEntryTimeout, getattrTimeout) } func (t *BasicsTest) StatNonexistent() { @@ -182,7 +197,7 @@ func (t *BasicsTest) StatBar() { //////////////////////////////////////////////////////////////////////// type NoCachingTest struct { - CachingFSTest + cachingFSTest } var _ SetUpInterface = &NoCachingTest{} @@ -195,31 +210,15 @@ func (t *NoCachingTest) SetUp(ti *TestInfo) { getattrTimeout = 0 ) - t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout) + t.cachingFSTest.setUp(lookupEntryTimeout, getattrTimeout) } func (t *NoCachingTest) StatStat() { - var err error - // Stat everything. - fooBefore, err := os.Stat(path.Join(t.dir, "foo")) - AssertEq(nil, err) - - dirBefore, err := os.Stat(path.Join(t.dir, "dir")) - AssertEq(nil, err) - - barBefore, err := os.Stat(path.Join(t.dir, "dir/bar")) - AssertEq(nil, err) + fooBefore, dirBefore, barBefore := t.statAll() // Stat again. - fooAfter, err := os.Stat(path.Join(t.dir, "foo")) - AssertEq(nil, err) - - dirAfter, err := os.Stat(path.Join(t.dir, "dir")) - AssertEq(nil, err) - - barAfter, err := os.Stat(path.Join(t.dir, "dir/bar")) - AssertEq(nil, err) + fooAfter, dirAfter, barAfter := t.statAll() // Make sure everything matches. ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) From ea6a65ae8ecceed1efc0bca17747fabde5526a07 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 13:59:48 +1100 Subject: [PATCH 33/43] NoCachingTest.StatRenumberStat --- samples/cachingfs/caching_fs_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index f215aec..fae5b17 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -214,10 +214,7 @@ func (t *NoCachingTest) SetUp(ti *TestInfo) { } func (t *NoCachingTest) StatStat() { - // Stat everything. fooBefore, dirBefore, barBefore := t.statAll() - - // Stat again. fooAfter, dirAfter, barAfter := t.statAll() // Make sure everything matches. @@ -231,7 +228,15 @@ func (t *NoCachingTest) StatStat() { } func (t *NoCachingTest) StatRenumberStat() { - AssertTrue(false, "TODO") + fooBefore, dirBefore, barBefore := t.statAll() + t.fs.RenumberInodes() + fooAfter, dirAfter, barAfter := t.statAll() + + // We should see different inode IDs, because the entries should not have + // been cached. + ExpectNe(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectNe(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectNe(getInodeID(barBefore), getInodeID(barAfter)) } func (t *NoCachingTest) StatMtimeStat() { From 1aab442f8b311defe725b5c713e223d949a3961c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:01:45 +1100 Subject: [PATCH 34/43] NoCachingTest.StatMtimeStat --- samples/cachingfs/caching_fs_test.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index fae5b17..7a382b7 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -228,19 +228,29 @@ func (t *NoCachingTest) StatStat() { } func (t *NoCachingTest) StatRenumberStat() { - fooBefore, dirBefore, barBefore := t.statAll() + t.statAll() t.fs.RenumberInodes() fooAfter, dirAfter, barAfter := t.statAll() - // We should see different inode IDs, because the entries should not have - // been cached. - ExpectNe(getInodeID(fooBefore), getInodeID(fooAfter)) - ExpectNe(getInodeID(dirBefore), getInodeID(dirAfter)) - ExpectNe(getInodeID(barBefore), getInodeID(barAfter)) + // We should see the new inode IDs, because the entries should not have been + // cached. + ExpectEq(t.fs.FooID(), getInodeID(fooAfter)) + ExpectEq(t.fs.DirID(), getInodeID(dirAfter)) + ExpectEq(t.fs.BarID(), getInodeID(barAfter)) } func (t *NoCachingTest) StatMtimeStat() { - AssertTrue(false, "TODO") + newMtime := t.initialMtime.Add(time.Second) + + t.statAll() + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statAll() + + // We should see the new mtimes, because the attributes should not have been + // cached. + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) } func (t *NoCachingTest) StatRenumberMtimeStat() { From b1ad507c8502d385acd520b24f67dd04ed532774 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:02:16 +1100 Subject: [PATCH 35/43] NoCachingTest.StatRenumberMtimeStat --- samples/cachingfs/caching_fs_test.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 7a382b7..4e550d2 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -254,5 +254,20 @@ func (t *NoCachingTest) StatMtimeStat() { } func (t *NoCachingTest) StatRenumberMtimeStat() { - AssertTrue(false, "TODO") + newMtime := t.initialMtime.Add(time.Second) + + t.statAll() + t.fs.RenumberInodes() + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statAll() + + // We should see the new inode IDs and mtimes, because nothing should have + // been cached. + ExpectEq(t.fs.FooID(), getInodeID(fooAfter)) + ExpectEq(t.fs.DirID(), getInodeID(dirAfter)) + ExpectEq(t.fs.BarID(), getInodeID(barAfter)) + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) } From ac34f0f7210bd77c1a6b5376823ccbf6f00c62f9 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:07:49 +1100 Subject: [PATCH 36/43] Added EntryCachingTest. --- samples/cachingfs/caching_fs_test.go | 99 ++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 4e550d2..b12d37f 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -271,3 +271,102 @@ func (t *NoCachingTest) StatRenumberMtimeStat() { ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) } + +//////////////////////////////////////////////////////////////////////// +// Entry caching +//////////////////////////////////////////////////////////////////////// + +type EntryCachingTest struct { + cachingFSTest + lookupEntryTimeout time.Duration +} + +var _ SetUpInterface = &EntryCachingTest{} + +func init() { RegisterTestSuite(&EntryCachingTest{}) } + +func (t *EntryCachingTest) SetUp(ti *TestInfo) { + t.lookupEntryTimeout = 250 * time.Millisecond + t.cachingFSTest.setUp(t.lookupEntryTimeout, 0) +} + +func (t *EntryCachingTest) StatStat() { + fooBefore, dirBefore, barBefore := t.statAll() + fooAfter, dirAfter, barAfter := t.statAll() + + // Make sure everything matches. + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime())) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime())) + + ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectEq(getInodeID(barBefore), getInodeID(barAfter)) +} + +func (t *EntryCachingTest) StatRenumberStat() { + fooBefore, dirBefore, barBefore := t.statAll() + t.fs.RenumberInodes() + fooAfter, dirAfter, barAfter := t.statAll() + + // We should still see the old inode IDs, because the inode entries should + // have been cached. + ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectEq(getInodeID(barBefore), getInodeID(barAfter)) + + // But after waiting for the entry cache to expire, we should see the new + // IDs. + time.Sleep(2 * t.lookupEntryTimeout) + fooAfter, dirAfter, barAfter = t.statAll() + + ExpectEq(t.fs.FooID(), getInodeID(fooAfter)) + ExpectEq(t.fs.DirID(), getInodeID(dirAfter)) + ExpectEq(t.fs.BarID(), getInodeID(barAfter)) +} + +func (t *EntryCachingTest) StatMtimeStat() { + newMtime := t.initialMtime.Add(time.Second) + + t.statAll() + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statAll() + + // We should see the new mtimes, because the attributes should not have been + // cached. + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) +} + +func (t *EntryCachingTest) StatRenumberMtimeStat() { + newMtime := t.initialMtime.Add(time.Second) + + fooBefore, dirBefore, barBefore := t.statAll() + t.fs.RenumberInodes() + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statAll() + + // We should still see the old inode IDs, because the inode entries should + // have been cached. But the attributes should not have been. + ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectEq(getInodeID(barBefore), getInodeID(barAfter)) + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) + + // After waiting for the entry cache to expire, we should see fresh + // everything. + time.Sleep(2 * t.lookupEntryTimeout) + fooAfter, dirAfter, barAfter = t.statAll() + + ExpectEq(t.fs.FooID(), getInodeID(fooAfter)) + ExpectEq(t.fs.DirID(), getInodeID(dirAfter)) + ExpectEq(t.fs.BarID(), getInodeID(barAfter)) + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) +} From 9ca89ca229386636bf04b63465a511e625f22f8f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:11:27 +1100 Subject: [PATCH 37/43] Added AttributeCachingTest. --- samples/cachingfs/caching_fs_test.go | 86 ++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index b12d37f..06d9587 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -370,3 +370,89 @@ func (t *EntryCachingTest) StatRenumberMtimeStat() { ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) } + +//////////////////////////////////////////////////////////////////////// +// Attribute caching +//////////////////////////////////////////////////////////////////////// + +type AttributeCachingTest struct { + cachingFSTest + getattrTimeout time.Duration +} + +var _ SetUpInterface = &AttributeCachingTest{} + +func init() { RegisterTestSuite(&AttributeCachingTest{}) } + +func (t *AttributeCachingTest) SetUp(ti *TestInfo) { + t.getattrTimeout = 250 * time.Millisecond + t.cachingFSTest.setUp(0, t.getattrTimeout) +} + +func (t *AttributeCachingTest) StatStat() { + fooBefore, dirBefore, barBefore := t.statAll() + fooAfter, dirAfter, barAfter := t.statAll() + + // Make sure everything matches. + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime())) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime())) + + ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectEq(getInodeID(barBefore), getInodeID(barAfter)) +} + +func (t *AttributeCachingTest) StatRenumberStat() { + t.statAll() + t.fs.RenumberInodes() + fooAfter, dirAfter, barAfter := t.statAll() + + // We should see the new inode IDs, because the entries should not have been + // cached. + ExpectEq(t.fs.FooID(), getInodeID(fooAfter)) + ExpectEq(t.fs.DirID(), getInodeID(dirAfter)) + ExpectEq(t.fs.BarID(), getInodeID(barAfter)) +} + +func (t *AttributeCachingTest) StatMtimeStat() { + newMtime := t.initialMtime.Add(time.Second) + + fooBefore, dirBefore, barBefore := t.statAll() + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statAll() + + // We should still see the old attributes. + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime())) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime())) + + // But after waiting for the attribute cache to expire, we should see the new + // attributes. + time.Sleep(2 * t.getattrTimeout) + fooAfter, dirAfter, barAfter = t.statAll() + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) +} + +func (t *AttributeCachingTest) StatRenumberMtimeStat() { + newMtime := t.initialMtime.Add(time.Second) + + t.statAll() + t.fs.RenumberInodes() + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statAll() + + // We should see new everything, because this is the first time the new + // inodes have been encountered. Entries for the old ones should not have + // been cached. + ExpectEq(t.fs.FooID(), getInodeID(fooAfter)) + ExpectEq(t.fs.DirID(), getInodeID(dirAfter)) + ExpectEq(t.fs.BarID(), getInodeID(barAfter)) + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) +} From 0fe8ed1493b7c5bfd660c783cb6fd72524d6f4f3 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:16:10 +1100 Subject: [PATCH 38/43] Fixed AttributeCachingTest.StatMtimeStat. --- samples/cachingfs/caching_fs_test.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 06d9587..9a412ad 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -418,20 +418,12 @@ func (t *AttributeCachingTest) StatRenumberStat() { func (t *AttributeCachingTest) StatMtimeStat() { newMtime := t.initialMtime.Add(time.Second) - fooBefore, dirBefore, barBefore := t.statAll() + t.statAll() t.fs.SetMtime(newMtime) fooAfter, dirAfter, barAfter := t.statAll() - // We should still see the old attributes. - ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) - ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime())) - ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime())) - - // But after waiting for the attribute cache to expire, we should see the new - // attributes. - time.Sleep(2 * t.getattrTimeout) - fooAfter, dirAfter, barAfter = t.statAll() - + // We should see the new attributes, since the entry had to be looked up + // again and the new attributes were returned with the entry. ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) From 8d1ca91bc98ba252ba96e651d51334bf47e161c6 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:22:44 +1100 Subject: [PATCH 39/43] AttributeCachingTest.StatRenumberMtimeStat_ViaFileDescriptor --- samples/cachingfs/caching_fs_test.go | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 9a412ad..5bc4457 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -115,6 +115,37 @@ func (t *cachingFSTest) statAll() (foo, dir, bar os.FileInfo) { return } +func (t *cachingFSTest) openFiles() (foo, dir, bar *os.File) { + var err error + + foo, err = os.Open(path.Join(t.dir, "foo")) + AssertEq(nil, err) + + dir, err = os.Open(path.Join(t.dir, "dir")) + AssertEq(nil, err) + + bar, err = os.Open(path.Join(t.dir, "bar")) + AssertEq(nil, err) + + return +} + +func (t *cachingFSTest) statFiles( + f, g, h *os.File) (foo, dir, bar os.FileInfo) { + var err error + + foo, err = f.Stat() + AssertEq(nil, err) + + dir, err = g.Stat() + AssertEq(nil, err) + + bar, err = h.Stat() + AssertEq(nil, err) + + return +} + func getInodeID(fi os.FileInfo) uint64 { return fi.Sys().(*syscall.Stat_t).Ino } @@ -448,3 +479,37 @@ func (t *AttributeCachingTest) StatRenumberMtimeStat() { ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) } + +func (t *AttributeCachingTest) StatRenumberMtimeStat_ViaFileDescriptor() { + newMtime := t.initialMtime.Add(time.Second) + + // Open everything, fixing a particular inode number for each. + foo, dir, bar := t.openFiles() + + fooBefore, dirBefore, barBefore := t.statFiles(foo, dir, bar) + t.fs.RenumberInodes() + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statFiles(foo, dir, bar) + + // We should still see the old cached mtime with the old inode ID. + ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectEq(getInodeID(barBefore), getInodeID(barAfter)) + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime())) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime())) + + // After waiting for the attribute cache to expire, we should see the fresh + // mtime, still with the old inode ID. + time.Sleep(2 * t.getattrTimeout) + fooAfter, dirAfter, barAfter = t.statAll() + + ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) + ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) + ExpectEq(getInodeID(barBefore), getInodeID(barAfter)) + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) +} From 56cd06ab51d88c16926d14e53da99decb4fb1d7f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:23:36 +1100 Subject: [PATCH 40/43] Implemented OpenDir and OpenFile. --- samples/cachingfs/caching_fs.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index 9349b10..00178ed 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -332,3 +332,19 @@ func (fs *cachingFS) GetInodeAttributes( return } + +func (fs *cachingFS) OpenDir( + ctx context.Context, + req *fuse.OpenDirRequest) ( + resp *fuse.OpenDirResponse, err error) { + resp = &fuse.OpenDirResponse{} + return +} + +func (fs *cachingFS) OpenFile( + ctx context.Context, + req *fuse.OpenFileRequest) ( + resp *fuse.OpenFileResponse, err error) { + resp = &fuse.OpenFileResponse{} + return +} From 440803fff69a5faa90ed511e51e6f5c41320001d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:24:13 +1100 Subject: [PATCH 41/43] Fixed some test bugs. --- samples/cachingfs/caching_fs_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 5bc4457..bee7d97 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -124,7 +124,7 @@ func (t *cachingFSTest) openFiles() (foo, dir, bar *os.File) { dir, err = os.Open(path.Join(t.dir, "dir")) AssertEq(nil, err) - bar, err = os.Open(path.Join(t.dir, "bar")) + bar, err = os.Open(path.Join(t.dir, "dir/bar")) AssertEq(nil, err) return @@ -485,6 +485,11 @@ func (t *AttributeCachingTest) StatRenumberMtimeStat_ViaFileDescriptor() { // Open everything, fixing a particular inode number for each. foo, dir, bar := t.openFiles() + defer func() { + foo.Close() + dir.Close() + bar.Close() + }() fooBefore, dirBefore, barBefore := t.statFiles(foo, dir, bar) t.fs.RenumberInodes() From c7db9c22ece052b01c090231276cfa2e43a37b91 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:24:55 +1100 Subject: [PATCH 42/43] Fixed more test bugs. --- samples/cachingfs/caching_fs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index bee7d97..051d4c5 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -508,7 +508,7 @@ func (t *AttributeCachingTest) StatRenumberMtimeStat_ViaFileDescriptor() { // After waiting for the attribute cache to expire, we should see the fresh // mtime, still with the old inode ID. time.Sleep(2 * t.getattrTimeout) - fooAfter, dirAfter, barAfter = t.statAll() + fooAfter, dirAfter, barAfter = t.statFiles(foo, dir, bar) ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter)) ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter)) From 69c73e39d4946785956ec9ce1f96f1b5d4d2e0cb Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 17 Mar 2015 14:27:23 +1100 Subject: [PATCH 43/43] AttributeCachingTest.StatMtimeStat_ViaFileDescriptor --- samples/cachingfs/caching_fs_test.go | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/samples/cachingfs/caching_fs_test.go b/samples/cachingfs/caching_fs_test.go index 051d4c5..20e13df 100644 --- a/samples/cachingfs/caching_fs_test.go +++ b/samples/cachingfs/caching_fs_test.go @@ -460,6 +460,36 @@ func (t *AttributeCachingTest) StatMtimeStat() { ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) } +func (t *AttributeCachingTest) StatMtimeStat_ViaFileDescriptor() { + newMtime := t.initialMtime.Add(time.Second) + + // Open everything, fixing a particular inode number for each. + foo, dir, bar := t.openFiles() + defer func() { + foo.Close() + dir.Close() + bar.Close() + }() + + fooBefore, dirBefore, barBefore := t.statFiles(foo, dir, bar) + t.fs.SetMtime(newMtime) + fooAfter, dirAfter, barAfter := t.statFiles(foo, dir, bar) + + // We should still see the old cached mtime. + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime())) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime())) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime())) + + // After waiting for the attribute cache to expire, we should see the fresh + // mtime. + time.Sleep(2 * t.getattrTimeout) + fooAfter, dirAfter, barAfter = t.statFiles(foo, dir, bar) + + ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime)) + ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime)) +} + func (t *AttributeCachingTest) StatRenumberMtimeStat() { newMtime := t.initialMtime.Add(time.Second)