commit
c1e8f4d449
|
@ -211,6 +211,34 @@ type FileSystem interface {
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *WriteFileRequest) (*WriteFileResponse, error)
|
req *WriteFileRequest) (*WriteFileResponse, error)
|
||||||
|
|
||||||
|
// Flush the current state of an open file to storage.
|
||||||
|
//
|
||||||
|
// vfs.txt documents this as being called for each close(2) system call (cf.
|
||||||
|
// http://goo.gl/FSkbrq). Code walk for that case:
|
||||||
|
//
|
||||||
|
// * (http://goo.gl/e3lv0e) sys_close calls __close_fd, calls filp_close.
|
||||||
|
// * (http://goo.gl/nI8fxD) filp_close calls f_op->flush (fuse_flush).
|
||||||
|
//
|
||||||
|
// But note that this is also called in other contexts where a file
|
||||||
|
// descriptor is closed, such as dup2(2) (cf. http://goo.gl/NQDvFS). In the
|
||||||
|
// case of close(2), a flush error is returned to the user. For dup2(2), it
|
||||||
|
// is not.
|
||||||
|
//
|
||||||
|
// Because of cases like dup2(2), calls to FlushFile are not necessarily one
|
||||||
|
// to one with calls to OpenFile. They should not be used for reference
|
||||||
|
// counting, and the handle must remain valid even after the method is called
|
||||||
|
// (use ReleaseFileHandle to dispose of it).
|
||||||
|
//
|
||||||
|
// Typical "real" file systems do not implement this, presumably relying on
|
||||||
|
// the kernel to write out the page cache to the block device eventually.
|
||||||
|
// They can get away with this because a later open(2) will see the same
|
||||||
|
// data. A file system that writes to remote storage however probably wants
|
||||||
|
// to at least schedule a real flush, and maybe do it immediately in order to
|
||||||
|
// return any errors that occur.
|
||||||
|
FlushFile(
|
||||||
|
ctx context.Context,
|
||||||
|
req *FlushFileRequest) (*FlushFileResponse, error)
|
||||||
|
|
||||||
// Release a previously-minted file handle. The kernel calls this when there
|
// Release a previously-minted file handle. The kernel calls this when there
|
||||||
// are no more references to an open file: all file descriptors are closed
|
// are no more references to an open file: all file descriptors are closed
|
||||||
// and all memory mappings are unmapped.
|
// and all memory mappings are unmapped.
|
||||||
|
@ -795,6 +823,17 @@ type WriteFileRequest struct {
|
||||||
type WriteFileResponse struct {
|
type WriteFileResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FlushFileRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
|
// The file and handle being flushed.
|
||||||
|
Inode InodeID
|
||||||
|
Handle HandleID
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlushFileResponse struct {
|
||||||
|
}
|
||||||
|
|
||||||
type ReleaseFileHandleRequest struct {
|
type ReleaseFileHandleRequest struct {
|
||||||
Header RequestHeader
|
Header RequestHeader
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,12 @@ func (fs *NotImplementedFileSystem) WriteFile(
|
||||||
return nil, fuse.ENOSYS
|
return nil, fuse.ENOSYS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *NotImplementedFileSystem) FlushFile(
|
||||||
|
ctx context.Context,
|
||||||
|
req *fuse.FlushFileRequest) (*fuse.FlushFileResponse, error) {
|
||||||
|
return nil, fuse.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *NotImplementedFileSystem) ReleaseFileHandle(
|
func (fs *NotImplementedFileSystem) ReleaseFileHandle(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *fuse.ReleaseFileHandleRequest) (*fuse.ReleaseFileHandleResponse, error) {
|
req *fuse.ReleaseFileHandleRequest) (*fuse.ReleaseFileHandleResponse, error) {
|
||||||
|
|
|
@ -32,7 +32,11 @@ import (
|
||||||
func NewFileSystem(
|
func NewFileSystem(
|
||||||
reportFlush func(string) error,
|
reportFlush func(string) error,
|
||||||
reportFsync func(string) error) (fs fuse.FileSystem, err error) {
|
reportFsync func(string) error) (fs fuse.FileSystem, err error) {
|
||||||
fs = &flushFS{}
|
fs = &flushFS{
|
||||||
|
reportFlush: reportFlush,
|
||||||
|
reportFsync: reportFsync,
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +44,8 @@ const fooID = fuse.RootInodeID + 1
|
||||||
|
|
||||||
type flushFS struct {
|
type flushFS struct {
|
||||||
fuseutil.NotImplementedFileSystem
|
fuseutil.NotImplementedFileSystem
|
||||||
|
reportFlush func(string) error
|
||||||
|
reportFsync func(string) error
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
fooContents []byte // GUARDED_BY(mu)
|
fooContents []byte // GUARDED_BY(mu)
|
||||||
|
@ -62,6 +68,7 @@ func (fs *flushFS) fooAttributes() fuse.InodeAttributes {
|
||||||
return fuse.InodeAttributes{
|
return fuse.InodeAttributes{
|
||||||
Nlink: 1,
|
Nlink: 1,
|
||||||
Mode: 0777,
|
Mode: 0777,
|
||||||
|
Size: uint64(len(fs.fooContents)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,3 +175,16 @@ func (fs *flushFS) WriteFile(
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *flushFS) FlushFile(
|
||||||
|
ctx context.Context,
|
||||||
|
req *fuse.FlushFileRequest) (
|
||||||
|
resp *fuse.FlushFileResponse, err error) {
|
||||||
|
resp = &fuse.FlushFileResponse{}
|
||||||
|
|
||||||
|
fs.mu.Lock()
|
||||||
|
defer fs.mu.Unlock()
|
||||||
|
|
||||||
|
err = fs.reportFlush(string(fs.fooContents))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -340,6 +340,10 @@ func (t *FlushFSTest) CloseReports_MultipleTimes_OverlappingFileHandles() {
|
||||||
AssertThat(t.getFsyncs(), ElementsAre())
|
AssertThat(t.getFsyncs(), ElementsAre())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) CloseReports_DuplicatedFileDescriptor() {
|
||||||
|
AssertTrue(false, "TODO")
|
||||||
|
}
|
||||||
|
|
||||||
func (t *FlushFSTest) CloseError() {
|
func (t *FlushFSTest) CloseError() {
|
||||||
// Open the file.
|
// Open the file.
|
||||||
f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0)
|
f, err := os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0)
|
||||||
|
@ -359,7 +363,7 @@ func (t *FlushFSTest) CloseError() {
|
||||||
f = nil
|
f = nil
|
||||||
|
|
||||||
AssertNe(nil, err)
|
AssertNe(nil, err)
|
||||||
ExpectThat(err, Error(HasSubstr("TODO")))
|
ExpectThat(err, Error(HasSubstr("no such file")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FlushFSTest) FsyncReports() {
|
func (t *FlushFSTest) FsyncReports() {
|
||||||
|
@ -425,5 +429,29 @@ func (t *FlushFSTest) FsyncError() {
|
||||||
err = f.Sync()
|
err = f.Sync()
|
||||||
|
|
||||||
AssertNe(nil, err)
|
AssertNe(nil, err)
|
||||||
ExpectThat(err, Error(HasSubstr("TODO")))
|
ExpectThat(err, Error(HasSubstr("no such file")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) Dup() {
|
||||||
|
AssertTrue(false, "TODO")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) Dup_CloseError() {
|
||||||
|
AssertTrue(false, "TODO")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) Dup2() {
|
||||||
|
AssertTrue(false, "TODO")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) Dup2_CloseError() {
|
||||||
|
AssertTrue(false, "TODO")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) Mmap() {
|
||||||
|
AssertTrue(false, "TODO")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) Directory() {
|
||||||
|
AssertTrue(false, "TODO")
|
||||||
}
|
}
|
||||||
|
|
19
server.go
19
server.go
|
@ -427,6 +427,25 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
typed.Respond(fuseResp)
|
typed.Respond(fuseResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *bazilfuse.FlushRequest:
|
||||||
|
// Convert the request.
|
||||||
|
req := &FlushFileRequest{
|
||||||
|
Header: convertHeader(typed.Header),
|
||||||
|
Inode: InodeID(typed.Header.Node),
|
||||||
|
Handle: HandleID(typed.Handle),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the file system.
|
||||||
|
_, err := s.fs.FlushFile(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Println("Responding:", err)
|
||||||
|
typed.RespondError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Println("Responding OK.")
|
||||||
|
typed.Respond()
|
||||||
|
|
||||||
case *bazilfuse.ReleaseRequest:
|
case *bazilfuse.ReleaseRequest:
|
||||||
// Directory or file?
|
// Directory or file?
|
||||||
if typed.Dir {
|
if typed.Dir {
|
||||||
|
|
Loading…
Reference in New Issue