Added flushfs tests involving fdatasync and directories.

For GoogleCloudPlatform/gcsfuse#13.
geesefs-0-30-9
Aaron Jacobs 2015-03-24 12:33:22 +11:00
commit 02eb1c8e32
6 changed files with 204 additions and 12 deletions

View File

@ -0,0 +1,23 @@
// 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 fsutil
import "os"
const FdatasyncSupported = false
func fdatasync(f *os.File) error {
panic("We require FdatasyncSupported be true.")
}

26
fsutil/fdatasync_linux.go Normal file
View File

@ -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 fsutil
import (
"os"
"syscall"
)
const FdatasyncSupported = true
func fdatasync(f *os.File) error {
return syscall.Fdatasync(int(f.Fd()))
}

View File

@ -48,3 +48,10 @@ func AnonymousFile(dir string) (f *os.File, err error) {
return
}
// Call fdatasync on the supplied file.
//
// REQUIRES: FdatasyncSupported is true.
func Fdatasync(f *os.File) error {
return fdatasync(f)
}

View File

@ -24,11 +24,14 @@ import (
"golang.org/x/net/context"
)
// Create a file system containing a single file named "foo".
// Create a file system whose sole contents are a file named "foo" and a
// directory named "bar".
//
// 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 and its status returned.
//
// The directory cannot be modified.
func NewFileSystem(
reportFlush func(string) error,
reportFsync func(string) error) (fs fuse.FileSystem, err error) {
@ -40,7 +43,10 @@ func NewFileSystem(
return
}
const fooID = fuse.RootInodeID + 1
const (
fooID = fuse.RootInodeID + 1 + iota
barID
)
type flushFS struct {
fuseutil.NotImplementedFileSystem
@ -72,6 +78,14 @@ func (fs *flushFS) fooAttributes() fuse.InodeAttributes {
}
}
// LOCKS_REQUIRED(fs.mu)
func (fs *flushFS) barAttributes() fuse.InodeAttributes {
return fuse.InodeAttributes{
Nlink: 1,
Mode: 0777 | os.ModeDir,
}
}
////////////////////////////////////////////////////////////////////////
// File system methods
////////////////////////////////////////////////////////////////////////
@ -94,14 +108,28 @@ func (fs *flushFS) LookUpInode(
defer fs.mu.Unlock()
// Sanity check.
if req.Parent != fuse.RootInodeID || req.Name != "foo" {
if req.Parent != fuse.RootInodeID {
err = fuse.ENOENT
return
}
resp.Entry = fuse.ChildInodeEntry{
Child: fooID,
Attributes: fs.fooAttributes(),
// Set up the entry.
switch req.Name {
case "foo":
resp.Entry = fuse.ChildInodeEntry{
Child: fooID,
Attributes: fs.fooAttributes(),
}
case "bar":
resp.Entry = fuse.ChildInodeEntry{
Child: barID,
Attributes: fs.barAttributes(),
}
default:
err = fuse.ENOENT
return
}
return
@ -125,6 +153,10 @@ func (fs *flushFS) GetInodeAttributes(
resp.Attributes = fs.fooAttributes()
return
case barID:
resp.Attributes = fs.barAttributes()
return
default:
err = fuse.ENOENT
return
@ -222,3 +254,21 @@ func (fs *flushFS) FlushFile(
err = fs.reportFlush(string(fs.fooContents))
return
}
func (fs *flushFS) OpenDir(
ctx context.Context,
req *fuse.OpenDirRequest) (
resp *fuse.OpenDirResponse, err error) {
resp = &fuse.OpenDirResponse{}
fs.mu.Lock()
defer fs.mu.Unlock()
// Sanity check.
if req.Inode != barID {
err = fuse.ENOSYS
return
}
return
}

View File

@ -409,6 +409,49 @@ func (t *NoErrorsTest) Fsync() {
AssertThat(t.getFsyncs(), ElementsAre("taco", "tacos"))
}
func (t *NoErrorsTest) Fdatasync() {
var n int
var err error
if !fsutil.FdatasyncSupported {
return
}
// Open the file.
t.f1, err = os.OpenFile(path.Join(t.Dir, "foo"), os.O_WRONLY, 0)
AssertEq(nil, err)
// Write some contents to the file.
n, err = t.f1.Write([]byte("taco"))
AssertEq(nil, err)
AssertEq(4, n)
AssertThat(t.getFlushes(), ElementsAre())
AssertThat(t.getFsyncs(), ElementsAre())
// Fdatasync.
err = fsutil.Fdatasync(t.f1)
AssertEq(nil, err)
AssertThat(t.getFlushes(), ElementsAre())
AssertThat(t.getFsyncs(), ElementsAre("taco"))
// Write some more contents.
n, err = t.f1.Write([]byte("s"))
AssertEq(nil, err)
AssertEq(1, n)
AssertThat(t.getFlushes(), ElementsAre())
AssertThat(t.getFsyncs(), ElementsAre("taco"))
// Fdatasync.
err = fsutil.Fdatasync(t.f1)
AssertEq(nil, err)
AssertThat(t.getFlushes(), ElementsAre())
AssertThat(t.getFsyncs(), ElementsAre("taco", "tacos"))
}
func (t *NoErrorsTest) Dup() {
var n int
var err error
@ -614,7 +657,29 @@ func (t *NoErrorsTest) Mmap_CloseBeforeMunmap() {
}
func (t *NoErrorsTest) Directory() {
AssertTrue(false, "TODO")
var err error
// Open the directory.
t.f1, err = os.Open(path.Join(t.Dir, "bar"))
AssertEq(nil, err)
// Sanity check: stat it.
fi, err := t.f1.Stat()
AssertEq(nil, err)
AssertEq(0777|os.ModeDir, fi.Mode())
// Sync it.
err = t.f1.Sync()
AssertEq(nil, err)
// Close it.
err = t.f1.Close()
t.f1 = nil
AssertEq(nil, err)
// No flushes or fsync requests should have been received.
ExpectThat(t.getFlushes(), ElementsAre())
ExpectThat(t.getFsyncs(), ElementsAre())
}
////////////////////////////////////////////////////////////////////////
@ -643,7 +708,6 @@ func (t *FlushErrorTest) Close() {
err = t.f1.Close()
t.f1 = nil
AssertNe(nil, err)
ExpectThat(err, Error(HasSubstr("no such file")))
}
@ -671,7 +735,6 @@ func (t *FlushErrorTest) Dup() {
if runtime.GOOS == "darwin" {
AssertEq(nil, err)
} else {
AssertNe(nil, err)
ExpectThat(err, Error(HasSubstr("no such file")))
}
@ -679,7 +742,6 @@ func (t *FlushErrorTest) Dup() {
err = t.f2.Close()
t.f2 = nil
AssertNe(nil, err)
ExpectThat(err, Error(HasSubstr("no such file")))
}
@ -725,6 +787,22 @@ func (t *FsyncErrorTest) Fsync() {
// Fsync.
err = t.f1.Sync()
AssertNe(nil, err)
ExpectThat(err, Error(HasSubstr("no such file")))
}
func (t *FsyncErrorTest) Fdatasync() {
var err error
if !fsutil.FdatasyncSupported {
return
}
// Open the file.
t.f1, err = os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0)
AssertEq(nil, err)
// Fdatasync.
err = fsutil.Fdatasync(t.f1)
ExpectThat(err, Error(HasSubstr("no such file")))
}

View File

@ -35,6 +35,8 @@ var fToolPath = flag.String(
"",
"Path to the mount_sample tool. If unset, we will compile it.")
var fDebug = flag.Bool("debug", false, "If true, print fuse debug info.")
// A struct that implements common behavior needed by tests in the samples/
// directory where the file system is mounted by a subprocess. Use it as an
// embedded field in your test fixture, calling its SetUp method from your
@ -273,7 +275,13 @@ func (t *SubprocessTest) initialize() (err error) {
mountCmd.Stderr = &stderr
mountCmd.ExtraFiles = extraFiles
// Start it.
// Handle debug mode.
if *fDebug {
mountCmd.Stderr = os.Stderr
mountCmd.Args = append(mountCmd.Args, "--fuse.debug")
}
// Start the command.
if err = mountCmd.Start(); err != nil {
err = fmt.Errorf("mountCmd.Start: %v", err)
return