Commit 2f9c4b1d authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: split up interface futher

* MutableDirOperations
* XAttrOperations
* LockOperations
parent 17f8f123
......@@ -67,22 +67,6 @@ func InodeOf(node Operations) *Inode {
return node.inode()
}
// DirStream lists directory entries.
type DirStream interface {
// HasNext indicates if there are further entries. HasNext
// might be called on already closed streams.
HasNext() bool
// Next retrieves the next entry. It is only called if HasNext
// has previously returned true. The Status may be used to
// indicate I/O errors
Next() (fuse.DirEntry, fuse.Status)
// Close releases resources related to this directory
// stream.
Close()
}
// Operations is the interface that implements the filesystem. Each
// Operations instance must embed DefaultNode.
type Operations interface {
......@@ -106,7 +90,16 @@ type Operations interface {
// susan gets the UID and GID for susan here.
Access(ctx context.Context, mask uint32) fuse.Status
// Extended attributes
// GetAttr reads attributes for an Inode
GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status
// SetAttr sets attributes for an Inode.
SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
}
// XAttrOperations is as collection of methods used to implement extended attributes.
type XAttrOperations interface {
Operations
// GetXAttr should read data for the given attribute into
// `dest` and return the number of bytes. If `dest` is too
......@@ -124,12 +117,6 @@ type Operations interface {
// `dest`. If the `dest` buffer is too small, it should return
// ERANGE and the correct size.
ListXAttr(ctx context.Context, dest []byte) (uint32, fuse.Status)
// GetAttr reads attributes for an Inode
GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status
// SetAttr sets attributes for an Inode
SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
}
// SymlinkOperations holds operations specific to symlinks.
......@@ -143,8 +130,6 @@ type SymlinkOperations interface {
// 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
......@@ -154,19 +139,6 @@ type FileOperations interface {
// 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,
......@@ -206,6 +178,40 @@ type FileOperations interface {
FSetAttr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
}
// LockOperations are operations for locking regions of regular files.
type LockOperations interface {
FileOperations
// 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)
}
// DirStream lists directory entries.
type DirStream interface {
// HasNext indicates if there are further entries. HasNext
// might be called on already closed streams.
HasNext() bool
// Next retrieves the next entry. It is only called if HasNext
// has previously returned true. The Status may be used to
// indicate I/O errors
Next() (fuse.DirEntry, fuse.Status)
// Close releases resources related to this directory
// stream.
Close()
}
// DirOperations are operations for directory nodes in the filesystem.
type DirOperations interface {
Operations
......@@ -219,6 +225,20 @@ type DirOperations interface {
// tree automatically if the return status is OK.
Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, 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
// checks.
OpenDir(ctx context.Context) fuse.Status
// ReadDir opens a stream of directory entries.
ReadDir(ctx context.Context) (DirStream, fuse.Status)
}
// MutableDirOperations are operations that change the hierarchy of a file system.
type MutableDirOperations interface {
DirOperations
// Mkdir is similar to Lookup, but must create a directory entry and Inode.
Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, fuse.Status)
......@@ -248,15 +268,6 @@ type DirOperations interface {
// different one. The changes is effected in the FS tree if
// the return status is OK
Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) 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
// checks.
OpenDir(ctx context.Context) fuse.Status
// ReadDir opens a stream of directory entries.
ReadDir(ctx context.Context) (DirStream, fuse.Status)
}
// FileHandle is a resource identifier for opened files. For a
......
......@@ -101,6 +101,43 @@ func (b *rawBridge) newInode(ops Operations, mode uint32, id FileID, persistent
return ops.inode()
}
// addNewChild inserts the child into the tree. Returns file handle if file != nil.
func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, file FileHandle, fileFlags uint32, out *fuse.EntryOut) uint32 {
lockNodes(parent, child)
parent.setEntry(name, child)
b.mu.Lock()
child.lookupCount++
var fh uint32
if file != nil {
fh = b.registerFile(child, file, fileFlags)
}
out.NodeId = child.nodeID.Ino
out.Generation = child.nodeID.Gen
out.Attr.Ino = child.nodeID.Ino
b.mu.Unlock()
unlockNodes(parent, child)
return fh
}
func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) {
if b.options.AttrTimeout != nil {
out.SetAttrTimeout(*b.options.AttrTimeout)
}
if b.options.EntryTimeout != nil {
out.SetEntryTimeout(*b.options.EntryTimeout)
}
}
func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
if b.options.AttrTimeout != nil {
out.SetTimeout(*b.options.AttrTimeout)
}
}
// NewNodeFS creates a node based filesystem based on an Operations
// instance for the root.
func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem {
......@@ -169,7 +206,11 @@ 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.dirOps().Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
var status fuse.Status
if mops, ok := parent.ops.(MutableDirOperations); ok {
status = mops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
}
if status.Ok() {
parent.RmChild(name)
}
......@@ -179,17 +220,26 @@ 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.dirOps().Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
var status fuse.Status
if mops, ok := parent.ops.(MutableDirOperations); ok {
status = mops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
}
if status.Ok() {
parent.RmChild(name)
}
return status
}
func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name string, out *fuse.EntryOut) (status fuse.Status) {
func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
child, status := parent.dirOps().Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
var child *Inode
var status fuse.Status
if mops, ok := parent.ops.(MutableDirOperations); ok {
child, status = mops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
}
if !status.Ok() {
return status
}
......@@ -203,10 +253,15 @@ func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name stri
return fuse.OK
}
func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name string, out *fuse.EntryOut) (status fuse.Status) {
func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
child, status := parent.dirOps().Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
var child *Inode
var status fuse.Status
if mops, ok := parent.ops.(MutableDirOperations); ok {
child, status = mops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
}
if !status.Ok() {
return status
}
......@@ -216,41 +271,18 @@ func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name stri
return fuse.OK
}
// addNewChild inserts the child into the tree. Returns file handle if file != nil.
func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, file FileHandle, fileFlags uint32, out *fuse.EntryOut) uint32 {
lockNodes(parent, child)
parent.setEntry(name, child)
b.mu.Lock()
child.lookupCount++
var fh uint32
if file != nil {
fh = b.registerFile(child, file, fileFlags)
}
out.NodeId = child.nodeID.Ino
out.Generation = child.nodeID.Gen
out.Attr.Ino = child.nodeID.Ino
b.mu.Unlock()
unlockNodes(parent, child)
return fh
}
func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name string, out *fuse.CreateOut) fuse.Status {
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
parent, _ := b.inode(input.NodeId, 0)
func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) {
if b.options.AttrTimeout != nil {
out.SetAttrTimeout(*b.options.AttrTimeout)
var child *Inode
var status fuse.Status
var f FileHandle
var flags uint32
if mops, ok := parent.ops.(MutableDirOperations); ok {
child, f, flags, status = mops.Create(ctx, name, input.Flags, input.Mode)
}
if b.options.EntryTimeout != nil {
out.SetEntryTimeout(*b.options.EntryTimeout)
}
}
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.dirOps().Create(ctx, name, input.Flags, input.Mode)
if !status.Ok() {
if b.options.NegativeTimeout != nil {
out.SetEntryTimeout(*b.options.NegativeTimeout)
......@@ -313,12 +345,6 @@ func (b *rawBridge) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *
return n.ops.GetAttr(ctx, out)
}
func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
if b.options.AttrTimeout != nil {
out.SetTimeout(*b.options.AttrTimeout)
}
}
func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fuse.AttrOut) (status fuse.Status) {
ctx := &fuse.Context{Caller: in.Caller, Cancel: cancel}
......@@ -339,41 +365,52 @@ func (b *rawBridge) Rename(cancel <-chan struct{}, input *fuse.RenameIn, oldName
p1, _ := b.inode(input.NodeId, 0)
p2, _ := b.inode(input.Newdir, 0)
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)
} else {
p1.MvChild(oldName, p2, newName, true)
if mops, ok := p1.ops.(MutableDirOperations); ok {
status := mops.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)
} else {
p1.MvChild(oldName, p2, newName, true)
}
return status
}
}
return status
return fuse.ENOSYS
}
func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string, out *fuse.EntryOut) (status fuse.Status) {
parent, _ := b.inode(input.NodeId, 0)
target, _ := b.inode(input.Oldnodeid, 0)
child, status := parent.dirOps().Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
if !status.Ok() {
return status
}
if mops, ok := parent.ops.(MutableDirOperations); ok {
child, status := mops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
if !status.Ok() {
return status
}
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
return fuse.ENOSYS
}
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.dirOps().Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
if !status.Ok() {
return status
}
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
if mops, ok := parent.ops.(MutableDirOperations); ok {
child, status := mops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
if !status.Ok() {
return status
}
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
return fuse.ENOSYS
}
func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) {
......@@ -396,22 +433,35 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) (status
func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
return n.ops.GetXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data)
if xops, ok := n.ops.(XAttrOperations); ok {
return xops.GetXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data)
}
return 0, fuse.ENOSYS
}
func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
return n.ops.ListXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest)
if xops, ok := n.ops.(XAttrOperations); ok {
return xops.ListXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest)
}
return 0, fuse.ENOSYS
}
func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
return n.ops.SetXAttr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags)
if xops, ok := n.ops.(XAttrOperations); ok {
return xops.SetXAttr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags)
}
return fuse.ENOSYS
}
func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) (status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
return n.ops.RemoveXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr)
if xops, ok := n.ops.(XAttrOperations); ok {
return xops.RemoveXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr)
}
return fuse.ENOSYS
}
func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) {
......@@ -456,17 +506,26 @@ func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte)
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.fileOps().GetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk)
if lops, ok := n.ops.(LockOperations); ok {
return lops.GetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk)
}
return fuse.ENOSYS
}
func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) (status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.fileOps().SetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
if lops, ok := n.ops.(LockOperations); ok {
return lops.SetLk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
}
return fuse.ENOSYS
}
func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) (status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
return n.fileOps().SetLkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
if lops, ok := n.ops.(LockOperations); ok {
return lops.SetLkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)
}
return fuse.ENOSYS
}
func (b *rawBridge) Release(input *fuse.ReleaseIn) {
......
......@@ -8,7 +8,6 @@ import (
"context"
"log"
"sync/atomic"
"time"
"unsafe"
"github.com/hanwen/go-fuse/fuse"
......@@ -47,15 +46,37 @@ func (n *DefaultOperations) setInode(inode *Inode) bool {
nil, unsafe.Pointer(inode))
}
func (n *DefaultOperations) inode() *Inode {
return (*Inode)(atomic.LoadPointer(
(*unsafe.Pointer)(unsafe.Pointer(&n.inode_))))
}
func (n *DefaultOperations) StatFs(ctx context.Context, out *fuse.StatfsOut) fuse.Status {
// this should be defined on OSX, or the FS won't mount
*out = fuse.StatfsOut{}
return fuse.OK
}
func (n *DefaultOperations) inode() *Inode {
return (*Inode)(atomic.LoadPointer(
(*unsafe.Pointer)(unsafe.Pointer(&n.inode_))))
func (n *DefaultOperations) GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status {
return fuse.ENOSYS
}
func (n *DefaultOperations) SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
return fuse.ENOSYS
}
func (n *DefaultOperations) Access(ctx context.Context, mask uint32) fuse.Status {
return fuse.ENOSYS
}
// ****************************************************************
func (n *DefaultOperations) FSetAttr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
if f != nil {
return f.SetAttr(ctx, in, out)
}
return fuse.ENOSYS
}
func (n *DefaultOperations) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, fuse.Status) {
......@@ -165,10 +186,6 @@ func (n *DefaultOperations) Allocate(ctx context.Context, f FileHandle, off uint
return fuse.ENOSYS
}
func (n *DefaultOperations) GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status {
return fuse.ENOSYS
}
func (n *DefaultOperations) FGetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status {
if f != nil {
f.GetAttr(ctx, out)
......@@ -176,18 +193,6 @@ func (n *DefaultOperations) FGetAttr(ctx context.Context, f FileHandle, out *fus
return fuse.ENOSYS
}
func (n *DefaultOperations) SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
return fuse.ENOSYS
}
func (n *DefaultOperations) FSetAttr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
if f != nil {
return f.SetAttr(ctx, in, out)
}
return fuse.ENOSYS
}
func (n *DefaultOperations) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, status fuse.Status) {
return nil, 0, fuse.ENOSYS
}
......@@ -211,10 +216,6 @@ func (n *DefaultOperations) RemoveXAttr(ctx context.Context, attr string) fuse.S
return fuse.ENOATTR
}
func (n *DefaultOperations) Access(ctx context.Context, mask uint32) fuse.Status {
return fuse.ENOSYS
}
func (n *DefaultOperations) ListXAttr(ctx context.Context, dest []byte) (uint32, fuse.Status) {
return 0, fuse.OK
}
......@@ -260,23 +261,6 @@ func (f *DefaultFile) SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse
return fuse.ENOSYS
}
func (f *DefaultFile) Truncate(ctx context.Context, size uint64) fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) Chown(ctx context.Context, uid uint32, gid uint32) fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) Chmod(ctx context.Context, perms uint32) fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) Utimens(ctx context.Context, atime *time.Time, mtime *time.Time) fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) Allocate(ctx context.Context, off uint64, size uint64, mode uint32) (status fuse.Status) {
return fuse.ENOSYS
}
......
......@@ -682,3 +682,5 @@ func TestGetAttrParallel(t *testing.T) {
}
wg.Wait()
}
// XXX test mknod.
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