Commit 4f60fdad authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: separate interfaces for File/Symlink/Dir

parent 70326fc5
......@@ -527,6 +527,10 @@ type CopyFileRangeIn struct {
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 {
NodeId uint64
Generation uint64
......
......@@ -55,8 +55,6 @@ To decide
* One giant interface with many methods, or many one-method
interfaces? Or some interface (file, dir, symlink, etc).
* one SetAttr method, or many (Chown, Truncate, etc.)
* function signatures, or types? The latter is easier to remember?
Easier to extend? The latter less efficient (indirections/copies)
......
......@@ -115,11 +115,6 @@ type Operations interface {
// `susan` gets the UID and GID for `susan` here.
Access(ctx context.Context, mask uint32) fuse.Status
// File locking
GetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (status fuse.Status)
SetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
SetLkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
// Extended attributes
// GetXAttr should read data for the given attribute into
......@@ -139,7 +134,88 @@ type Operations interface {
// ERANGE and the correct size.
ListXAttr(ctx context.Context, dest []byte) (uint32, fuse.Status)
GetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status
GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status
SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
}
// SymlinkOperations holds operations specific to symlinks.
type SymlinkOperations interface {
Operations
// Readlink reads the content of a symlink.
Readlink(ctx context.Context) (string, fuse.Status)
}
// FileOperations holds operations that apply to regular files. The
// default implementation, as returned from NewFileOperations forwards
// to the passed-in FileHandle.
//
// XXX Mknod output too?
type FileOperations interface {
Operations
// Open opens an Inode (of regular file type) for reading. It
// is optional but recommended to return a FileHandle.
Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, status fuse.Status)
// File locking
// GetLk returns locks that would conflict with the given
// input lock. If no locks conflict, the output has type
// L_UNLCK. See fcntl(2) for more information.
GetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (status fuse.Status)
// Obtain a lock on a file, or fail if the lock could not
// obtained. See fcntl(2) for more information.
SetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
// Obtain a lock on a file, waiting if necessary. See fcntl(2)
// for more information.
SetLkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
// Reads data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming
// `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle.
Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, fuse.Status)
// Writes the data into the file handle at given offset. After
// returning, the data will be reused and may not referenced.
// The default implementation forwards to the FileHandle.
Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, status fuse.Status)
// Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage. The default implementation forwards to the
// FileHandle.
Fsync(ctx context.Context, f FileHandle, flags uint32) (status fuse.Status)
// Flush is called for close() call on a file descriptor. In
// case of duplicated descriptor, it may be called more than
// once for a file. The default implementation forwards to the
// FileHandle.
Flush(ctx context.Context, f FileHandle) fuse.Status
// This is called to before the file handle is forgotten. The
// kernel ingores the return value of this method,
// so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead.
// The default implementation forwards to the FileHandle.
Release(ctx context.Context, f FileHandle) fuse.Status
// Allocate preallocates space for future writes, so they will
// never encounter ESPACE.
Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) (status fuse.Status)
FGetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status
FSetAttr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
}
// DirOperations are operations for directory nodes in the filesystem.
type DirOperations interface {
Operations
// Lookup should find a direct child of the node by child
// name. If the entry does not exist, it should return ENOENT
......@@ -180,13 +256,6 @@ type Operations interface {
// the return status is OK
Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) fuse.Status
// Readlink reads the content of a symlink.
Readlink(ctx context.Context) (string, fuse.Status)
// Open opens an Inode (of regular file type) for reading. It
// is optional but recommended to return a FileHandle.
Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, status fuse.Status)
// OpenDir opens a directory Inode for reading its
// contents. The actual reading is driven from ReadDir, so
// this method is just for performing sanity/permission
......@@ -195,105 +264,46 @@ type Operations interface {
// ReadDir opens a stream of directory entries.
ReadDir(ctx context.Context) (DirStream, fuse.Status)
// Reads data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming
// `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle.
Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, fuse.Status)
// Writes the data into the file handle at given offset. After
// returning, the data will be reused and may not referenced.
// The default implementation forwards to the FileHandle.
Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, status fuse.Status)
// Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage. The default implementation forwards to the
// FileHandle.
Fsync(ctx context.Context, f FileHandle, flags uint32) (status fuse.Status)
// Flush is called for close() call on a file descriptor. In
// case of duplicated descriptor, it may be called more than
// once for a file. The default implementation forwards to the
// FileHandle.
Flush(ctx context.Context, f FileHandle) fuse.Status
// This is called to before the file handle is forgotten. The
// kernel ingores the return value of this method is ignored,
// so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead.
// The default implementation forwards to the FileHandle.
Release(f FileHandle) fuse.Status
/*
NOSUBMIT - fold into a setattr method, or expand methods?
Decoding SetAttr is a bit of a PITA, but if we use fuse
types as args, we can't take apart SetAttr for the caller
*/
// Truncate sets the file length to the given size. The
// default implementation forwards to the FileHandle.
Truncate(ctx context.Context, f FileHandle, size uint64) fuse.Status
// Chown changes the file owner . The default implementation
// forwards to the FileHandle.
Chown(ctx context.Context, f FileHandle, uid uint32, gid uint32) fuse.Status
// Chmod changes the file permissions. The default
// implementation forwards to the FileHandle.
Chmod(ctx context.Context, f FileHandle, perms uint32) fuse.Status
// Utimens changes the files timestamps. The default
// implementation forwards to the FileHandle.
Utimens(ctx context.Context, f FileHandle, atime *time.Time, mtime *time.Time) fuse.Status
// Allocate preallocates space for future writes, so they will
// never encounter ESPACE. The default implementation
// forwards to the FileHandle.
Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) (status fuse.Status)
}
// FileHandle is a resource identifier for opened files.
// FileHandle is a resource identifier for opened files. For a
// description, see the equivalent operations in FileOperations.
type FileHandle interface {
Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, fuse.Status)
// Writes the data at given offset. After returning, the data
// will be reused and may not referenced.
Write(ctx context.Context, data []byte, off int64) (written uint32, status fuse.Status)
// File locking
GetLk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (status fuse.Status)
SetLk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
SetLkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
// Flush is called for close() call on a file descriptor. In
// case of duplicated descriptor, it may be called more than
// once for a file.
Flush(ctx context.Context) fuse.Status
Fsync(ctx context.Context, flags uint32) fuse.Status
// This is called to before the file handle is forgotten. This
// method has no return value, so nothing can synchronizes on
// the call, and it cannot be canceled. Any cleanup that
// requires specific synchronization or could fail with I/O
// errors should happen in Flush instead.
Release() fuse.Status
Release(ctx context.Context) fuse.Status
GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status
Truncate(ctx context.Context, size uint64) fuse.Status
Chown(ctx context.Context, uid uint32, gid uint32) fuse.Status
Chmod(ctx context.Context, perms uint32) fuse.Status
Utimens(ctx context.Context, atime *time.Time, mtime *time.Time) fuse.Status
SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
Allocate(ctx context.Context, off uint64, size uint64, mode uint32) (status fuse.Status)
}
// Options sets options for the entire filesystem
type Options struct {
// Debug toggles debug output
Debug bool
// If set to nonnil, this defines the overall entry timeout
// for the file system. See fuse.EntryOut for more information.
EntryTimeout *time.Duration
// If set to nonnil, this defines the overall attribute
// timeout for the file system. See fuse.EntryOut for more
// information.
AttrTimeout *time.Duration
// If set to nonnil, this defines the overall entry timeout
// for failed lookups (fuse.ENOENT). See fuse.EntryOut for
// more information.
NegativeTimeout *time.Duration
}
......@@ -73,6 +73,17 @@ func (b *rawBridge) newInode(ops Operations, mode uint32, id FileID, persistent
return old
}
mode = mode &^ 07777
switch mode {
case fuse.S_IFDIR:
_ = ops.(DirOperations)
case fuse.S_IFLNK:
_ = ops.(SymlinkOperations)
case fuse.S_IFREG:
_ = ops.(FileOperations)
default:
log.Panicf("filetype %o unimplemented", mode)
}
inode := &Inode{
mode: mode,
ops: ops,
......@@ -92,7 +103,7 @@ func (b *rawBridge) newInode(ops Operations, mode uint32, id FileID, persistent
// NewNodeFS creates a node based filesystem based on an Operations
// instance for the root.
func NewNodeFS(root Operations, opts *Options) fuse.RawFileSystem {
func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem {
bridge := &rawBridge{
automaticIno: 1 << 63,
}
......@@ -141,7 +152,7 @@ func (b *rawBridge) inode(id uint64, fh uint64) (*Inode, *fileEntry) {
func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name string, out *fuse.EntryOut) (status fuse.Status) {
parent, _ := b.inode(header.NodeId, 0)
child, status := parent.ops.Lookup(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name, out)
child, status := parent.dirOps().Lookup(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name, out)
if !status.Ok() {
if b.options.NegativeTimeout != nil {
out.SetEntryTimeout(*b.options.NegativeTimeout)
......@@ -158,7 +169,7 @@ func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name s
func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
status := parent.ops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
status := parent.dirOps().Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
if status.Ok() {
parent.RmChild(name)
}
......@@ -168,7 +179,7 @@ func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name st
func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
status := parent.ops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
status := parent.dirOps().Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
if status.Ok() {
parent.RmChild(name)
}
......@@ -178,7 +189,7 @@ func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name s
func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name string, out *fuse.EntryOut) (status fuse.Status) {
parent, _ := b.inode(input.NodeId, 0)
child, status := parent.ops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
child, status := parent.dirOps().Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
if !status.Ok() {
return status
}
......@@ -195,7 +206,7 @@ func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name stri
func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name string, out *fuse.EntryOut) (status fuse.Status) {
parent, _ := b.inode(input.NodeId, 0)
child, status := parent.ops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
child, status := parent.dirOps().Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
if !status.Ok() {
return status
}
......@@ -239,7 +250,7 @@ func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) {
func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name string, out *fuse.CreateOut) (status fuse.Status) {
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
parent, _ := b.inode(input.NodeId, 0)
child, f, flags, status := parent.ops.Create(ctx, name, input.Flags, input.Mode)
child, f, flags, status := parent.dirOps().Create(ctx, name, input.Flags, input.Mode)
if !status.Ok() {
if b.options.NegativeTimeout != nil {
out.SetEntryTimeout(*b.options.NegativeTimeout)
......@@ -275,6 +286,9 @@ func (b *rawBridge) SetDebug(debug bool) {}
func (b *rawBridge) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *fuse.AttrOut) fuse.Status {
n, fEntry := b.inode(input.NodeId, input.Fh())
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
if fops, ok := n.ops.(FileOperations); ok {
f := fEntry.file
if input.Flags()&fuse.FUSE_GETATTR_FH == 0 {
// The linux kernel doesnt pass along the file
......@@ -290,11 +304,13 @@ func (b *rawBridge) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *
b.mu.Unlock()
}
status := n.ops.GetAttr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f, out)
status := fops.FGetAttr(ctx, f, out)
b.setAttrTimeout(out)
out.Ino = input.NodeId
out.Mode = (out.Attr.Mode & 07777) | n.mode
return status
}
return n.ops.GetAttr(ctx, out)
}
func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
......@@ -303,80 +319,27 @@ func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
}
}
func (b *rawBridge) SetAttr(cancel <-chan struct{}, input *fuse.SetAttrIn, out *fuse.AttrOut) (status fuse.Status) {
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fuse.AttrOut) (status fuse.Status) {
ctx := &fuse.Context{Caller: in.Caller, Cancel: cancel}
n, fEntry := b.inode(input.NodeId, input.Fh)
n, fEntry := b.inode(in.NodeId, in.Fh)
f := fEntry.file
if input.Valid&fuse.FATTR_FH == 0 {
if in.Valid&fuse.FATTR_FH == 0 {
f = nil
}
if input.Valid&fuse.FATTR_MODE != 0 {
permissions := uint32(07777) & input.Mode
status = n.ops.Chmod(ctx, f, permissions)
if fops, ok := n.ops.(FileOperations); ok {
return fops.FSetAttr(ctx, f, in, out)
}
if status.Ok() && (input.Valid&(fuse.FATTR_UID|fuse.FATTR_GID) != 0) {
var uid uint32 = ^uint32(0) // means "do not change" in chown(2)
var gid uint32 = ^uint32(0)
if input.Valid&fuse.FATTR_UID != 0 {
uid = input.Uid
}
if input.Valid&fuse.FATTR_GID != 0 {
gid = input.Gid
}
status = n.ops.Chown(ctx, f, uid, gid)
}
if status.Ok() && input.Valid&fuse.FATTR_SIZE != 0 {
status = n.ops.Truncate(ctx, f, input.Size)
}
if status.Ok() && (input.Valid&(fuse.FATTR_ATIME|fuse.FATTR_MTIME|fuse.FATTR_ATIME_NOW|fuse.FATTR_MTIME_NOW) != 0) {
now := time.Now()
var atime *time.Time
var mtime *time.Time
if input.Valid&fuse.FATTR_ATIME != 0 {
if input.Valid&fuse.FATTR_ATIME_NOW != 0 {
atime = &now
} else {
t := time.Unix(int64(input.Atime), int64(input.Atimensec))
atime = &t
}
}
if input.Valid&fuse.FATTR_MTIME != 0 {
if input.Valid&fuse.FATTR_MTIME_NOW != 0 {
mtime = &now
} else {
t := time.Unix(int64(input.Mtime), int64(input.Mtimensec))
mtime = &t
}
}
status = n.ops.Utimens(ctx, f, atime, mtime)
}
if !status.Ok() {
return status
}
// Must call GetAttr(); the filesystem may override some of
// the changes we effect here.
status = n.ops.GetAttr(ctx, f, out)
b.setAttrTimeout(out)
out.Ino = n.nodeID.Ino
out.Mode = n.mode | (out.Mode &^ 07777)
return status
return n.ops.SetAttr(ctx, in, out)
}
func (b *rawBridge) Rename(cancel <-chan struct{}, input *fuse.RenameIn, oldName string, newName string) fuse.Status {
p1, _ := b.inode(input.NodeId, 0)
p2, _ := b.inode(input.Newdir, 0)
status := p1.ops.Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags)
status := p1.dirOps().Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags)
if status.Ok() {
if input.Flags&unix.RENAME_EXCHANGE != 0 {
p1.ExchangeChild(oldName, p2, newName)
......@@ -391,7 +354,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string
parent, _ := b.inode(input.NodeId, 0)
target, _ := b.inode(input.Oldnodeid, 0)
child, status := parent.ops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
child, status := parent.dirOps().Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
if !status.Ok() {
return status
}
......@@ -403,7 +366,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string
func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, target string, name string, out *fuse.EntryOut) (status fuse.Status) {
parent, _ := b.inode(header.NodeId, 0)
child, status := parent.ops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
child, status := parent.dirOps().Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
if !status.Ok() {
return status
}
......@@ -415,7 +378,7 @@ func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, targe
func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
result, status := n.ops.Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel})
result, status := n.linkOps().Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if !status.Ok() {
return nil, status
}
......@@ -454,7 +417,7 @@ func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, a
func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) {
n, _ := b.inode(input.NodeId, 0)
// NOSUBMIT: what about the mode argument?
f, flags, status := n.ops.Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags)
f, flags, status := n.fileOps().Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags)
if !status.Ok() {
return status
}
......@@ -488,28 +451,29 @@ func (b *rawBridge) registerFile(n *Inode, f FileHandle, flags uint32) uint32 {
func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset))
return n.fileOps().Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset))
}
func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.LkOut) (status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.GetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk)
return n.fileOps().GetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk)
}
func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) (status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.SetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
return n.fileOps().SetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
}
func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) (status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.SetLkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
return n.fileOps().SetLkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
}
func (b *rawBridge) Release(input *fuse.ReleaseIn) {
// XXX should have cancel channel too.
n, f := b.releaseFileEntry(input.NodeId, input.Fh)
f.wg.Wait()
n.ops.Release(f.file)
n.fileOps().Release(&fuse.Context{Caller: input.Caller, Cancel: nil}, f.file)
b.mu.Lock()
defer b.mu.Unlock()
......@@ -549,27 +513,27 @@ func (b *rawBridge) releaseFileEntry(nid uint64, fh uint64) (*Inode, *fileEntry)
func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []byte) (written uint32, status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset))
return n.fileOps().Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset))
}
func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)
return n.fileOps().Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)
}
func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) (status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags)
return n.fileOps().Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags)
}
func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) (status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.ops.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode)
return n.fileOps().Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode)
}
func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
status := n.ops.OpenDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
status := n.dirOps().OpenDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if !status.Ok() {
return status
}
......@@ -585,7 +549,7 @@ func (b *rawBridge) getStream(cancel <-chan struct{}, input *fuse.ReadIn, inode
f.dirStream.Close()
f.dirStream = nil
}
str, status := inode.ops.ReadDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
str, status := inode.dirOps().ReadDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if !status.Ok() {
return status
}
......@@ -654,7 +618,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
return fuse.OK
}
child, status := n.ops.Lookup(&fuse.Context{Caller: input.Caller, Cancel: cancel}, e.Name, entryOut)
child, status := n.dirOps().Lookup(&fuse.Context{Caller: input.Caller, Cancel: cancel}, e.Name, entryOut)
if !status.Ok() {
if b.options.NegativeTimeout != nil {
entryOut.SetEntryTimeout(*b.options.NegativeTimeout)
......@@ -676,7 +640,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) (status fuse.Status) {
n, _ := b.inode(input.NodeId, input.Fh)
return n.ops.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags)
return n.fileOps().Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags)
}
func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fuse.StatfsOut) (status fuse.Status) {
......
......@@ -150,9 +150,9 @@ func (n *DefaultOperations) Flush(ctx context.Context, f FileHandle) fuse.Status
return fuse.ENOSYS
}
func (n *DefaultOperations) Release(f FileHandle) fuse.Status {
func (n *DefaultOperations) Release(ctx context.Context, f FileHandle) fuse.Status {
if f != nil {
return f.Release()
return f.Release(ctx)
}
return fuse.ENOSYS
}
......@@ -165,41 +165,24 @@ func (n *DefaultOperations) Allocate(ctx context.Context, f FileHandle, off uint
return fuse.ENOSYS
}
func (n *DefaultOperations) GetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status {
if f != nil {
f.GetAttr(ctx, out)
}
return fuse.ENOSYS
}
func (n *DefaultOperations) Truncate(ctx context.Context, f FileHandle, size uint64) fuse.Status {
if f != nil {
return f.Truncate(ctx, size)
}
func (n *DefaultOperations) GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status {
return fuse.ENOSYS
}
func (n *DefaultOperations) Chown(ctx context.Context, f FileHandle, uid uint32, gid uint32) fuse.Status {
func (n *DefaultOperations) FGetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status {
if f != nil {
return f.Chown(ctx, uid, gid)
f.GetAttr(ctx, out)
}
return fuse.ENOSYS
}
func (n *DefaultOperations) Chmod(ctx context.Context, f FileHandle, perms uint32) fuse.Status {
if f != nil {
return f.Chmod(ctx, perms)
}
func (n *DefaultOperations) SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
return fuse.ENOSYS
}
func (n *DefaultOperations) Utimens(ctx context.Context, f FileHandle, atime *time.Time, mtime *time.Time) fuse.Status {
func (n *DefaultOperations) FSetAttr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
if f != nil {
return f.Utimens(ctx, atime, mtime)
return f.SetAttr(ctx, in, out)
}
return fuse.ENOSYS
......@@ -265,7 +248,7 @@ func (f *DefaultFile) Flush(ctx context.Context) fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) Release() fuse.Status {
func (f *DefaultFile) Release(ctx context.Context) fuse.Status {
return fuse.ENOSYS
}
......@@ -273,6 +256,10 @@ func (f *DefaultFile) GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Statu
return fuse.ENOSYS
}
func (f *DefaultFile) SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) Truncate(ctx context.Context, size uint64) fuse.Status {
return fuse.ENOSYS
}
......
......@@ -45,7 +45,7 @@ func (f *loopbackFile) Write(ctx context.Context, data []byte, off int64) (uint3
return uint32(n), fuse.ToStatus(err)
}
func (f *loopbackFile) Release() fuse.Status {
func (f *loopbackFile) Release(ctx context.Context) fuse.Status {
f.mu.Lock()
err := syscall.Close(f.fd)
f.mu.Unlock()
......@@ -128,28 +128,68 @@ func (f *loopbackFile) setLock(ctx context.Context, owner uint64, lk *fuse.FileL
}
}
func (f *loopbackFile) Truncate(ctx context.Context, size uint64) fuse.Status {
f.mu.Lock()
r := fuse.ToStatus(syscall.Ftruncate(f.fd, int64(size)))
f.mu.Unlock()
func (f *loopbackFile) SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
if status := f.setAttr(ctx, in); !status.Ok() {
return status
}
return r
return f.GetAttr(ctx, out)
}
func (f *loopbackFile) Chmod(ctx context.Context, mode uint32) fuse.Status {
func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) fuse.Status {
f.mu.Lock()
r := fuse.ToStatus(syscall.Fchmod(f.fd, mode))
f.mu.Unlock()
defer f.mu.Unlock()
var status fuse.Status
if mode, ok := in.GetMode(); ok {
status = fuse.ToStatus(syscall.Fchmod(f.fd, mode))
if !status.Ok() {
return status
}
}
return r
}
uid32, uOk := in.GetUID()
gid32, gOk := in.GetGID()
if uOk || gOk {
uid := -1
gid := -1
func (f *loopbackFile) Chown(ctx context.Context, uid uint32, gid uint32) fuse.Status {
f.mu.Lock()
r := fuse.ToStatus(syscall.Fchown(f.fd, int(uid), int(gid)))
f.mu.Unlock()
if uOk {
uid = int(uid32)
}
if gOk {
gid = int(gid32)
}
status = fuse.ToStatus(syscall.Fchown(f.fd, uid, gid))
if !status.Ok() {
return status
}
}
return r
mtime, mok := in.GetMTime()
atime, aok := in.GetATime()
if mok || aok {
ap := &atime
mp := &mtime
if !aok {
ap = nil
}
if !mok {
mp = nil
}
status = f.utimens(ap, mp)
if !status.Ok() {
return status
}
}
if sz, ok := in.GetSize(); ok {
status = fuse.ToStatus(syscall.Ftruncate(f.fd, int64(sz)))
if !status.Ok() {
return status
}
}
return fuse.OK
}
func (f *loopbackFile) GetAttr(ctx context.Context, a *fuse.AttrOut) fuse.Status {
......
......@@ -23,7 +23,7 @@ func (f *loopbackFile) Allocate(ctx context.Context, off uint64, sz uint64, mode
}
// Utimens - file handle based version of loopbackFileSystem.Utimens()
func (f *loopbackFile) Utimens(ctx context.Context, a *time.Time, m *time.Time) fuse.Status {
func (f *loopbackFile) utimens(a *time.Time, m *time.Time) fuse.Status {
var ts [2]syscall.Timespec
ts[0] = fuse.UtimeToTimespec(a)
ts[1] = fuse.UtimeToTimespec(m)
......
......@@ -88,6 +88,18 @@ type Inode struct {
parents map[parentData]struct{}
}
func (n *Inode) dirOps() DirOperations {
return n.ops.(DirOperations)
}
func (n *Inode) fileOps() FileOperations {
return n.ops.(FileOperations)
}
func (n *Inode) linkOps() SymlinkOperations {
return n.ops.(SymlinkOperations)
}
// FileID returns the (Ino, Gen) tuple for this node.
func (n *Inode) FileID() FileID {
return n.nodeID
......
......@@ -37,7 +37,7 @@ func (n *loopbackNode) StatFs(ctx context.Context, out *fuse.StatfsOut) fuse.Sta
return fuse.OK
}
func (n *loopbackRoot) GetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status {
func (n *loopbackRoot) GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status {
var err error = nil
st := syscall.Stat_t{}
err = syscall.Stat(n.root, &st)
......@@ -253,7 +253,7 @@ func (n *loopbackNode) ReadDir(ctx context.Context) (DirStream, fuse.Status) {
return NewLoopbackDirStream(n.path())
}
func (n *loopbackNode) GetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status {
func (n *loopbackNode) FGetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status {
if f != nil {
return f.GetAttr(ctx, out)
}
......@@ -270,7 +270,9 @@ func (n *loopbackNode) GetAttr(ctx context.Context, f FileHandle, out *fuse.Attr
return fuse.OK
}
func NewLoopback(root string) Operations {
// NewLoopback returns a root node for a loopback file system whose
// root is at the given root.
func NewLoopback(root string) DirOperations {
n := &loopbackRoot{
root: root,
}
......
......@@ -34,7 +34,7 @@ type testCase struct {
origDir string
mntDir string
loopback Operations
loopback DirOperations
rawFS fuse.RawFileSystem
server *fuse.Server
}
......
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