rclone/vendor/github.com/hanwen/go-fuse/v2/fs/bridge.go

973 lines
26 KiB
Go

// 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 fs
import (
"context"
"log"
"sync"
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal"
)
func errnoToStatus(errno syscall.Errno) fuse.Status {
return fuse.Status(errno)
}
type fileEntry struct {
file FileHandle
// index into Inode.openFiles
nodeIndex int
// Protects directory fields. Must be acquired before bridge.mu
mu sync.Mutex
// Directory
dirStream DirStream
hasOverflow bool
overflow fuse.DirEntry
wg sync.WaitGroup
}
type rawBridge struct {
options Options
root *Inode
server *fuse.Server
// mu protects the following data. Locks for inodes must be
// taken before rawBridge.mu
mu sync.Mutex
nodes map[uint64]*Inode
automaticIno uint64
files []*fileEntry
freeFiles []uint32
}
// newInode creates creates new inode pointing to ops.
func (b *rawBridge) newInodeUnlocked(ops InodeEmbedder, id StableAttr, persistent bool) *Inode {
b.mu.Lock()
defer b.mu.Unlock()
if id.Reserved() {
log.Panicf("using reserved ID %d for inode number", id.Ino)
}
// This ops already was populated. Just return it.
if ops.embed().bridge != nil {
return ops.embed()
}
if id.Ino == 0 {
for {
id.Ino = b.automaticIno
b.automaticIno++
_, ok := b.nodes[id.Ino]
if !ok {
break
}
}
}
// the same node can be looked up through 2 paths in parallel, eg.
//
// root
// / \
// dir1 dir2
// \ /
// file
//
// dir1.Lookup("file") and dir2.Lookup("file") are executed
// simultaneously. The matching StableAttrs ensure that we return the
// same node.
old := b.nodes[id.Ino]
if old != nil {
return old
}
id.Mode = id.Mode &^ 07777
if id.Mode == 0 {
id.Mode = fuse.S_IFREG
}
b.nodes[id.Ino] = ops.embed()
initInode(ops.embed(), ops, id, b, persistent)
return ops.embed()
}
func (b *rawBridge) newInode(ctx context.Context, ops InodeEmbedder, id StableAttr, persistent bool) *Inode {
ch := b.newInodeUnlocked(ops, id, persistent)
if ch != ops.embed() {
return ch
}
if oa, ok := ops.(NodeOnAdder); ok {
oa.OnAdd(ctx)
}
return ch
}
// 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 {
if name == "." || name == ".." {
log.Panicf("BUG: tried to add virtual entry %q to the actual tree", name)
}
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.stableAttr.Ino
out.Generation = child.stableAttr.Gen
out.Attr.Ino = child.stableAttr.Ino
b.mu.Unlock()
unlockNodes(parent, child)
return fh
}
func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) {
b.setAttr(&out.Attr)
if b.options.AttrTimeout != nil && out.AttrTimeout() == 0 {
out.SetAttrTimeout(*b.options.AttrTimeout)
}
if b.options.EntryTimeout != nil && out.EntryTimeout() == 0 {
out.SetEntryTimeout(*b.options.EntryTimeout)
}
}
func (b *rawBridge) setAttr(out *fuse.Attr) {
if !b.options.NullPermissions && out.Mode&07777 == 0 {
out.Mode |= 0644
if out.Mode&syscall.S_IFDIR != 0 {
out.Mode |= 0111
}
}
if b.options.UID != 0 && out.Uid == 0 {
out.Uid = b.options.UID
}
if b.options.GID != 0 && out.Gid == 0 {
out.Gid = b.options.GID
}
setBlocks(out)
}
func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
if b.options.AttrTimeout != nil && out.Timeout() == 0 {
out.SetTimeout(*b.options.AttrTimeout)
}
}
// NewNodeFS creates a node based filesystem based on the
// InodeEmbedder instance for the root of the tree.
func NewNodeFS(root InodeEmbedder, opts *Options) fuse.RawFileSystem {
bridge := &rawBridge{
automaticIno: opts.FirstAutomaticIno,
}
if bridge.automaticIno == 1 {
bridge.automaticIno++
}
if bridge.automaticIno == 0 {
bridge.automaticIno = 1 << 63
}
if opts != nil {
bridge.options = *opts
} else {
oneSec := time.Second
bridge.options.EntryTimeout = &oneSec
bridge.options.AttrTimeout = &oneSec
}
initInode(root.embed(), root,
StableAttr{
Ino: 1,
Mode: fuse.S_IFDIR,
},
bridge,
false,
)
bridge.root = root.embed()
bridge.root.lookupCount = 1
bridge.nodes = map[uint64]*Inode{
1: bridge.root,
}
// Fh 0 means no file handle.
bridge.files = []*fileEntry{{}}
if opts.OnAdd != nil {
opts.OnAdd(context.Background())
} else if oa, ok := root.(NodeOnAdder); ok {
oa.OnAdd(context.Background())
}
return bridge
}
func (b *rawBridge) String() string {
return "rawBridge"
}
func (b *rawBridge) inode(id uint64, fh uint64) (*Inode, *fileEntry) {
b.mu.Lock()
defer b.mu.Unlock()
n, f := b.nodes[id], b.files[fh]
if n == nil {
log.Panicf("unknown node %d", id)
}
return n, f
}
func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
ctx := &fuse.Context{Caller: header.Caller, Cancel: cancel}
child, errno := b.lookup(ctx, parent, name, out)
if errno != 0 {
if b.options.NegativeTimeout != nil && out.EntryTimeout() == 0 {
out.SetEntryTimeout(*b.options.NegativeTimeout)
}
return errnoToStatus(errno)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
if lu, ok := parent.ops.(NodeLookuper); ok {
return lu.Lookup(ctx, name, out)
}
child := parent.GetChild(name)
if child == nil {
return nil, syscall.ENOENT
}
if ga, ok := child.ops.(NodeGetattrer); ok {
var a fuse.AttrOut
errno := ga.Getattr(ctx, nil, &a)
if errno == 0 {
out.Attr = a.Attr
}
}
return child, OK
}
func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno
if mops, ok := parent.ops.(NodeRmdirer); ok {
errno = mops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
}
if errno == 0 {
parent.RmChild(name)
}
return errnoToStatus(errno)
}
func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno
if mops, ok := parent.ops.(NodeUnlinker); ok {
errno = mops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
}
if errno == 0 {
parent.RmChild(name)
}
return errnoToStatus(errno)
}
func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
var child *Inode
var errno syscall.Errno
if mops, ok := parent.ops.(NodeMkdirer); ok {
child, errno = mops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
} else {
return fuse.ENOTSUP
}
if errno != 0 {
return errnoToStatus(errno)
}
if out.Attr.Mode&^07777 == 0 {
out.Attr.Mode |= fuse.S_IFDIR
}
if out.Attr.Mode&^07777 != fuse.S_IFDIR {
log.Panicf("Mkdir: mode must be S_IFDIR (%o), got %o", fuse.S_IFDIR, out.Attr.Mode)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
var child *Inode
var errno syscall.Errno
if mops, ok := parent.ops.(NodeMknoder); ok {
child, errno = mops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
}
if errno != 0 {
return errnoToStatus(errno)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
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)
var child *Inode
var errno syscall.Errno
var f FileHandle
var flags uint32
if mops, ok := parent.ops.(NodeCreater); ok {
child, f, flags, errno = mops.Create(ctx, name, input.Flags, input.Mode, &out.EntryOut)
} else {
return fuse.EROFS
}
if errno != 0 {
if b.options.NegativeTimeout != nil {
out.SetEntryTimeout(*b.options.NegativeTimeout)
}
return errnoToStatus(errno)
}
out.Fh = uint64(b.addNewChild(parent, name, child, f, input.Flags|syscall.O_CREAT, &out.EntryOut))
out.OpenFlags = flags
child.setEntryOut(&out.EntryOut)
b.setEntryOutTimeout(&out.EntryOut)
return fuse.OK
}
func (b *rawBridge) Forget(nodeid, nlookup uint64) {
n, _ := b.inode(nodeid, 0)
n.removeRef(nlookup, false)
}
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())
f := fEntry.file
if f == nil {
// The linux kernel doesnt pass along the file
// descriptor, so we have to fake it here.
// See https://github.com/libfuse/libfuse/issues/62
b.mu.Lock()
for _, fh := range n.openFiles {
f = b.files[fh].file
b.files[fh].wg.Add(1)
defer b.files[fh].wg.Done()
break
}
b.mu.Unlock()
}
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
return errnoToStatus(b.getattr(ctx, n, f, out))
}
func (b *rawBridge) getattr(ctx context.Context, n *Inode, f FileHandle, out *fuse.AttrOut) syscall.Errno {
var errno syscall.Errno
var fg FileGetattrer
if f != nil {
fg, _ = f.(FileGetattrer)
}
if fops, ok := n.ops.(NodeGetattrer); ok {
errno = fops.Getattr(ctx, f, out)
} else if fg != nil {
errno = fg.Getattr(ctx, out)
} else {
// We set Mode below, which is the minimum for success
}
if errno == 0 {
out.Ino = n.stableAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.stableAttr.Mode
b.setAttr(&out.Attr)
b.setAttrTimeout(out)
}
return errno
}
func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
ctx := &fuse.Context{Caller: in.Caller, Cancel: cancel}
fh, _ := in.GetFh()
n, fEntry := b.inode(in.NodeId, fh)
f := fEntry.file
var errno = syscall.ENOTSUP
if fops, ok := n.ops.(NodeSetattrer); ok {
errno = fops.Setattr(ctx, f, in, out)
} else if fops, ok := f.(FileSetattrer); ok {
errno = fops.Setattr(ctx, in, out)
}
out.Mode = n.stableAttr.Mode | (out.Mode & 07777)
return errnoToStatus(errno)
}
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)
if mops, ok := p1.ops.(NodeRenamer); ok {
errno := mops.Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags)
if errno == 0 {
if input.Flags&RENAME_EXCHANGE != 0 {
p1.ExchangeChild(oldName, p2, newName)
} else {
if ok := p1.MvChild(oldName, p2, newName, true); !ok {
log.Println("MvChild failed")
}
}
}
return errnoToStatus(errno)
}
return fuse.ENOTSUP
}
func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
target, _ := b.inode(input.Oldnodeid, 0)
if mops, ok := parent.ops.(NodeLinker); ok {
child, errno := mops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
if errno != 0 {
return errnoToStatus(errno)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
return fuse.ENOTSUP
}
func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, target string, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
if mops, ok := parent.ops.(NodeSymlinker); ok {
child, status := mops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
if status != 0 {
return errnoToStatus(status)
}
b.addNewChild(parent, name, child, nil, 0, out)
child.setEntryOut(out)
b.setEntryOutTimeout(out)
return fuse.OK
}
return fuse.ENOTSUP
}
func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
if linker, ok := n.ops.(NodeReadlinker); ok {
result, errno := linker.Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if errno != 0 {
return nil, errnoToStatus(errno)
}
return result, fuse.OK
}
return nil, fuse.ENOTSUP
}
func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
if a, ok := n.ops.(NodeAccesser); ok {
return errnoToStatus(a.Access(ctx, input.Mask))
}
// default: check attributes.
caller := input.Caller
var out fuse.AttrOut
if s := b.getattr(ctx, n, nil, &out); s != 0 {
return errnoToStatus(s)
}
if !internal.HasAccess(caller.Uid, caller.Gid, out.Uid, out.Gid, out.Mode, input.Mask) {
return fuse.EACCES
}
return fuse.OK
}
// Extended attributes.
func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(NodeGetxattrer); ok {
nb, errno := xops.Getxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data)
return nb, errnoToStatus(errno)
}
return 0, fuse.ENOATTR
}
func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(NodeListxattrer); ok {
sz, errno := xops.Listxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest)
return sz, errnoToStatus(errno)
}
return 0, fuse.OK
}
func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if xops, ok := n.ops.(NodeSetxattrer); ok {
return errnoToStatus(xops.Setxattr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags))
}
return fuse.ENOATTR
}
func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status {
n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(NodeRemovexattrer); ok {
return errnoToStatus(xops.Removexattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr))
}
return fuse.ENOATTR
}
func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if op, ok := n.ops.(NodeOpener); ok {
f, flags, errno := op.Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags)
if errno != 0 {
return errnoToStatus(errno)
}
if f != nil {
b.mu.Lock()
defer b.mu.Unlock()
out.Fh = uint64(b.registerFile(n, f, input.Flags))
}
out.OpenFlags = flags
return fuse.OK
}
return fuse.ENOTSUP
}
// registerFile hands out a file handle. Must have bridge.mu
func (b *rawBridge) registerFile(n *Inode, f FileHandle, flags uint32) uint32 {
var fh uint32
if len(b.freeFiles) > 0 {
last := len(b.freeFiles) - 1
fh = b.freeFiles[last]
b.freeFiles = b.freeFiles[:last]
} else {
fh = uint32(len(b.files))
b.files = append(b.files, &fileEntry{})
}
fileEntry := b.files[fh]
fileEntry.nodeIndex = len(n.openFiles)
fileEntry.file = f
n.openFiles = append(n.openFiles, fh)
return fh
}
func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
if fops, ok := n.ops.(NodeReader); ok {
res, errno := fops.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset))
return res, errnoToStatus(errno)
}
if fr, ok := f.file.(FileReader); ok {
res, errno := fr.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, buf, int64(input.Offset))
return res, errnoToStatus(errno)
}
return nil, fuse.ENOTSUP
}
func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.LkOut) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(NodeGetlker); ok {
return errnoToStatus(lops.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk))
}
if gl, ok := f.file.(FileGetlker); ok {
return errnoToStatus(gl.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags, &out.Lk))
}
return fuse.ENOTSUP
}
func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(NodeSetlker); ok {
return errnoToStatus(lops.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
}
if sl, ok := n.ops.(FileSetlker); ok {
return errnoToStatus(sl.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(NodeSetlkwer); ok {
return errnoToStatus(lops.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
}
if sl, ok := n.ops.(FileSetlkwer); ok {
return errnoToStatus(sl.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) Release(cancel <-chan struct{}, input *fuse.ReleaseIn) {
n, f := b.releaseFileEntry(input.NodeId, input.Fh)
if f == nil {
return
}
f.wg.Wait()
if r, ok := n.ops.(NodeReleaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)
} else if r, ok := f.file.(FileReleaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel})
}
b.mu.Lock()
defer b.mu.Unlock()
b.freeFiles = append(b.freeFiles, uint32(input.Fh))
}
func (b *rawBridge) ReleaseDir(input *fuse.ReleaseIn) {
_, f := b.releaseFileEntry(input.NodeId, input.Fh)
f.wg.Wait()
f.mu.Lock()
if f.dirStream != nil {
f.dirStream.Close()
f.dirStream = nil
}
f.mu.Unlock()
b.mu.Lock()
defer b.mu.Unlock()
b.freeFiles = append(b.freeFiles, uint32(input.Fh))
}
func (b *rawBridge) releaseFileEntry(nid uint64, fh uint64) (*Inode, *fileEntry) {
b.mu.Lock()
defer b.mu.Unlock()
n := b.nodes[nid]
var entry *fileEntry
if fh > 0 {
last := len(n.openFiles) - 1
entry = b.files[fh]
if last != entry.nodeIndex {
n.openFiles[entry.nodeIndex] = n.openFiles[last]
b.files[n.openFiles[entry.nodeIndex]].nodeIndex = entry.nodeIndex
}
n.openFiles = n.openFiles[:last]
}
return n, entry
}
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)
if wr, ok := n.ops.(NodeWriter); ok {
w, errno := wr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset))
return w, errnoToStatus(errno)
}
if fr, ok := f.file.(FileWriter); ok {
w, errno := fr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, data, int64(input.Offset))
return w, errnoToStatus(errno)
}
return 0, fuse.ENOTSUP
}
func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if fl, ok := n.ops.(NodeFlusher); ok {
return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file))
}
if fl, ok := f.file.(FileFlusher); ok {
return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}))
}
return 0
}
func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if fs, ok := n.ops.(NodeFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags))
}
if fs, ok := f.file.(FileFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.FsyncFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if a, ok := n.ops.(NodeAllocater); ok {
return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode))
}
if a, ok := n.ops.(FileAllocater); ok {
return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Offset, input.Length, input.Mode))
}
return fuse.ENOTSUP
}
func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if od, ok := n.ops.(NodeOpendirer); ok {
errno := od.Opendir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if errno != 0 {
return errnoToStatus(errno)
}
}
b.mu.Lock()
defer b.mu.Unlock()
out.Fh = uint64(b.registerFile(n, nil, 0))
return fuse.OK
}
// setStream sets the directory part of f. Must hold f.mu
func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode *Inode, f *fileEntry) syscall.Errno {
if f.dirStream == nil || input.Offset == 0 {
if f.dirStream != nil {
f.dirStream.Close()
f.dirStream = nil
}
str, errno := b.getStream(&fuse.Context{Caller: input.Caller, Cancel: cancel}, inode)
if errno != 0 {
return errno
}
f.hasOverflow = false
f.dirStream = str
}
return 0
}
func (b *rawBridge) getStream(ctx context.Context, inode *Inode) (DirStream, syscall.Errno) {
if rd, ok := inode.ops.(NodeReaddirer); ok {
return rd.Readdir(ctx)
}
r := []fuse.DirEntry{}
for k, ch := range inode.Children() {
r = append(r, fuse.DirEntry{Mode: ch.Mode(),
Name: k,
Ino: ch.StableAttr().Ino})
}
return NewListDirStream(r), 0
}
func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
f.mu.Lock()
defer f.mu.Unlock()
if errno := b.setStream(cancel, input, n, f); errno != 0 {
return errnoToStatus(errno)
}
if f.hasOverflow {
// always succeeds.
out.AddDirEntry(f.overflow)
f.hasOverflow = false
}
for f.dirStream.HasNext() {
e, errno := f.dirStream.Next()
if errno != 0 {
return errnoToStatus(errno)
}
if !out.AddDirEntry(e) {
f.overflow = e
f.hasOverflow = true
return errnoToStatus(errno)
}
}
return fuse.OK
}
func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
f.mu.Lock()
defer f.mu.Unlock()
if errno := b.setStream(cancel, input, n, f); errno != 0 {
return errnoToStatus(errno)
}
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
for f.dirStream.HasNext() || f.hasOverflow {
var e fuse.DirEntry
var errno syscall.Errno
if f.hasOverflow {
e = f.overflow
f.hasOverflow = false
} else {
e, errno = f.dirStream.Next()
}
if errno != 0 {
return errnoToStatus(errno)
}
entryOut := out.AddDirLookupEntry(e)
if entryOut == nil {
f.overflow = e
f.hasOverflow = true
return fuse.OK
}
// Virtual entries "." and ".." should be part of the
// directory listing, but not part of the filesystem tree.
// The values in EntryOut are ignored by Linux
// (see fuse_direntplus_link() in linux/fs/fuse/readdir.c), so leave
// them at zero-value.
if e.Name == "." || e.Name == ".." {
continue
}
child, errno := b.lookup(ctx, n, e.Name, entryOut)
if errno != 0 {
if b.options.NegativeTimeout != nil {
entryOut.SetEntryTimeout(*b.options.NegativeTimeout)
}
} else {
b.addNewChild(n, e.Name, child, nil, 0, entryOut)
child.setEntryOut(entryOut)
b.setEntryOutTimeout(entryOut)
if (e.Mode &^ 07777) != (child.stableAttr.Mode &^ 07777) {
// The file type has changed behind our back. Use the new value.
out.FixMode(child.stableAttr.Mode)
}
entryOut.Mode = child.stableAttr.Mode | (entryOut.Mode & 07777)
}
}
return fuse.OK
}
func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, _ := b.inode(input.NodeId, input.Fh)
if fs, ok := n.ops.(NodeFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fuse.StatfsOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if sf, ok := n.ops.(NodeStatfser); ok {
return errnoToStatus(sf.Statfs(&fuse.Context{Caller: input.Caller, Cancel: cancel}, out))
}
// leave zeroed out
return fuse.OK
}
func (b *rawBridge) Init(s *fuse.Server) {
b.server = s
}
func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRangeIn) (size uint32, status fuse.Status) {
n1, f1 := b.inode(in.NodeId, in.FhIn)
cfr, ok := n1.ops.(NodeCopyFileRanger)
if !ok {
return 0, fuse.ENOTSUP
}
n2, f2 := b.inode(in.NodeIdOut, in.FhOut)
sz, errno := cfr.CopyFileRange(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f1.file, in.OffIn, n2, f2.file, in.OffOut, in.Len, in.Flags)
return sz, errnoToStatus(errno)
}
func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status {
n, f := b.inode(in.NodeId, in.Fh)
ls, ok := n.ops.(NodeLseeker)
if ok {
off, errno := ls.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f.file, in.Offset, in.Whence)
out.Offset = off
return errnoToStatus(errno)
}
if fs, ok := f.file.(FileLseeker); ok {
off, errno := fs.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel}, in.Offset, in.Whence)
out.Offset = off
return errnoToStatus(errno)
}
if in.Whence == _SEEK_DATA || in.Whence == _SEEK_HOLE {
out.Offset = in.Offset
return fuse.OK
}
return fuse.ENOTSUP
}