Commit a1e2414a authored by Kirill Smelkov's avatar Kirill Smelkov

Merge branch 'master' into t

* master: (118 commits)
  nodefs: split the dual function of OperationStubs
  nodefs: regularize method naming
  nodefs: test Mknod
  nodefs: by default return input offset for Lseek
  nodefs: rename DefaultOperations to OperationStubs
  nodefs: doc nits.
  nodefs: port utimens and allocate from fuse/nodefs
  fuse: add cancel channel to Release call
  nodefs: provide Inode.Root rather than Inode.IsRoot
  nodefs: drop InodeOf
  nodefs: embed Inode directly in DefaultOperations
  nodefs: fix outdated comment
  nodefs: fix double syscall.Close
  splice: more verbose panic if discarding pipe pair fails
  nodefs: document ToErrno, NewListDirStream
  nodefs: only set default timeout if Entry/Attr does not specify a timeout
  fuse: add Timeout methods to EntryOut and AttrOut
  nodefs: return ENOTSUP rather than ENOSYS
  nodefs: make everything compile on darwin
  nodefs: use syscall.Errno instead of fuse.Status
  ...
parents e439cdfa 8c22b3ff
...@@ -132,10 +132,7 @@ This is not an official Google product. ...@@ -132,10 +132,7 @@ This is not an official Google product.
Grep source code for TODO. Major topics: Grep source code for TODO. Major topics:
* Missing support for network FS file locking: `FUSE_GETLK`, `FUSE_SETLK`, * Missing support for `CUSE`, `BMAP`, `IOCTL`
`FUSE_SETLKW`
* Missing support for `FUSE_INTERRUPT`, `CUSE`, `BMAP`, `IOCTL`
* In the path API, renames are racy; See also: * In the path API, renames are racy; See also:
......
...@@ -172,6 +172,19 @@ type MountOptions struct { ...@@ -172,6 +172,19 @@ type MountOptions struct {
// each method in separate goroutine. // each method in separate goroutine.
// //
// A null implementation is provided by NewDefaultRawFileSystem. // A null implementation is provided by NewDefaultRawFileSystem.
//
// After a successful FUSE API call returns, you may not read input or
// write output data: for performance reasons, memory is reused for
// following requests, and reading/writing the request data will lead
// to race conditions. If you spawn a background routine from a FUSE
// API call, any incoming request data it wants to reference should be
// copied over.
//
// If a FUSE API call is canceled (which is signaled by closing the
// `cancel` channel), the API call should return EINTR. In this case,
// the outstanding request data is not reused, so the API call may
// return EINTR without ensuring that child contexts have successfully
// completed.
type RawFileSystem interface { type RawFileSystem interface {
String() string String() string
...@@ -182,7 +195,7 @@ type RawFileSystem interface { ...@@ -182,7 +195,7 @@ type RawFileSystem interface {
// about a file inside a directory. Many lookup calls can // about a file inside a directory. Many lookup calls can
// occur in parallel, but only one call happens for each (dir, // occur in parallel, but only one call happens for each (dir,
// name) pair. // name) pair.
Lookup(header *InHeader, name string, out *EntryOut) (status Status) Lookup(cancel <-chan struct{}, header *InHeader, name string, out *EntryOut) (status Status)
// Forget is called when the kernel discards entries from its // Forget is called when the kernel discards entries from its
// dentry cache. This happens on unmount, and when the kernel // dentry cache. This happens on unmount, and when the kernel
...@@ -193,53 +206,66 @@ type RawFileSystem interface { ...@@ -193,53 +206,66 @@ type RawFileSystem interface {
Forget(nodeid, nlookup uint64) Forget(nodeid, nlookup uint64)
// Attributes. // Attributes.
GetAttr(input *GetAttrIn, out *AttrOut) (code Status) GetAttr(cancel <-chan struct{}, input *GetAttrIn, out *AttrOut) (code Status)
SetAttr(input *SetAttrIn, out *AttrOut) (code Status) SetAttr(cancel <-chan struct{}, input *SetAttrIn, out *AttrOut) (code Status)
// Modifying structure. // Modifying structure.
Mknod(input *MknodIn, name string, out *EntryOut) (code Status) Mknod(cancel <-chan struct{}, input *MknodIn, name string, out *EntryOut) (code Status)
Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status) Mkdir(cancel <-chan struct{}, input *MkdirIn, name string, out *EntryOut) (code Status)
Unlink(header *InHeader, name string) (code Status) Unlink(cancel <-chan struct{}, header *InHeader, name string) (code Status)
Rmdir(header *InHeader, name string) (code Status) Rmdir(cancel <-chan struct{}, header *InHeader, name string) (code Status)
Rename(input *RenameIn, oldName string, newName string) (code Status) Rename(cancel <-chan struct{}, input *RenameIn, oldName string, newName string) (code Status)
Link(input *LinkIn, filename string, out *EntryOut) (code Status) Link(cancel <-chan struct{}, input *LinkIn, filename string, out *EntryOut) (code Status)
Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) Symlink(cancel <-chan struct{}, header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status)
Readlink(header *InHeader) (out []byte, code Status) Readlink(cancel <-chan struct{}, header *InHeader) (out []byte, code Status)
Access(input *AccessIn) (code Status) Access(cancel <-chan struct{}, input *AccessIn) (code Status)
// Extended attributes. // Extended attributes.
GetXAttrSize(header *InHeader, attr string) (sz int, code Status)
GetXAttrData(header *InHeader, attr string) (data []byte, code Status) // GetXAttr reads an extended attribute, and should return the
ListXAttr(header *InHeader) (attributes []byte, code Status) // number of bytes. If the buffer is too small, return ERANGE,
SetXAttr(input *SetXAttrIn, attr string, data []byte) Status // with the required buffer size.
RemoveXAttr(header *InHeader, attr string) (code Status) GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (sz uint32, code Status)
// ListXAttr lists extended attributes as '\0' delimited byte
// slice, and return the number of bytes. If the buffer is too
// small, return ERANGE, with the required buffer size.
ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (uint32, Status)
// SetAttr writes an extended attribute.
SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status
// RemoveXAttr removes an extended attribute.
RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) (code Status)
// File handling. // File handling.
Create(input *CreateIn, name string, out *CreateOut) (code Status) Create(cancel <-chan struct{}, input *CreateIn, name string, out *CreateOut) (code Status)
Open(input *OpenIn, out *OpenOut) (status Status) Open(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status)
Read(input *ReadIn, buf []byte) (ReadResult, Status) Read(cancel <-chan struct{}, input *ReadIn, buf []byte) (ReadResult, Status)
Lseek(cancel <-chan struct{}, in *LseekIn, out *LseekOut) Status
// File locking // File locking
GetLk(input *LkIn, out *LkOut) (code Status) GetLk(cancel <-chan struct{}, input *LkIn, out *LkOut) (code Status)
SetLk(input *LkIn) (code Status) SetLk(cancel <-chan struct{}, input *LkIn) (code Status)
SetLkw(input *LkIn) (code Status) SetLkw(cancel <-chan struct{}, input *LkIn) (code Status)
Release(cancel <-chan struct{}, input *ReleaseIn)
Write(cancel <-chan struct{}, input *WriteIn, data []byte) (written uint32, code Status)
CopyFileRange(cancel <-chan struct{}, input *CopyFileRangeIn) (written uint32, code Status)
Release(input *ReleaseIn) Flush(cancel <-chan struct{}, input *FlushIn) Status
Write(input *WriteIn, data []byte) (written uint32, code Status) Fsync(cancel <-chan struct{}, input *FsyncIn) (code Status)
Flush(input *FlushIn) Status Fallocate(cancel <-chan struct{}, input *FallocateIn) (code Status)
Fsync(input *FsyncIn) (code Status)
Fallocate(input *FallocateIn) (code Status)
// Directory handling // Directory handling
OpenDir(input *OpenIn, out *OpenOut) (status Status) OpenDir(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status)
ReadDir(input *ReadIn, out *DirEntryList) Status ReadDir(cancel <-chan struct{}, input *ReadIn, out *DirEntryList) Status
ReadDirPlus(input *ReadIn, out *DirEntryList) Status ReadDirPlus(cancel <-chan struct{}, input *ReadIn, out *DirEntryList) Status
ReleaseDir(input *ReleaseIn) ReleaseDir(input *ReleaseIn)
FsyncDir(input *FsyncIn) (code Status) FsyncDir(cancel <-chan struct{}, input *FsyncIn) (code Status)
// StatFs(cancel <-chan struct{}, input *InHeader, out *StatfsOut) (code Status)
StatFs(input *InHeader, out *StatfsOut) (code Status)
// This is called on processing the first request. The // This is called on processing the first request. The
// filesystem implementation can use the server argument to // filesystem implementation can use the server argument to
......
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"context"
"time"
)
// Context passes along cancelation signal and request data (PID, GID,
// UID). The name of this class predates the standard "context"
// package from Go, but it does implement the context.Context
// interface.
//
// When a FUSE request is canceled, the API routine should respond by
// returning the EINTR status code.
type Context struct {
Caller
Cancel <-chan struct{}
}
func (c *Context) Deadline() (time.Time, bool) {
return time.Time{}, false
}
func (c *Context) Done() <-chan struct{} {
return c.Cancel
}
func (c *Context) Err() error {
select {
case <-c.Cancel:
return context.Canceled
default:
return nil
}
}
type callerKeyType struct{}
var callerKey callerKeyType
func FromContext(ctx context.Context) (*Caller, bool) {
v, ok := ctx.Value(callerKey).(*Caller)
return v, ok
}
func NewContext(ctx context.Context, caller *Caller) context.Context {
return context.WithValue(ctx, callerKey, caller)
}
func (c *Context) Value(key interface{}) interface{} {
if key == callerKey {
return &c.Caller
}
return nil
}
var _ = context.Context((*Context)(nil))
...@@ -26,139 +26,143 @@ func (fs *defaultRawFileSystem) String() string { ...@@ -26,139 +26,143 @@ func (fs *defaultRawFileSystem) String() string {
func (fs *defaultRawFileSystem) SetDebug(dbg bool) { func (fs *defaultRawFileSystem) SetDebug(dbg bool) {
} }
func (fs *defaultRawFileSystem) StatFs(header *InHeader, out *StatfsOut) Status { func (fs *defaultRawFileSystem) StatFs(cancel <-chan struct{}, header *InHeader, out *StatfsOut) Status {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Lookup(header *InHeader, name string, out *EntryOut) (code Status) { func (fs *defaultRawFileSystem) Lookup(cancel <-chan struct{}, header *InHeader, name string, out *EntryOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Forget(nodeID, nlookup uint64) { func (fs *defaultRawFileSystem) Forget(nodeID, nlookup uint64) {
} }
func (fs *defaultRawFileSystem) GetAttr(input *GetAttrIn, out *AttrOut) (code Status) { func (fs *defaultRawFileSystem) GetAttr(cancel <-chan struct{}, input *GetAttrIn, out *AttrOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Open(input *OpenIn, out *OpenOut) (status Status) { func (fs *defaultRawFileSystem) Open(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status) {
return OK return OK
} }
func (fs *defaultRawFileSystem) SetAttr(input *SetAttrIn, out *AttrOut) (code Status) { func (fs *defaultRawFileSystem) SetAttr(cancel <-chan struct{}, input *SetAttrIn, out *AttrOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Readlink(header *InHeader) (out []byte, code Status) { func (fs *defaultRawFileSystem) Readlink(cancel <-chan struct{}, header *InHeader) (out []byte, code Status) {
return nil, ENOSYS return nil, ENOSYS
} }
func (fs *defaultRawFileSystem) Mknod(input *MknodIn, name string, out *EntryOut) (code Status) { func (fs *defaultRawFileSystem) Mknod(cancel <-chan struct{}, input *MknodIn, name string, out *EntryOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status) { func (fs *defaultRawFileSystem) Mkdir(cancel <-chan struct{}, input *MkdirIn, name string, out *EntryOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Unlink(header *InHeader, name string) (code Status) { func (fs *defaultRawFileSystem) Unlink(cancel <-chan struct{}, header *InHeader, name string) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Rmdir(header *InHeader, name string) (code Status) { func (fs *defaultRawFileSystem) Rmdir(cancel <-chan struct{}, header *InHeader, name string) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) { func (fs *defaultRawFileSystem) Symlink(cancel <-chan struct{}, header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Rename(input *RenameIn, oldName string, newName string) (code Status) { func (fs *defaultRawFileSystem) Rename(cancel <-chan struct{}, input *RenameIn, oldName string, newName string) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Link(input *LinkIn, name string, out *EntryOut) (code Status) { func (fs *defaultRawFileSystem) Link(cancel <-chan struct{}, input *LinkIn, name string, out *EntryOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) GetXAttrSize(header *InHeader, attr string) (size int, code Status) { func (fs *defaultRawFileSystem) GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (size uint32, code Status) {
return 0, ENOSYS return 0, ENOSYS
} }
func (fs *defaultRawFileSystem) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) { func (fs *defaultRawFileSystem) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status {
return nil, ENOATTR
}
func (fs *defaultRawFileSystem) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) ListXAttr(header *InHeader) (data []byte, code Status) { func (fs *defaultRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (n uint32, code Status) {
return nil, ENOSYS return 0, ENOSYS
} }
func (fs *defaultRawFileSystem) RemoveXAttr(header *InHeader, attr string) Status { func (fs *defaultRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Access(input *AccessIn) (code Status) { func (fs *defaultRawFileSystem) Access(cancel <-chan struct{}, input *AccessIn) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Create(input *CreateIn, name string, out *CreateOut) (code Status) { func (fs *defaultRawFileSystem) Create(cancel <-chan struct{}, input *CreateIn, name string, out *CreateOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) OpenDir(input *OpenIn, out *OpenOut) (status Status) { func (fs *defaultRawFileSystem) OpenDir(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Status) { func (fs *defaultRawFileSystem) Read(cancel <-chan struct{}, input *ReadIn, buf []byte) (ReadResult, Status) {
return nil, ENOSYS return nil, ENOSYS
} }
func (fs *defaultRawFileSystem) GetLk(in *LkIn, out *LkOut) (code Status) { func (fs *defaultRawFileSystem) GetLk(cancel <-chan struct{}, in *LkIn, out *LkOut) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) SetLk(in *LkIn) (code Status) { func (fs *defaultRawFileSystem) SetLk(cancel <-chan struct{}, in *LkIn) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) SetLkw(in *LkIn) (code Status) { func (fs *defaultRawFileSystem) SetLkw(cancel <-chan struct{}, in *LkIn) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Release(input *ReleaseIn) { func (fs *defaultRawFileSystem) Release(cancel <-chan struct{}, input *ReleaseIn) {
} }
func (fs *defaultRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) { func (fs *defaultRawFileSystem) Write(cancel <-chan struct{}, input *WriteIn, data []byte) (written uint32, code Status) {
return 0, ENOSYS return 0, ENOSYS
} }
func (fs *defaultRawFileSystem) Flush(input *FlushIn) Status { func (fs *defaultRawFileSystem) Flush(cancel <-chan struct{}, input *FlushIn) Status {
return OK return OK
} }
func (fs *defaultRawFileSystem) Fsync(input *FsyncIn) (code Status) { func (fs *defaultRawFileSystem) Fsync(cancel <-chan struct{}, input *FsyncIn) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) ReadDir(input *ReadIn, l *DirEntryList) Status { func (fs *defaultRawFileSystem) ReadDir(cancel <-chan struct{}, input *ReadIn, l *DirEntryList) Status {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) ReadDirPlus(input *ReadIn, l *DirEntryList) Status { func (fs *defaultRawFileSystem) ReadDirPlus(cancel <-chan struct{}, input *ReadIn, l *DirEntryList) Status {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) ReleaseDir(input *ReleaseIn) { func (fs *defaultRawFileSystem) ReleaseDir(input *ReleaseIn) {
} }
func (fs *defaultRawFileSystem) FsyncDir(input *FsyncIn) (code Status) { func (fs *defaultRawFileSystem) FsyncDir(cancel <-chan struct{}, input *FsyncIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Fallocate(cancel <-chan struct{}, in *FallocateIn) (code Status) {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) Fallocate(in *FallocateIn) (code Status) { func (fs *defaultRawFileSystem) CopyFileRange(cancel <-chan struct{}, input *CopyFileRangeIn) (written uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) Lseek(cancel <-chan struct{}, in *LseekIn, out *LseekOut) Status {
return ENOSYS return ENOSYS
} }
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
"sync"
)
////////////////////////////////////////////////////////////////
// Locking raw FS.
type lockingRawFileSystem struct {
RawFS RawFileSystem
lock sync.Mutex
}
// Returns a Wrap
func NewLockingRawFileSystem(fs RawFileSystem) RawFileSystem {
return &lockingRawFileSystem{
RawFS: fs,
}
}
func (fs *lockingRawFileSystem) FS() RawFileSystem {
return fs.RawFS
}
func (fs *lockingRawFileSystem) locked() func() {
fs.lock.Lock()
return func() { fs.lock.Unlock() }
}
func (fs *lockingRawFileSystem) Lookup(header *InHeader, name string, out *EntryOut) (code Status) {
defer fs.locked()()
return fs.RawFS.Lookup(header, name, out)
}
func (fs *lockingRawFileSystem) SetDebug(dbg bool) {
defer fs.locked()()
fs.RawFS.SetDebug(dbg)
}
func (fs *lockingRawFileSystem) Forget(nodeID uint64, nlookup uint64) {
defer fs.locked()()
fs.RawFS.Forget(nodeID, nlookup)
}
func (fs *lockingRawFileSystem) GetAttr(input *GetAttrIn, out *AttrOut) (code Status) {
defer fs.locked()()
return fs.RawFS.GetAttr(input, out)
}
func (fs *lockingRawFileSystem) Open(input *OpenIn, out *OpenOut) (status Status) {
defer fs.locked()()
return fs.RawFS.Open(input, out)
}
func (fs *lockingRawFileSystem) SetAttr(input *SetAttrIn, out *AttrOut) (code Status) {
defer fs.locked()()
return fs.RawFS.SetAttr(input, out)
}
func (fs *lockingRawFileSystem) Readlink(header *InHeader) (out []byte, code Status) {
defer fs.locked()()
return fs.RawFS.Readlink(header)
}
func (fs *lockingRawFileSystem) Mknod(input *MknodIn, name string, out *EntryOut) (code Status) {
defer fs.locked()()
return fs.RawFS.Mknod(input, name, out)
}
func (fs *lockingRawFileSystem) Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status) {
defer fs.locked()()
return fs.RawFS.Mkdir(input, name, out)
}
func (fs *lockingRawFileSystem) Unlink(header *InHeader, name string) (code Status) {
defer fs.locked()()
return fs.RawFS.Unlink(header, name)
}
func (fs *lockingRawFileSystem) Rmdir(header *InHeader, name string) (code Status) {
defer fs.locked()()
return fs.RawFS.Rmdir(header, name)
}
func (fs *lockingRawFileSystem) Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) {
defer fs.locked()()
return fs.RawFS.Symlink(header, pointedTo, linkName, out)
}
func (fs *lockingRawFileSystem) Rename(input *RenameIn, oldName string, newName string) (code Status) {
defer fs.locked()()
return fs.RawFS.Rename(input, oldName, newName)
}
func (fs *lockingRawFileSystem) Link(input *LinkIn, name string, out *EntryOut) (code Status) {
defer fs.locked()()
return fs.RawFS.Link(input, name, out)
}
func (fs *lockingRawFileSystem) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status {
defer fs.locked()()
return fs.RawFS.SetXAttr(input, attr, data)
}
func (fs *lockingRawFileSystem) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) {
defer fs.locked()()
return fs.RawFS.GetXAttrData(header, attr)
}
func (fs *lockingRawFileSystem) GetXAttrSize(header *InHeader, attr string) (sz int, code Status) {
defer fs.locked()()
return fs.RawFS.GetXAttrSize(header, attr)
}
func (fs *lockingRawFileSystem) ListXAttr(header *InHeader) (data []byte, code Status) {
defer fs.locked()()
return fs.RawFS.ListXAttr(header)
}
func (fs *lockingRawFileSystem) RemoveXAttr(header *InHeader, attr string) Status {
defer fs.locked()()
return fs.RawFS.RemoveXAttr(header, attr)
}
func (fs *lockingRawFileSystem) Access(input *AccessIn) (code Status) {
defer fs.locked()()
return fs.RawFS.Access(input)
}
func (fs *lockingRawFileSystem) Create(input *CreateIn, name string, out *CreateOut) (code Status) {
defer fs.locked()()
return fs.RawFS.Create(input, name, out)
}
func (fs *lockingRawFileSystem) OpenDir(input *OpenIn, out *OpenOut) (status Status) {
defer fs.locked()()
return fs.RawFS.OpenDir(input, out)
}
func (fs *lockingRawFileSystem) Release(input *ReleaseIn) {
defer fs.locked()()
fs.RawFS.Release(input)
}
func (fs *lockingRawFileSystem) ReleaseDir(input *ReleaseIn) {
defer fs.locked()()
fs.RawFS.ReleaseDir(input)
}
func (fs *lockingRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Status) {
defer fs.locked()()
return fs.RawFS.Read(input, buf)
}
func (fs *lockingRawFileSystem) GetLk(in *LkIn, out *LkOut) (code Status) {
defer fs.locked()()
return fs.RawFS.GetLk(in, out)
}
func (fs *lockingRawFileSystem) SetLk(in *LkIn) (code Status) {
defer fs.locked()()
return fs.RawFS.SetLk(in)
}
func (fs *lockingRawFileSystem) SetLkw(in *LkIn) (code Status) {
defer fs.locked()()
return fs.RawFS.SetLkw(in)
}
func (fs *lockingRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) {
defer fs.locked()()
return fs.RawFS.Write(input, data)
}
func (fs *lockingRawFileSystem) Flush(input *FlushIn) Status {
defer fs.locked()()
return fs.RawFS.Flush(input)
}
func (fs *lockingRawFileSystem) Fsync(input *FsyncIn) (code Status) {
defer fs.locked()()
return fs.RawFS.Fsync(input)
}
func (fs *lockingRawFileSystem) ReadDir(input *ReadIn, out *DirEntryList) Status {
defer fs.locked()()
return fs.RawFS.ReadDir(input, out)
}
func (fs *lockingRawFileSystem) ReadDirPlus(input *ReadIn, out *DirEntryList) Status {
defer fs.locked()()
return fs.RawFS.ReadDirPlus(input, out)
}
func (fs *lockingRawFileSystem) FsyncDir(input *FsyncIn) (code Status) {
defer fs.locked()()
return fs.RawFS.FsyncDir(input)
}
func (fs *lockingRawFileSystem) Init(s *Server) {
defer fs.locked()()
fs.RawFS.Init(s)
}
func (fs *lockingRawFileSystem) StatFs(header *InHeader, out *StatfsOut) (code Status) {
defer fs.locked()()
return fs.RawFS.StatFs(header, out)
}
func (fs *lockingRawFileSystem) Fallocate(in *FallocateIn) (code Status) {
defer fs.locked()()
return fs.RawFS.Fallocate(in)
}
func (fs *lockingRawFileSystem) String() string {
defer fs.locked()()
return fmt.Sprintf("Locked(%s)", fs.RawFS.String())
}
...@@ -22,14 +22,14 @@ type connectorDir struct { ...@@ -22,14 +22,14 @@ type connectorDir struct {
stream []fuse.DirEntry stream []fuse.DirEntry
} }
func (d *connectorDir) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) { func (d *connectorDir) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
// rewinddir() should be as if reopening directory. // rewinddir() should be as if reopening directory.
// TODO - test this. // TODO - test this.
if d.stream == nil || input.Offset == 0 { if d.stream == nil || input.Offset == 0 {
d.stream, code = d.node.OpenDir(&input.Context) d.stream, code = d.node.OpenDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if !code.Ok() { if !code.Ok() {
return code return code
} }
...@@ -58,13 +58,13 @@ func (d *connectorDir) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) (code ...@@ -58,13 +58,13 @@ func (d *connectorDir) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) (code
return fuse.OK return fuse.OK
} }
func (d *connectorDir) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) { func (d *connectorDir) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
// rewinddir() should be as if reopening directory. // rewinddir() should be as if reopening directory.
if d.stream == nil || input.Offset == 0 { if d.stream == nil || input.Offset == 0 {
d.stream, code = d.node.OpenDir(&input.Context) d.stream, code = d.node.OpenDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if !code.Ok() { if !code.Ok() {
return code return code
} }
...@@ -98,7 +98,7 @@ func (d *connectorDir) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) ( ...@@ -98,7 +98,7 @@ func (d *connectorDir) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) (
continue continue
} }
d.rawFS.Lookup(&input.InHeader, e.Name, entryDest) d.rawFS.Lookup(cancel, &input.InHeader, e.Name, entryDest)
} }
return fuse.OK return fuse.OK
} }
......
...@@ -230,7 +230,7 @@ func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode { ...@@ -230,7 +230,7 @@ func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
// This will not affect inode ID lookup counts, which // This will not affect inode ID lookup counts, which
// are only update in response to kernel requests. // are only update in response to kernel requests.
var dummy fuse.InHeader var dummy fuse.InHeader
child, _ := c.internalLookup(&a, parent, r, &dummy) child, _ := c.internalLookup(nil, &a, parent, r, &dummy)
if child == nil { if child == nil {
return nil return nil
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -13,15 +13,7 @@ import ( ...@@ -13,15 +13,7 @@ import (
// FIXME print _Dirent content (reply to READDIR and READDIRPLUS) // FIXME print _Dirent content (reply to READDIR and READDIRPLUS)
var initFlagNames map[int64]string var (
var releaseFlagNames map[int64]string
var OpenFlagNames map[int64]string
var FuseOpenFlagNames map[int64]string
var accessFlagName map[int64]string
var writeFlagNames map[int64]string
var readFlagNames map[int64]string
func init() {
writeFlagNames = map[int64]string{ writeFlagNames = map[int64]string{
WRITE_CACHE: "CACHE", WRITE_CACHE: "CACHE",
WRITE_LOCKOWNER: "LOCKOWNER", WRITE_LOCKOWNER: "LOCKOWNER",
...@@ -59,7 +51,7 @@ func init() { ...@@ -59,7 +51,7 @@ func init() {
releaseFlagNames = map[int64]string{ releaseFlagNames = map[int64]string{
RELEASE_FLUSH: "FLUSH", RELEASE_FLUSH: "FLUSH",
} }
OpenFlagNames = map[int64]string{ openFlagNames = map[int64]string{
int64(os.O_WRONLY): "WRONLY", int64(os.O_WRONLY): "WRONLY",
int64(os.O_RDWR): "RDWR", int64(os.O_RDWR): "RDWR",
int64(os.O_APPEND): "APPEND", int64(os.O_APPEND): "APPEND",
...@@ -77,7 +69,7 @@ func init() { ...@@ -77,7 +69,7 @@ func init() {
// O_LARGEFILE (= 0x8000 in linux, 0 in syscall XXX ?) // O_LARGEFILE (= 0x8000 in linux, 0 in syscall XXX ?)
//0x8000: "LARGE", //0x8000: "LARGE",
} }
FuseOpenFlagNames = map[int64]string{ fuseOpenFlagNames = map[int64]string{
FOPEN_DIRECT_IO: "DIRECT", FOPEN_DIRECT_IO: "DIRECT",
FOPEN_KEEP_CACHE: "CACHE", FOPEN_KEEP_CACHE: "CACHE",
FOPEN_NONSEEKABLE: "NONSEEK", FOPEN_NONSEEKABLE: "NONSEEK",
...@@ -87,10 +79,9 @@ func init() { ...@@ -87,10 +79,9 @@ func init() {
W_OK: "w", W_OK: "w",
R_OK: "r", R_OK: "r",
} }
)
} func flagString(names map[int64]string, fl int64, def string) string {
func FlagString(names map[int64]string, fl int64, def string) string {
s := []string{} s := []string{}
for k, v := range names { for k, v := range names {
if fl&k != 0 { if fl&k != 0 {
...@@ -108,114 +99,114 @@ func FlagString(names map[int64]string, fl int64, def string) string { ...@@ -108,114 +99,114 @@ func FlagString(names map[int64]string, fl int64, def string) string {
return strings.Join(s, ",") return strings.Join(s, ",")
} }
func (me *ForgetIn) string() string { func (in *ForgetIn) string() string {
return fmt.Sprintf("{Nlookup=%d}", me.Nlookup) return fmt.Sprintf("{Nlookup=%d}", in.Nlookup)
} }
func (me *_BatchForgetIn) string() string { func (in *_BatchForgetIn) string() string {
return fmt.Sprintf("{Count=%d}", me.Count) return fmt.Sprintf("{Count=%d}", in.Count)
} }
func (me *MkdirIn) string() string { func (in *MkdirIn) string() string {
return fmt.Sprintf("{0%o (0%o)}", me.Mode, me.Umask) return fmt.Sprintf("{0%o (0%o)}", in.Mode, in.Umask)
} }
func (me *Rename1In) string() string { func (in *Rename1In) string() string {
return fmt.Sprintf("{i%d}", me.Newdir) return fmt.Sprintf("{i%d}", in.Newdir)
} }
func (me *RenameIn) string() string { func (in *RenameIn) string() string {
return fmt.Sprintf("{i%d %x}", me.Newdir, me.Flags) return fmt.Sprintf("{i%d %x}", in.Newdir, in.Flags)
} }
func (me *SetAttrIn) string() string { func (in *SetAttrIn) string() string {
s := []string{} s := []string{}
if me.Valid&FATTR_MODE != 0 { if in.Valid&FATTR_MODE != 0 {
s = append(s, fmt.Sprintf("mode 0%o", me.Mode)) s = append(s, fmt.Sprintf("mode 0%o", in.Mode))
} }
if me.Valid&FATTR_UID != 0 { if in.Valid&FATTR_UID != 0 {
s = append(s, fmt.Sprintf("uid %d", me.Uid)) s = append(s, fmt.Sprintf("uid %d", in.Uid))
} }
if me.Valid&FATTR_GID != 0 { if in.Valid&FATTR_GID != 0 {
s = append(s, fmt.Sprintf("gid %d", me.Gid)) s = append(s, fmt.Sprintf("gid %d", in.Gid))
} }
if me.Valid&FATTR_SIZE != 0 { if in.Valid&FATTR_SIZE != 0 {
s = append(s, fmt.Sprintf("size %d", me.Size)) s = append(s, fmt.Sprintf("size %d", in.Size))
} }
if me.Valid&FATTR_ATIME != 0 { if in.Valid&FATTR_ATIME != 0 {
s = append(s, fmt.Sprintf("atime %d.%09d", me.Atime, me.Atimensec)) s = append(s, fmt.Sprintf("atime %d.%09d", in.Atime, in.Atimensec))
} }
if me.Valid&FATTR_MTIME != 0 { if in.Valid&FATTR_MTIME != 0 {
s = append(s, fmt.Sprintf("mtime %d.%09d", me.Mtime, me.Mtimensec)) s = append(s, fmt.Sprintf("mtime %d.%09d", in.Mtime, in.Mtimensec))
} }
if me.Valid&FATTR_FH != 0 { if in.Valid&FATTR_FH != 0 {
s = append(s, fmt.Sprintf("h%d", me.Fh)) s = append(s, fmt.Sprintf("h%d", in.Fh))
} }
// TODO - FATTR_ATIME_NOW = (1 << 7), FATTR_MTIME_NOW = (1 << 8), FATTR_LOCKOWNER = (1 << 9) // TODO - FATTR_ATIME_NOW = (1 << 7), FATTR_MTIME_NOW = (1 << 8), FATTR_LOCKOWNER = (1 << 9)
return fmt.Sprintf("{%s}", strings.Join(s, ", ")) return fmt.Sprintf("{%s}", strings.Join(s, ", "))
} }
func (me *ReleaseIn) string() string { func (in *ReleaseIn) string() string {
return fmt.Sprintf("{h%d %s %s L%d}", return fmt.Sprintf("{h%d %s %s L%d}",
me.Fh, FlagString(OpenFlagNames, int64(me.Flags), ""), in.Fh, flagString(openFlagNames, int64(in.Flags), ""),
FlagString(releaseFlagNames, int64(me.ReleaseFlags), ""), flagString(releaseFlagNames, int64(in.ReleaseFlags), ""),
me.LockOwner) in.LockOwner)
} }
func (me *OpenIn) string() string { func (in *OpenIn) string() string {
return fmt.Sprintf("{%s}", FlagString(OpenFlagNames, int64(me.Flags), "O_RDONLY")) return fmt.Sprintf("{%s}", flagString(openFlagNames, int64(in.Flags), "O_RDONLY"))
} }
func (me *OpenOut) string() string { func (in *OpenOut) string() string {
return fmt.Sprintf("{h%d %s}", me.Fh, return fmt.Sprintf("{h%d %s}", in.Fh,
FlagString(FuseOpenFlagNames, int64(me.OpenFlags), "")) flagString(fuseOpenFlagNames, int64(in.OpenFlags), ""))
} }
func (me *InitIn) string() string { func (in *InitIn) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s}", return fmt.Sprintf("{%d.%d Ra 0x%x %s}",
me.Major, me.Minor, me.MaxReadAhead, in.Major, in.Minor, in.MaxReadAhead,
FlagString(initFlagNames, int64(me.Flags), "")) flagString(initFlagNames, int64(in.Flags), ""))
} }
func (me *InitOut) string() string { func (o *InitOut) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s %d/%d Wr 0x%x Tg 0x%x}", return fmt.Sprintf("{%d.%d Ra 0x%x %s %d/%d Wr 0x%x Tg 0x%x}",
me.Major, me.Minor, me.MaxReadAhead, o.Major, o.Minor, o.MaxReadAhead,
FlagString(initFlagNames, int64(me.Flags), ""), flagString(initFlagNames, int64(o.Flags), ""),
me.CongestionThreshold, me.MaxBackground, me.MaxWrite, o.CongestionThreshold, o.MaxBackground, o.MaxWrite,
me.TimeGran) o.TimeGran)
} }
func (s *FsyncIn) string() string { func (s *FsyncIn) string() string {
return fmt.Sprintf("{h%d Flags %x}", s.Fh, s.FsyncFlags) return fmt.Sprintf("{h%d Flags %x}", s.Fh, s.FsyncFlags)
} }
func (me *SetXAttrIn) string() string { func (in *SetXAttrIn) string() string {
return fmt.Sprintf("{sz %d f%o}", me.Size, me.Flags) return fmt.Sprintf("{sz %d f%o}", in.Size, in.Flags)
} }
func (me *GetXAttrIn) string() string { func (in *GetXAttrIn) string() string {
return fmt.Sprintf("{sz %d}", me.Size) return fmt.Sprintf("{sz %d}", in.Size)
} }
func (me *GetXAttrOut) string() string { func (o *GetXAttrOut) string() string {
return fmt.Sprintf("{sz %d}", me.Size) return fmt.Sprintf("{sz %d}", o.Size)
} }
func (me *AccessIn) string() string { func (in *AccessIn) string() string {
return fmt.Sprintf("{u=%d g=%d %s}", return fmt.Sprintf("{u=%d g=%d %s}",
me.Uid, in.Uid,
me.Gid, in.Gid,
FlagString(accessFlagName, int64(me.Mask), "")) flagString(accessFlagName, int64(in.Mask), ""))
} }
func (me *FlushIn) string() string { func (in *FlushIn) string() string {
return fmt.Sprintf("{h%d}", me.Fh) return fmt.Sprintf("{h%d}", in.Fh)
} }
func (me *AttrOut) string() string { func (o *AttrOut) string() string {
return fmt.Sprintf( return fmt.Sprintf(
"{tA=%gs %v}", "{tA=%gs %v}",
ft(me.AttrValid, me.AttrValidNsec), &me.Attr) ft(o.AttrValid, o.AttrValidNsec), &o.Attr)
} }
// ft converts (seconds , nanoseconds) -> float(seconds) // ft converts (seconds , nanoseconds) -> float(seconds)
...@@ -224,21 +215,21 @@ func ft(tsec uint64, tnsec uint32) float64 { ...@@ -224,21 +215,21 @@ func ft(tsec uint64, tnsec uint32) float64 {
} }
// Returned by LOOKUP // Returned by LOOKUP
func (me *EntryOut) string() string { func (o *EntryOut) string() string {
return fmt.Sprintf("{i%d g%d tE=%gs tA=%gs %v}", return fmt.Sprintf("{i%d g%d tE=%gs tA=%gs %v}",
me.NodeId, me.Generation, ft(me.EntryValid, me.EntryValidNsec), o.NodeId, o.Generation, ft(o.EntryValid, o.EntryValidNsec),
ft(me.AttrValid, me.AttrValidNsec), &me.Attr) ft(o.AttrValid, o.AttrValidNsec), &o.Attr)
} }
func (me *CreateOut) string() string { func (o *CreateOut) string() string {
return fmt.Sprintf("{i%d g%d %v %v}", me.NodeId, me.Generation, &me.EntryOut, &me.OpenOut) return fmt.Sprintf("{i%d g%d %v %v}", o.NodeId, o.Generation, &o.EntryOut, &o.OpenOut)
} }
func (me *StatfsOut) string() string { func (o *StatfsOut) string() string {
return fmt.Sprintf( return fmt.Sprintf(
"{blocks (%d,%d)/%d files %d/%d bs%d nl%d frs%d}", "{blocks (%d,%d)/%d files %d/%d bs%d nl%d frs%d}",
me.Bfree, me.Bavail, me.Blocks, me.Ffree, me.Files, o.Bfree, o.Bavail, o.Blocks, o.Ffree, o.Files,
me.Bsize, me.NameLen, me.Frsize) o.Bsize, o.NameLen, o.Frsize)
} }
func (o *NotifyInvalEntryOut) string() string { func (o *NotifyInvalEntryOut) string() string {
...@@ -274,10 +265,36 @@ func (f *LinkIn) string() string { ...@@ -274,10 +265,36 @@ func (f *LinkIn) string() string {
return fmt.Sprintf("{Oldnodeid: %d}", f.Oldnodeid) return fmt.Sprintf("{Oldnodeid: %d}", f.Oldnodeid)
} }
func (o *WriteOut) string() string {
return fmt.Sprintf("{%db }", o.Size)
}
func (i *CopyFileRangeIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) => i%d Fh %d [%d, %d)}",
i.FhIn, i.OffIn, i.Len, i.NodeIdOut, i.FhOut, i.OffOut, i.Len)
}
func (in *InterruptIn) string() string { func (in *InterruptIn) string() string {
return fmt.Sprintf("{ix %d}", in.Unique) return fmt.Sprintf("{ix %d}", in.Unique)
} }
var seekNames = map[uint32]string{
0: "SET",
1: "CUR",
2: "END",
3: "DATA",
4: "HOLE",
}
func (in *LseekIn) string() string {
return fmt.Sprintf("{Fh %d [%s +%d)}", in.Fh,
seekNames[in.Whence], in.Offset)
}
func (o *LseekOut) string() string {
return fmt.Sprintf("{%d}", o.Offset)
}
// Print pretty prints FUSE data types for kernel communication // Print pretty prints FUSE data types for kernel communication
func Print(obj interface{}) string { func Print(obj interface{}) string {
t, ok := obj.(interface { t, ok := obj.(interface {
......
...@@ -32,7 +32,7 @@ func (a *Attr) string() string { ...@@ -32,7 +32,7 @@ func (a *Attr) string() string {
func (me *CreateIn) string() string { func (me *CreateIn) string() string {
return fmt.Sprintf( return fmt.Sprintf(
"{0%o [%s]}", me.Mode, "{0%o [%s]}", me.Mode,
FlagString(OpenFlagNames, int64(me.Flags), "O_RDONLY")) flagString(openFlagNames, int64(me.Flags), "O_RDONLY"))
} }
func (me *GetAttrIn) string() string { return "" } func (me *GetAttrIn) string() string { return "" }
...@@ -44,11 +44,11 @@ func (me *MknodIn) string() string { ...@@ -44,11 +44,11 @@ func (me *MknodIn) string() string {
func (me *ReadIn) string() string { func (me *ReadIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s}", return fmt.Sprintf("{Fh %d [%d +%d) %s}",
me.Fh, me.Offset, me.Size, me.Fh, me.Offset, me.Size,
FlagString(readFlagNames, int64(me.ReadFlags), "")) flagString(readFlagNames, int64(me.ReadFlags), ""))
} }
func (me *WriteIn) string() string { func (me *WriteIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s}", return fmt.Sprintf("{Fh %d [%d +%d) %s}",
me.Fh, me.Offset, me.Size, me.Fh, me.Offset, me.Size,
FlagString(writeFlagNames, int64(me.WriteFlags), "")) flagString(writeFlagNames, int64(me.WriteFlags), ""))
} }
...@@ -10,10 +10,9 @@ import ( ...@@ -10,10 +10,9 @@ import (
) )
func init() { func init() {
OpenFlagNames[syscall.O_DIRECT] = "DIRECT" openFlagNames[syscall.O_DIRECT] = "DIRECT"
OpenFlagNames[syscall.O_LARGEFILE] = "LARGEFILE" openFlagNames[syscall.O_LARGEFILE] = "LARGEFILE"
OpenFlagNames[syscall_O_NOATIME] = "NOATIME" openFlagNames[syscall_O_NOATIME] = "NOATIME"
} }
func (a *Attr) string() string { func (a *Attr) string() string {
...@@ -31,46 +30,40 @@ func (a *Attr) string() string { ...@@ -31,46 +30,40 @@ func (a *Attr) string() string {
ft(a.Ctime, a.Ctimensec)) ft(a.Ctime, a.Ctimensec))
} }
func (me *CreateIn) string() string { func (in *CreateIn) string() string {
return fmt.Sprintf( return fmt.Sprintf(
"{0%o [%s] (0%o)}", me.Mode, "{0%o [%s] (0%o)}", in.Mode,
FlagString(OpenFlagNames, int64(me.Flags), "O_RDONLY"), me.Umask) flagString(openFlagNames, int64(in.Flags), "O_RDONLY"), in.Umask)
} }
func (me *GetAttrIn) string() string { func (in *GetAttrIn) string() string {
return fmt.Sprintf("{h%d}", me.Fh_) return fmt.Sprintf("{h%d}", in.Fh_)
} }
func (me *MknodIn) string() string { func (in *MknodIn) string() string {
return fmt.Sprintf("{0%o (0%o), %d}", me.Mode, me.Umask, me.Rdev) return fmt.Sprintf("{0%o (0%o), %d}", in.Mode, in.Umask, in.Rdev)
} }
func (me *ReadIn) string() string { func (in *ReadIn) string() string {
// δ: `Fh X` -> hX; Lx - only if READ_LOCKOWNER // δ: `Fh X` -> hX; Lx - only if READ_LOCKOWNER
s := fmt.Sprintf("{h%d [%d +%d)", me.Fh, me.Offset, me.Size) s := fmt.Sprintf("{h%d [%d +%d)", in.Fh, in.Offset, in.Size)
rflags := me.ReadFlags &^ READ_LOCKOWNER rflags := in.ReadFlags &^ READ_LOCKOWNER
if rflags != 0 { if rflags != 0 {
s += " " + FlagString(readFlagNames, int64(rflags), "") s += " " + flagString(readFlagNames, int64(rflags), "")
} }
s += " " + FlagString(OpenFlagNames, int64(me.Flags), "RDONLY") s += " " + flagString(openFlagNames, int64(in.Flags), "RDONLY")
if me.ReadFlags & READ_LOCKOWNER != 0 { if in.ReadFlags & READ_LOCKOWNER != 0 {
s += fmt.Sprintf(" L%x", me.LockOwner) s += fmt.Sprintf(" L%x", in.LockOwner)
} }
s += "}" s += "}"
return s return s
//return fmt.Sprintf("{h%d [%d +%d) %s L%d %s}",
// me.Fh, me.Offset, me.Size,
// FlagString(readFlagNames, int64(me.ReadFlags), ""),
// me.LockOwner,
// FlagString(OpenFlagNames, int64(me.Flags), "RDONLY"))
} }
func (me *WriteIn) string() string { func (in *WriteIn) string() string {
// TODO Lx optional - only if WRITE_LOCKOWNER is set // TODO Lx optional - only if WRITE_LOCKOWNER is set
return fmt.Sprintf("{h%d [%d +%d) %s L%d %s}", return fmt.Sprintf("{h%d [%d +%d) %s L%d %s}",
me.Fh, me.Offset, me.Size, in.Fh, in.Offset, in.Size,
FlagString(writeFlagNames, int64(me.WriteFlags), ""), flagString(writeFlagNames, int64(in.WriteFlags), ""),
me.LockOwner, in.LockOwner,
FlagString(OpenFlagNames, int64(me.Flags), "RDONLY")) flagString(openFlagNames, int64(in.Flags), "RDONLY"))
} }
...@@ -217,8 +217,14 @@ func (r *request) outData() unsafe.Pointer { ...@@ -217,8 +217,14 @@ func (r *request) outData() unsafe.Pointer {
// serializeHeader serializes the response header. The header points // serializeHeader serializes the response header. The header points
// to an internal buffer of the receiver. // to an internal buffer of the receiver.
func (r *request) serializeHeader(flatDataSize int) (header []byte) { func (r *request) serializeHeader(flatDataSize int) (header []byte) {
dataLength := r.handler.OutputSize var dataLength uintptr
if r.handler != nil {
dataLength = r.handler.OutputSize
}
if r.status > OK { if r.status > OK {
// only do this for positive status; negative status
// is used for notification.
dataLength = 0 dataLength = 0
} }
......
...@@ -62,6 +62,9 @@ type Server struct { ...@@ -62,6 +62,9 @@ type Server struct {
loops sync.WaitGroup loops sync.WaitGroup
ready chan error ready chan error
// for implementing single threaded processing.
requestProcessingMu sync.Mutex
} }
// SetDebug is deprecated. Use MountOptions.Debug instead. // SetDebug is deprecated. Use MountOptions.Debug instead.
...@@ -132,9 +135,6 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server ...@@ -132,9 +135,6 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server
} }
} }
o := *opts o := *opts
if o.SingleThreaded {
fs = NewLockingRawFileSystem(fs)
}
if o.MaxWrite < 0 { if o.MaxWrite < 0 {
o.MaxWrite = 0 o.MaxWrite = 0
...@@ -382,6 +382,11 @@ func (ms *Server) Serve() { ...@@ -382,6 +382,11 @@ func (ms *Server) Serve() {
} }
} }
// Wait waits for the serve loop to exit
func (ms *Server) Wait() {
ms.loops.Wait()
}
func (ms *Server) handleInit() Status { func (ms *Server) handleInit() Status {
// The first request should be INIT; read it synchronously, // The first request should be INIT; read it synchronously,
// and don't spawn new readers. // and don't spawn new readers.
...@@ -435,6 +440,11 @@ exit: ...@@ -435,6 +440,11 @@ exit:
} }
func (ms *Server) handleRequest(req *request) Status { func (ms *Server) handleRequest(req *request) Status {
if ms.opts.SingleThreaded {
ms.requestProcessingMu.Lock()
defer ms.requestProcessingMu.Unlock()
}
req.parse() req.parse()
if req.handler == nil { if req.handler == nil {
req.status = ENOSYS req.status = ENOSYS
......
...@@ -103,7 +103,7 @@ func NewTestCase(t *testing.T) *testCase { ...@@ -103,7 +103,7 @@ func NewTestCase(t *testing.T) *testCase {
LookupKnownChildren: true, LookupKnownChildren: true,
}) })
tc.state, err = fuse.NewServer( tc.state, err = fuse.NewServer(
fuse.NewRawFileSystem(tc.connector.RawFS()), tc.mnt, &fuse.MountOptions{ tc.connector.RawFS(), tc.mnt, &fuse.MountOptions{
SingleThreaded: true, SingleThreaded: true,
Debug: testutil.VerboseTest(), Debug: testutil.VerboseTest(),
}) })
......
...@@ -57,7 +57,7 @@ func TestUmask(t *testing.T) { ...@@ -57,7 +57,7 @@ func TestUmask(t *testing.T) {
LookupKnownChildren: true, LookupKnownChildren: true,
}) })
server, err := fuse.NewServer( server, err := fuse.NewServer(
fuse.NewRawFileSystem(connector.RawFS()), mnt, &fuse.MountOptions{ connector.RawFS(), mnt, &fuse.MountOptions{
SingleThreaded: true, SingleThreaded: true,
Debug: testutil.VerboseTest(), Debug: testutil.VerboseTest(),
}) })
......
...@@ -29,6 +29,9 @@ const ( ...@@ -29,6 +29,9 @@ const (
// EAGAIN Resource temporarily unavailable // EAGAIN Resource temporarily unavailable
EAGAIN = Status(syscall.EAGAIN) EAGAIN = Status(syscall.EAGAIN)
// EINTR Call was interrupted
EINTR = Status(syscall.EINTR)
// EINVAL Invalid argument // EINVAL Invalid argument
EINVAL = Status(syscall.EINVAL) EINVAL = Status(syscall.EINVAL)
...@@ -47,6 +50,9 @@ const ( ...@@ -47,6 +50,9 @@ const (
// ENOTDIR Not a directory // ENOTDIR Not a directory
ENOTDIR = Status(syscall.ENOTDIR) ENOTDIR = Status(syscall.ENOTDIR)
// ENOTSUP Not supported
ENOTSUP = Status(syscall.ENOTSUP)
// EISDIR Is a directory // EISDIR Is a directory
EISDIR = Status(syscall.EISDIR) EISDIR = Status(syscall.EISDIR)
...@@ -303,7 +309,9 @@ type InitOut struct { ...@@ -303,7 +309,9 @@ type InitOut struct {
CongestionThreshold uint16 CongestionThreshold uint16
MaxWrite uint32 MaxWrite uint32
TimeGran uint32 TimeGran uint32
Unused [9]uint32 MaxPages uint16
Padding uint16
Unused [8]uint32
} }
type _CuseInitIn struct { type _CuseInitIn struct {
...@@ -502,6 +510,33 @@ type FlushIn struct { ...@@ -502,6 +510,33 @@ type FlushIn struct {
LockOwner uint64 LockOwner uint64
} }
type LseekIn struct {
InHeader
Fh uint64
Offset uint64
Whence uint32
Padding uint32
}
type LseekOut struct {
Offset uint64
}
type CopyFileRangeIn struct {
InHeader
FhIn uint64
OffIn uint64
NodeIdOut uint64
FhOut uint64
OffOut uint64
Len uint64
Flags uint64
}
// EntryOut holds the result of a (directory,name) lookup. It has two
// TTLs, one for the (directory, name) lookup itself, and one for the
// attributes (eg. size, mode). The entry TTL also applies if the
// lookup result is ENOENT ("negative entry lookup")
type EntryOut struct { type EntryOut struct {
NodeId uint64 NodeId uint64
Generation uint64 Generation uint64
...@@ -512,6 +547,15 @@ type EntryOut struct { ...@@ -512,6 +547,15 @@ type EntryOut struct {
Attr Attr
} }
// EntryTimeout returns entry timeout currently
func (o *EntryOut) EntryTimeout() time.Duration {
return time.Duration(uint64(o.EntryValidNsec) + o.EntryValid*1e9)
}
func (o *EntryOut) AttrTimeout() time.Duration {
return time.Duration(uint64(o.AttrValidNsec) + o.AttrValid*1e9)
}
func (o *EntryOut) SetEntryTimeout(dt time.Duration) { func (o *EntryOut) SetEntryTimeout(dt time.Duration) {
ns := int64(dt) ns := int64(dt)
o.EntryValidNsec = uint32(ns % 1e9) o.EntryValidNsec = uint32(ns % 1e9)
...@@ -531,6 +575,10 @@ type AttrOut struct { ...@@ -531,6 +575,10 @@ type AttrOut struct {
Attr Attr
} }
func (o *AttrOut) Timeout() time.Duration {
return time.Duration(uint64(o.AttrValidNsec) + o.AttrValid*1e9)
}
func (o *AttrOut) SetTimeout(dt time.Duration) { func (o *AttrOut) SetTimeout(dt time.Duration) {
ns := int64(dt) ns := int64(dt)
o.AttrValidNsec = uint32(ns % 1e9) o.AttrValidNsec = uint32(ns % 1e9)
...@@ -542,7 +590,11 @@ type CreateOut struct { ...@@ -542,7 +590,11 @@ type CreateOut struct {
OpenOut OpenOut
} }
type Context struct { // Caller has data on the process making the FS call.
//
// The UID and GID are effective UID/GID, except for the ACCESS
// opcode, where UID and GID are the real UIDs
type Caller struct {
Owner Owner
Pid uint32 Pid uint32
} }
...@@ -552,7 +604,7 @@ type InHeader struct { ...@@ -552,7 +604,7 @@ type InHeader struct {
Opcode int32 Opcode int32
Unique uint64 Unique uint64
NodeId uint64 NodeId uint64
Context Caller
Padding uint32 Padding uint32
} }
......
...@@ -153,4 +153,14 @@ func (s *StatfsOut) FromStatfsT(statfs *syscall.Statfs_t) { ...@@ -153,4 +153,14 @@ func (s *StatfsOut) FromStatfsT(statfs *syscall.Statfs_t) {
s.Ffree = statfs.Ffree s.Ffree = statfs.Ffree
s.Bsize = uint32(statfs.Iosize) // Iosize translates to Bsize: the optimal transfer size. s.Bsize = uint32(statfs.Iosize) // Iosize translates to Bsize: the optimal transfer size.
s.Frsize = s.Bsize // Bsize translates to Frsize: the minimum transfer size. s.Frsize = s.Bsize // Bsize translates to Frsize: the minimum transfer size.
// The block counts are in units of statfs.Bsize.
// If s.Bsize != statfs.Bsize, we have to recalculate the block counts
// accordingly (s.Bsize is usually 256*statfs.Bsize).
if s.Bsize > statfs.Bsize {
adj := uint64(s.Bsize / statfs.Bsize)
s.Blocks /= adj
s.Bfree /= adj
s.Bavail /= adj
}
} }
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
)
// NewRawFileSystem adds the methods missing for implementing a
// RawFileSystem to any object.
func NewRawFileSystem(fs interface{}) RawFileSystem {
return &wrappingFS{fs}
}
type wrappingFS struct {
fs interface{}
}
func (fs *wrappingFS) Init(srv *Server) {
if s, ok := fs.fs.(interface {
Init(*Server)
}); ok {
s.Init(srv)
}
}
func (fs *wrappingFS) String() string {
return fmt.Sprintf("%v", fs.fs)
}
func (fs *wrappingFS) SetDebug(dbg bool) {
if s, ok := fs.fs.(interface {
SetDebug(bool)
}); ok {
s.SetDebug(dbg)
}
}
func (fs *wrappingFS) StatFs(header *InHeader, out *StatfsOut) Status {
if s, ok := fs.fs.(interface {
StatFs(header *InHeader, out *StatfsOut) Status
}); ok {
return s.StatFs(header, out)
}
return ENOSYS
}
func (fs *wrappingFS) Lookup(header *InHeader, name string, out *EntryOut) (code Status) {
if s, ok := fs.fs.(interface {
Lookup(header *InHeader, name string, out *EntryOut) (code Status)
}); ok {
return s.Lookup(header, name, out)
}
return ENOSYS
}
func (fs *wrappingFS) Forget(nodeID, nlookup uint64) {
if s, ok := fs.fs.(interface {
Forget(nodeID, nlookup uint64)
}); ok {
s.Forget(nodeID, nlookup)
}
}
func (fs *wrappingFS) GetAttr(input *GetAttrIn, out *AttrOut) (code Status) {
if s, ok := fs.fs.(interface {
GetAttr(input *GetAttrIn, out *AttrOut) (code Status)
}); ok {
return s.GetAttr(input, out)
}
return ENOSYS
}
func (fs *wrappingFS) Open(input *OpenIn, out *OpenOut) (status Status) {
if s, ok := fs.fs.(interface {
Open(input *OpenIn, out *OpenOut) (status Status)
}); ok {
return s.Open(input, out)
}
return ENOSYS
}
func (fs *wrappingFS) SetAttr(input *SetAttrIn, out *AttrOut) (code Status) {
if s, ok := fs.fs.(interface {
SetAttr(input *SetAttrIn, out *AttrOut) (code Status)
}); ok {
return s.SetAttr(input, out)
}
return ENOSYS
}
func (fs *wrappingFS) Readlink(header *InHeader) (out []byte, code Status) {
if s, ok := fs.fs.(interface {
Readlink(header *InHeader) (out []byte, code Status)
}); ok {
return s.Readlink(header)
}
return nil, ENOSYS
}
func (fs *wrappingFS) Mknod(input *MknodIn, name string, out *EntryOut) (code Status) {
if s, ok := fs.fs.(interface {
Mknod(input *MknodIn, name string, out *EntryOut) (code Status)
}); ok {
return s.Mknod(input, name, out)
}
return ENOSYS
}
func (fs *wrappingFS) Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status) {
if s, ok := fs.fs.(interface {
Mkdir(input *MkdirIn, name string, out *EntryOut) (code Status)
}); ok {
return s.Mkdir(input, name, out)
}
return ENOSYS
}
func (fs *wrappingFS) Unlink(header *InHeader, name string) (code Status) {
if s, ok := fs.fs.(interface {
Unlink(header *InHeader, name string) (code Status)
}); ok {
return s.Unlink(header, name)
}
return ENOSYS
}
func (fs *wrappingFS) Rmdir(header *InHeader, name string) (code Status) {
if s, ok := fs.fs.(interface {
Rmdir(header *InHeader, name string) (code Status)
}); ok {
return s.Rmdir(header, name)
}
return ENOSYS
}
func (fs *wrappingFS) Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) {
if s, ok := fs.fs.(interface {
Symlink(header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status)
}); ok {
return s.Symlink(header, pointedTo, linkName, out)
}
return ENOSYS
}
func (fs *wrappingFS) Rename(input *RenameIn, oldName string, newName string) (code Status) {
if s, ok := fs.fs.(interface {
Rename(input *RenameIn, oldName string, newName string) (code Status)
}); ok {
return s.Rename(input, oldName, newName)
}
return ENOSYS
}
func (fs *wrappingFS) Link(input *LinkIn, name string, out *EntryOut) (code Status) {
if s, ok := fs.fs.(interface {
Link(input *LinkIn, name string, out *EntryOut) (code Status)
}); ok {
return s.Link(input, name, out)
}
return ENOSYS
}
func (fs *wrappingFS) GetXAttrSize(header *InHeader, attr string) (size int, code Status) {
if s, ok := fs.fs.(interface {
GetXAttrSize(header *InHeader, attr string) (size int, code Status)
}); ok {
return s.GetXAttrSize(header, attr)
}
return 0, ENOSYS
}
func (fs *wrappingFS) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) {
if s, ok := fs.fs.(interface {
GetXAttrData(header *InHeader, attr string) (data []byte, code Status)
}); ok {
return s.GetXAttrData(header, attr)
}
return nil, ENOSYS
}
func (fs *wrappingFS) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status {
if s, ok := fs.fs.(interface {
SetXAttr(input *SetXAttrIn, attr string, data []byte) Status
}); ok {
return s.SetXAttr(input, attr, data)
}
return ENOSYS
}
func (fs *wrappingFS) ListXAttr(header *InHeader) (data []byte, code Status) {
if s, ok := fs.fs.(interface {
ListXAttr(header *InHeader) (data []byte, code Status)
}); ok {
return s.ListXAttr(header)
}
return nil, ENOSYS
}
func (fs *wrappingFS) RemoveXAttr(header *InHeader, attr string) Status {
if s, ok := fs.fs.(interface {
RemoveXAttr(header *InHeader, attr string) Status
}); ok {
return s.RemoveXAttr(header, attr)
}
return ENOSYS
}
func (fs *wrappingFS) Access(input *AccessIn) (code Status) {
if s, ok := fs.fs.(interface {
Access(input *AccessIn) (code Status)
}); ok {
return s.Access(input)
}
return ENOSYS
}
func (fs *wrappingFS) Create(input *CreateIn, name string, out *CreateOut) (code Status) {
if s, ok := fs.fs.(interface {
Create(input *CreateIn, name string, out *CreateOut) (code Status)
}); ok {
return s.Create(input, name, out)
}
return ENOSYS
}
func (fs *wrappingFS) OpenDir(input *OpenIn, out *OpenOut) (status Status) {
if s, ok := fs.fs.(interface {
OpenDir(input *OpenIn, out *OpenOut) (status Status)
}); ok {
return s.OpenDir(input, out)
}
return ENOSYS
}
func (fs *wrappingFS) Read(input *ReadIn, buf []byte) (ReadResult, Status) {
if s, ok := fs.fs.(interface {
Read(input *ReadIn, buf []byte) (ReadResult, Status)
}); ok {
return s.Read(input, buf)
}
return nil, ENOSYS
}
func (fs *wrappingFS) GetLk(in *LkIn, out *LkOut) (code Status) {
if s, ok := fs.fs.(interface {
GetLk(in *LkIn, out *LkOut) (code Status)
}); ok {
return s.GetLk(in, out)
}
return ENOSYS
}
func (fs *wrappingFS) SetLk(in *LkIn) (code Status) {
if s, ok := fs.fs.(interface {
SetLk(in *LkIn) (code Status)
}); ok {
return s.SetLk(in)
}
return ENOSYS
}
func (fs *wrappingFS) SetLkw(in *LkIn) (code Status) {
if s, ok := fs.fs.(interface {
SetLkw(in *LkIn) (code Status)
}); ok {
return s.SetLkw(in)
}
return ENOSYS
}
func (fs *wrappingFS) Release(input *ReleaseIn) {
if s, ok := fs.fs.(interface {
Release(input *ReleaseIn)
}); ok {
s.Release(input)
}
}
func (fs *wrappingFS) Write(input *WriteIn, data []byte) (written uint32, code Status) {
if s, ok := fs.fs.(interface {
Write(input *WriteIn, data []byte) (written uint32, code Status)
}); ok {
return s.Write(input, data)
}
return 0, ENOSYS
}
func (fs *wrappingFS) Flush(input *FlushIn) Status {
if s, ok := fs.fs.(interface {
Flush(input *FlushIn) Status
}); ok {
return s.Flush(input)
}
return OK
}
func (fs *wrappingFS) Fsync(input *FsyncIn) (code Status) {
if s, ok := fs.fs.(interface {
Fsync(input *FsyncIn) (code Status)
}); ok {
return s.Fsync(input)
}
return ENOSYS
}
func (fs *wrappingFS) ReadDir(input *ReadIn, l *DirEntryList) Status {
if s, ok := fs.fs.(interface {
ReadDir(input *ReadIn, l *DirEntryList) Status
}); ok {
return s.ReadDir(input, l)
}
return ENOSYS
}
func (fs *wrappingFS) ReadDirPlus(input *ReadIn, l *DirEntryList) Status {
if s, ok := fs.fs.(interface {
ReadDirPlus(input *ReadIn, l *DirEntryList) Status
}); ok {
return s.ReadDirPlus(input, l)
}
return ENOSYS
}
func (fs *wrappingFS) ReleaseDir(input *ReleaseIn) {
if s, ok := fs.fs.(interface {
ReleaseDir(input *ReleaseIn)
}); ok {
s.ReleaseDir(input)
}
}
func (fs *wrappingFS) FsyncDir(input *FsyncIn) (code Status) {
if s, ok := fs.fs.(interface {
FsyncDir(input *FsyncIn) (code Status)
}); ok {
return s.FsyncDir(input)
}
return ENOSYS
}
func (fs *wrappingFS) Fallocate(in *FallocateIn) (code Status) {
if s, ok := fs.fs.(interface {
Fallocate(in *FallocateIn) (code Status)
}); ok {
return s.Fallocate(in)
}
return ENOSYS
}
Objective
=========
A high-performance FUSE API that minimizes pitfalls with writing
correct filesystems.
Decisions
=========
* Nodes contain references to their children. This is useful
because most filesystems will need to construct tree-like
structures.
* Nodes contain references to their parents. As a result, we can
derive the path for each Inode, and there is no need for a
separate PathFS.
* Nodes can be "persistent", meaning their lifetime is not under
control of the kernel. This is useful for constructing FS trees
in advance, rather than driven by LOOKUP.
* The NodeID for FS tree node must be defined on creation and are
immutable. By contrast, reusing NodeIds (eg. rsc/bazil FUSE, as
well as old go-fuse/fuse/nodefs) needs extra synchronization to
avoid races with notify and FORGET, and makes handling the inode
Generation more complicated.
* The mode of an Inode is defined on creation. Files cannot change
type during their lifetime. This also prevents the common error
of forgetting to return the filetype in Lookup/GetAttr.
* The NodeID (used for communicating with kernel) is equal to
Attr.Ino (value shown in Stat and Lstat return values.).
* No global treelock, to ensure scalability.
* Support for hard links. libfuse doesn't support this in the
high-level API. Extra care for race conditions is needed when
looking up the same file through different paths.
* do not issue Notify{Entry,Delete} as part of
AddChild/RmChild/MvChild: because NodeIDs are unique and
immutable, there is no confusion about which nodes are
invalidated, and the notification doesn't have to happen under
lock.
* Directory reading uses the DirStream. Semantics for rewinding
directory reads, and adding files after opening (but before
reading) are handled automatically. No support for directory
seeks.
* Method names are based on syscall names. Where there is no
syscall (eg. "open directory"), we bias towards writing
everything together (Opendir)
To do/To decide
=========
* decide on a final package name
* handle less open/create.
This diff is collapsed.
This diff is collapsed.
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"sync"
"syscall"
"testing"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
)
type keepCacheFile struct {
OperationStubs
keepCache bool
mu sync.Mutex
content []byte
count int
}
func (f *keepCacheFile) setContent(delta int) {
f.mu.Lock()
defer f.mu.Unlock()
f.count += delta
f.content = []byte(fmt.Sprintf("%010x", f.count))
}
func (f *keepCacheFile) Open(ctx context.Context, flags uint32) (FileHandle, uint32, syscall.Errno) {
var fl uint32
if f.keepCache {
fl = fuse.FOPEN_KEEP_CACHE
}
f.setContent(0)
return nil, fl, OK
}
func (f *keepCacheFile) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
out.Size = uint64(len(f.content))
return OK
}
func (f *keepCacheFile) Read(ctx context.Context, fh FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
f.setContent(1)
f.mu.Lock()
defer f.mu.Unlock()
return fuse.ReadResultData(f.content[off:]), OK
}
type keepCacheRoot struct {
OperationStubs
keep, nokeep *keepCacheFile
}
func (r *keepCacheRoot) OnAdd(ctx context.Context) {
i := r.Inode()
r.keep = &keepCacheFile{
keepCache: true,
}
r.keep.setContent(0)
i.AddChild("keep", i.NewInode(ctx, r.keep, NodeAttr{}), true)
r.nokeep = &keepCacheFile{
keepCache: false,
}
r.nokeep.setContent(0)
i.AddChild("nokeep", i.NewInode(ctx, r.nokeep, NodeAttr{}), true)
}
// Test FOPEN_KEEP_CACHE. This is a little subtle: the automatic cache
// invalidation triggers if mtime or file size is changed, so only
// change content but no metadata.
func TestKeepCache(t *testing.T) {
mntDir := testutil.TempDir()
defer os.RemoveAll(mntDir)
root := &keepCacheRoot{}
server, err := Mount(mntDir, root, &Options{
MountOptions: fuse.MountOptions{
Debug: testutil.VerboseTest(),
},
FirstAutomaticIno: 1,
// no caching.
})
defer server.Unmount()
c1, err := ioutil.ReadFile(mntDir + "/keep")
if err != nil {
t.Fatalf("read keep 1: %v", err)
}
c2, err := ioutil.ReadFile(mntDir + "/keep")
if err != nil {
t.Fatalf("read keep 2: %v", err)
}
if bytes.Compare(c1, c2) != 0 {
t.Errorf("keep read 2 got %q want read 1 %q", c2, c1)
}
if s := root.keep.Inode().NotifyContent(0, 100); s != OK {
t.Errorf("NotifyContent: %v", s)
}
c3, err := ioutil.ReadFile(mntDir + "/keep")
if err != nil {
t.Fatalf("read keep 3: %v", err)
}
if bytes.Compare(c2, c3) == 0 {
t.Errorf("keep read 3 got %q want different", c3)
}
nc1, err := ioutil.ReadFile(mntDir + "/nokeep")
if err != nil {
t.Fatalf("read keep 1: %v", err)
}
nc2, err := ioutil.ReadFile(mntDir + "/nokeep")
if err != nil {
t.Fatalf("read keep 2: %v", err)
}
if bytes.Compare(nc1, nc2) == 0 {
t.Errorf("nokeep read 2 got %q want read 1 %q", c2, c1)
}
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"syscall"
"github.com/hanwen/go-fuse/fuse"
)
// OK is the Errno return value to indicate absense of errors.
var OK = syscall.Errno(0)
// ToErrno exhumes the syscall.Errno error from wrapped error values.
func ToErrno(err error) syscall.Errno {
s := fuse.ToStatus(err)
return syscall.Errno(s)
}
// RENAME_EXCHANGE is a flag argument for renameat2()
const RENAME_EXCHANGE = 0x2
// seek to the next data
const _SEEK_DATA = 3
// seek to the next hole
const _SEEK_HOLE = 4
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import "syscall"
// ENOATTR indicates that an extended attribute was not present.
var ENOATTR = syscall.ENOATTR
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import "syscall"
// ENOATTR indicates that an extended attribute was not present.
var ENOATTR = syscall.ENODATA
This diff is collapsed.
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"bytes"
"context"
"fmt"
"os"
"syscall"
"testing"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
)
type dioRoot struct {
OperationStubs
}
func (r *dioRoot) OnAdd(ctx context.Context) {
r.Inode().AddChild("file", r.Inode().NewInode(ctx, &dioFile{}, NodeAttr{}), false)
}
// A file handle that pretends that every hole/data starts at
// multiples of 1024
type dioFH struct {
FileHandleStubs
}
func (f *dioFH) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) {
next := (off + 1023) & (^uint64(1023))
return next, OK
}
func (fh *dioFH) Read(ctx context.Context, data []byte, off int64) (fuse.ReadResult, syscall.Errno) {
r := bytes.Repeat([]byte(fmt.Sprintf("%010d", off)), 1+len(data)/10)
return fuse.ReadResultData(r[:len(data)]), OK
}
// overrides Open so it can return a dioFH file handle
type dioFile struct {
OperationStubs
}
func (f *dioFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
return &dioFH{}, fuse.FOPEN_DIRECT_IO, OK
}
func TestDirectIO(t *testing.T) {
root := &dioRoot{}
mntDir := testutil.TempDir()
defer os.RemoveAll(mntDir)
server, err := Mount(mntDir, root, &Options{
MountOptions: fuse.MountOptions{
Debug: testutil.VerboseTest(),
},
FirstAutomaticIno: 1,
// no caching.
})
defer server.Unmount()
f, err := os.Open(mntDir + "/file")
if err != nil {
t.Fatalf("Open %v", err)
}
defer f.Close()
var buf [10]byte
n, err := f.Read(buf[:])
if err != nil {
t.Fatalf("Read %v", err)
}
want := bytes.Repeat([]byte{'0'}, 10)
got := buf[:n]
if bytes.Compare(got, want) != 0 {
t.Errorf("got %q want %q", got, want)
}
if n, err := syscall.Seek(int(f.Fd()), 512, _SEEK_DATA); err != nil {
t.Errorf("Seek: %v", err)
} else if n != 1024 {
t.Errorf("seek: got %d, want %d", n, 1024)
}
n, err = f.Read(buf[:])
if err != nil {
t.Fatalf("Read %v", err)
}
want = []byte(fmt.Sprintf("%010d", 1024))
got = buf[:n]
if bytes.Compare(got, want) != 0 {
t.Errorf("got %q want %q", got, want)
}
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"syscall"
"github.com/hanwen/go-fuse/fuse"
)
type dirArray struct {
entries []fuse.DirEntry
}
func (a *dirArray) HasNext() bool {
return len(a.entries) > 0
}
func (a *dirArray) Next() (fuse.DirEntry, syscall.Errno) {
e := a.entries[0]
a.entries = a.entries[1:]
return e, 0
}
func (a *dirArray) Close() {
}
// NewListDirStream wraps a slice of DirEntry as a DirStream.
func NewListDirStream(list []fuse.DirEntry) DirStream {
return &dirArray{list}
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"io"
"os"
"syscall"
"github.com/hanwen/go-fuse/fuse"
)
func NewLoopbackDirStream(nm string) (DirStream, syscall.Errno) {
f, err := os.Open(nm)
if err != nil {
return nil, ToErrno(err)
}
defer f.Close()
var entries []fuse.DirEntry
for {
want := 100
infos, err := f.Readdir(want)
for _, info := range infos {
s := fuse.ToStatT(info)
if s == nil {
continue
}
entries = append(entries, fuse.DirEntry{
Name: info.Name(),
Mode: uint32(s.Mode),
Ino: s.Ino,
})
}
if len(infos) < want || err == io.EOF {
break
}
if err != nil {
return nil, ToErrno(err)
}
}
return &dirArray{entries}, OK
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"syscall"
"unsafe"
"github.com/hanwen/go-fuse/fuse"
)
type loopbackDirStream struct {
buf []byte
todo []byte
fd int
}
// NewLoopbackDirStream open a directory for reading as a DirStream
func NewLoopbackDirStream(name string) (DirStream, syscall.Errno) {
fd, err := syscall.Open(name, syscall.O_DIRECTORY, 0755)
if err != nil {
return nil, ToErrno(err)
}
ds := &loopbackDirStream{
buf: make([]byte, 4096),
fd: fd,
}
if err := ds.load(); err != 0 {
ds.Close()
return nil, err
}
return ds, OK
}
func (ds *loopbackDirStream) Close() {
syscall.Close(ds.fd)
}
func (ds *loopbackDirStream) HasNext() bool {
return len(ds.todo) > 0
}
func (ds *loopbackDirStream) Next() (fuse.DirEntry, syscall.Errno) {
de := (*syscall.Dirent)(unsafe.Pointer(&ds.todo[0]))
nameBytes := ds.todo[unsafe.Offsetof(syscall.Dirent{}.Name):de.Reclen]
ds.todo = ds.todo[de.Reclen:]
for l := len(nameBytes); l > 0; l-- {
if nameBytes[l-1] != 0 {
break
}
nameBytes = nameBytes[:l-1]
}
result := fuse.DirEntry{
Ino: de.Ino,
Mode: (uint32(de.Type) << 12),
Name: string(nameBytes),
}
return result, ds.load()
}
func (ds *loopbackDirStream) load() syscall.Errno {
if len(ds.todo) > 0 {
return OK
}
n, err := syscall.Getdents(ds.fd, ds.buf)
if err != nil {
return ToErrno(err)
}
ds.todo = ds.buf[:n]
return OK
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"io/ioutil"
"log"
"os"
"github.com/hanwen/go-fuse/fuse"
)
// An example of creating a loopback file system, and mounting it onto
// a directory
func ExampleMount() {
mntDir, _ := ioutil.TempDir("", "")
home := os.Getenv("HOME")
root, err := NewLoopbackRoot(home)
if err != nil {
log.Panic(err)
}
server, err := Mount(mntDir, root, &Options{
MountOptions: fuse.MountOptions{Debug: true},
})
if err != nil {
log.Panic(err)
}
log.Printf("Mounted %s as loopback on %s", home, mntDir)
log.Printf("Unmount by calling 'fusermount -u %s'", mntDir)
server.Wait()
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"context"
// "time"
"syscall"
"github.com/hanwen/go-fuse/fuse"
"golang.org/x/sys/unix"
)
// NewLoopbackFile creates a FileHandle out of a file descriptor. All
// operations are implemented.
func NewLoopbackFile(fd int) FileHandle {
return &loopbackFile{fd: fd}
}
type loopbackFile struct {
fd int
}
func (f *loopbackFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) {
r := fuse.ReadResultFd(uintptr(f.fd), off, len(buf))
return r, OK
}
func (f *loopbackFile) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) {
n, err := syscall.Pwrite(f.fd, data, off)
return uint32(n), ToErrno(err)
}
func (f *loopbackFile) Release(ctx context.Context) syscall.Errno {
err := syscall.Close(f.fd)
return ToErrno(err)
}
func (f *loopbackFile) Flush(ctx context.Context) syscall.Errno {
// Since Flush() may be called for each dup'd fd, we don't
// want to really close the file, we just want to flush. This
// is achieved by closing a dup'd fd.
newFd, err := syscall.Dup(f.fd)
if err != nil {
return ToErrno(err)
}
err = syscall.Close(newFd)
return ToErrno(err)
}
func (f *loopbackFile) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
r := ToErrno(syscall.Fsync(f.fd))
return r
}
const (
_OFD_GETLK = 36
_OFD_SETLK = 37
_OFD_SETLKW = 38
)
func (f *loopbackFile) Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (errno syscall.Errno) {
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
errno = ToErrno(syscall.FcntlFlock(uintptr(f.fd), _OFD_GETLK, &flk))
out.FromFlockT(&flk)
return
}
func (f *loopbackFile) Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
return f.setLock(ctx, owner, lk, flags, false)
}
func (f *loopbackFile) Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
return f.setLock(ctx, owner, lk, flags, true)
}
func (f *loopbackFile) setLock(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, blocking bool) (errno syscall.Errno) {
if (flags & fuse.FUSE_LK_FLOCK) != 0 {
var op int
switch lk.Typ {
case syscall.F_RDLCK:
op = syscall.LOCK_SH
case syscall.F_WRLCK:
op = syscall.LOCK_EX
case syscall.F_UNLCK:
op = syscall.LOCK_UN
default:
return syscall.EINVAL
}
if !blocking {
op |= syscall.LOCK_NB
}
return ToErrno(syscall.Flock(f.fd, op))
} else {
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
var op int
if blocking {
op = _OFD_SETLKW
} else {
op = _OFD_SETLK
}
return ToErrno(syscall.FcntlFlock(uintptr(f.fd), op, &flk))
}
}
func (f *loopbackFile) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
if errno := f.setAttr(ctx, in); errno != 0 {
return errno
}
return f.Getattr(ctx, out)
}
func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) syscall.Errno {
var errno syscall.Errno
if mode, ok := in.GetMode(); ok {
errno = ToErrno(syscall.Fchmod(f.fd, mode))
if errno != 0 {
return errno
}
}
uid32, uOk := in.GetUID()
gid32, gOk := in.GetGID()
if uOk || gOk {
uid := -1
gid := -1
if uOk {
uid = int(uid32)
}
if gOk {
gid = int(gid32)
}
errno = ToErrno(syscall.Fchown(f.fd, uid, gid))
if errno != 0 {
return errno
}
}
mtime, mok := in.GetMTime()
atime, aok := in.GetATime()
if mok || aok {
ap := &atime
mp := &mtime
if !aok {
ap = nil
}
if !mok {
mp = nil
}
errno = f.utimens(ap, mp)
if errno != 0 {
return errno
}
}
if sz, ok := in.GetSize(); ok {
errno = ToErrno(syscall.Ftruncate(f.fd, int64(sz)))
if errno != 0 {
return errno
}
}
return OK
}
func (f *loopbackFile) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno {
st := syscall.Stat_t{}
err := syscall.Fstat(f.fd, &st)
if err != nil {
return ToErrno(err)
}
a.FromStat(&st)
return OK
}
func (f *loopbackFile) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) {
n, err := unix.Seek(f.fd, int64(off), int(whence))
return uint64(n), ToErrno(err)
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"context"
"syscall"
"time"
"github.com/hanwen/go-fuse/fuse"
)
func (f *loopbackFile) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32) syscall.Errno {
err := syscall.Fallocate(f.fd, mode, int64(off), int64(sz))
if err != nil {
return ToErrno(err)
}
return OK
}
// Utimens - file handle based version of loopbackFileSystem.Utimens()
func (f *loopbackFile) utimens(a *time.Time, m *time.Time) syscall.Errno {
var ts [2]syscall.Timespec
ts[0] = fuse.UtimeToTimespec(a)
ts[1] = fuse.UtimeToTimespec(m)
err := futimens(int(f.fd), &ts)
return ToErrno(err)
}
This diff is collapsed.
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nodefs
import (
"context"
"os"
"os/exec"
"syscall"
"testing"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
)
type interruptRoot struct {
OperationStubs
child interruptOps
}
type interruptOps struct {
OperationStubs
interrupted bool
}
func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
if name != "file" {
return nil, syscall.ENOENT
}
ch := r.Inode().NewInode(ctx, &r.child, NodeAttr{
Ino: 2,
Gen: 1})
return ch, OK
}
func (o *interruptOps) Open(ctx context.Context, flags uint32) (FileHandle, uint32, syscall.Errno) {
select {
case <-time.After(100 * time.Millisecond):
return nil, 0, syscall.EIO
case <-ctx.Done():
o.interrupted = true
return nil, 0, syscall.EINTR
}
}
// This currently doesn't test functionality, but is useful to investigate how
// INTERRUPT opcodes are handled.
func TestInterrupt(t *testing.T) {
mntDir := testutil.TempDir()
defer os.Remove(mntDir)
root := &interruptRoot{}
oneSec := time.Second
server, err := Mount(mntDir, root, &Options{
MountOptions: fuse.MountOptions{
Debug: testutil.VerboseTest(),
},
EntryTimeout: &oneSec,
AttrTimeout: &oneSec,
})
if err != nil {
t.Fatal(err)
}
defer server.Unmount()
cmd := exec.Command("cat", mntDir+"/file")
if err := cmd.Start(); err != nil {
t.Fatalf("run %v: %v", cmd, err)
}
time.Sleep(10 * time.Millisecond)
if err := cmd.Process.Kill(); err != nil {
t.Errorf("Kill: %v", err)
}
server.Unmount()
if !root.child.interrupted {
t.Errorf("open request was not interrupted")
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -6,6 +6,7 @@ package splice ...@@ -6,6 +6,7 @@ package splice
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"syscall" "syscall"
) )
...@@ -43,6 +44,11 @@ func (p *Pair) discard() { ...@@ -43,6 +44,11 @@ func (p *Pair) discard() {
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
// all good. // all good.
} else if err != nil { } else if err != nil {
panic(err) errR := syscall.Close(p.r)
errW := syscall.Close(p.w)
// This can happen if something closed our fd
// inadvertently (eg. double close)
log.Panicf("splicing into /dev/null: %v (close R %d '%v', close W %d '%v')", err, p.r, errR, p.w, errW)
} }
} }
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment