Commit 153eda7c authored by Kirill Smelkov's avatar Kirill Smelkov

go mod vendor

parent e3e61d67
......@@ -3,11 +3,11 @@ module lab.nexedi.com/nexedi/wendelin.core/wcfs
go 1.14
require (
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/hanwen/go-fuse/v2 v2.0.3 // replaced to -> kirr/go-fuse@y/nodefs-cancel
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/hanwen/go-fuse/v2 v2.0.3 // indirect; replaced to -> kirr/go-fuse@y/nodefs-cancel
github.com/johncgriffin/overflow v0.0.0-20170615021017-4d914c927216
github.com/kisielk/og-rek v1.0.1-0.20180928202415-8b25c4cefd6c
github.com/pkg/errors v0.9.1
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.6.1
lab.nexedi.com/kirr/go123 v0.0.0-20200916121347-316617668e12
lab.nexedi.com/kirr/neo/go v0.0.0-20201012044742-28494187df87
......
Adam H. Leventhal <adam.leventhal@gmail.com>
Daniel Martí <mvdan@mvdan.cc>
Fazlul Shahriar <fshahriar@gmail.com>
Frederick Akalin <akalin@gmail.com>
Google Inc.
Haitao Li <lihaitao@gmail.com>
Jakob Unterwurzacher <jakobunt@gmail.com>
James D. Nurmi <james@abneptis.com>
Jeff <leterip@me.com>
Kaoet Ibe <kaoet.ibe@outlook.com>
Kirill Smelkov <kirr@nexedi.com>
Logan Hanks <logan@bitcasa.com>
Maria Shaldibina <mshaldibina@pivotal.io>
Nick Cooper <gh@smoogle.org>
Patrick Crosby <pcrosby@gmail.com>
Paul Jolly <paul@myitcv.org.uk>
Paul Warren <paul.warren@emc.com>
Shayan Pooya <shayan@arista.com>
Valient Gough <vgough@pobox.com>
Yongwoo Park <nnnlife@gmail.com>
// New BSD License
//
// Copyright (c) 2010 the Go-FUSE Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Ivan Krasin nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
This diff is collapsed.
// 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 (
"os"
"syscall"
"time"
)
func (a *Attr) IsFifo() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFIFO }
// IsChar reports whether the FileInfo describes a character special file.
func (a *Attr) IsChar() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFCHR }
// IsDir reports whether the FileInfo describes a directory.
func (a *Attr) IsDir() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFDIR }
// IsBlock reports whether the FileInfo describes a block special file.
func (a *Attr) IsBlock() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFBLK }
// IsRegular reports whether the FileInfo describes a regular file.
func (a *Attr) IsRegular() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFREG }
// IsSymlink reports whether the FileInfo describes a symbolic link.
func (a *Attr) IsSymlink() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFLNK }
// IsSocket reports whether the FileInfo describes a socket.
func (a *Attr) IsSocket() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFSOCK }
func (a *Attr) SetTimes(access *time.Time, mod *time.Time, chstatus *time.Time) {
if access != nil {
a.Atime = uint64(access.Unix())
a.Atimensec = uint32(access.Nanosecond())
}
if mod != nil {
a.Mtime = uint64(mod.Unix())
a.Mtimensec = uint32(mod.Nanosecond())
}
if chstatus != nil {
a.Ctime = uint64(chstatus.Unix())
a.Ctimensec = uint32(chstatus.Nanosecond())
}
}
func (a *Attr) ChangeTime() time.Time {
return time.Unix(int64(a.Ctime), int64(a.Ctimensec))
}
func (a *Attr) AccessTime() time.Time {
return time.Unix(int64(a.Atime), int64(a.Atimensec))
}
func (a *Attr) ModTime() time.Time {
return time.Unix(int64(a.Mtime), int64(a.Mtimensec))
}
func ToStatT(f os.FileInfo) *syscall.Stat_t {
s, _ := f.Sys().(*syscall.Stat_t)
if s != nil {
return s
}
return nil
}
func ToAttr(f os.FileInfo) *Attr {
if f == nil {
return nil
}
s := ToStatT(f)
if s != nil {
a := &Attr{}
a.FromStat(s)
return a
}
return nil
}
// 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 (
"syscall"
)
func (a *Attr) FromStat(s *syscall.Stat_t) {
a.Ino = uint64(s.Ino)
a.Size = uint64(s.Size)
a.Blocks = uint64(s.Blocks)
a.Atime = uint64(s.Atimespec.Sec)
a.Atimensec = uint32(s.Atimespec.Nsec)
a.Mtime = uint64(s.Mtimespec.Sec)
a.Mtimensec = uint32(s.Mtimespec.Nsec)
a.Ctime = uint64(s.Ctimespec.Sec)
a.Ctimensec = uint32(s.Ctimespec.Nsec)
a.Mode = uint32(s.Mode)
a.Nlink = uint32(s.Nlink)
a.Uid = uint32(s.Uid)
a.Gid = uint32(s.Gid)
a.Rdev = uint32(s.Rdev)
}
// 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 (
"syscall"
)
func (a *Attr) FromStat(s *syscall.Stat_t) {
a.Ino = uint64(s.Ino)
a.Size = uint64(s.Size)
a.Blocks = uint64(s.Blocks)
a.Atime = uint64(s.Atim.Sec)
a.Atimensec = uint32(s.Atim.Nsec)
a.Mtime = uint64(s.Mtim.Sec)
a.Mtimensec = uint32(s.Mtim.Nsec)
a.Ctime = uint64(s.Ctim.Sec)
a.Ctimensec = uint32(s.Ctim.Nsec)
a.Mode = s.Mode
a.Nlink = uint32(s.Nlink)
a.Uid = uint32(s.Uid)
a.Gid = uint32(s.Gid)
a.Rdev = uint32(s.Rdev)
a.Blksize = uint32(s.Blksize)
}
// 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 (
"os"
"sync"
)
// bufferPool implements explicit memory management. It is used for
// minimizing the GC overhead of communicating with the kernel.
type bufferPool struct {
lock sync.Mutex
// For each page size multiple a list of slice pointers.
buffersBySize []*sync.Pool
}
var pageSize = os.Getpagesize()
func (p *bufferPool) getPool(pageCount int) *sync.Pool {
p.lock.Lock()
for len(p.buffersBySize) < pageCount+1 {
p.buffersBySize = append(p.buffersBySize, nil)
}
if p.buffersBySize[pageCount] == nil {
p.buffersBySize[pageCount] = &sync.Pool{
New: func() interface{} { return make([]byte, pageSize*pageCount) },
}
}
pool := p.buffersBySize[pageCount]
p.lock.Unlock()
return pool
}
// AllocBuffer creates a buffer of at least the given size. After use,
// it should be deallocated with FreeBuffer().
func (p *bufferPool) AllocBuffer(size uint32) []byte {
sz := int(size)
if sz < pageSize {
sz = pageSize
}
if sz%pageSize != 0 {
sz += pageSize
}
pages := sz / pageSize
b := p.getPool(pages).Get().([]byte)
return b[:size]
}
// FreeBuffer takes back a buffer if it was allocated through
// AllocBuffer. It is not an error to call FreeBuffer() on a slice
// obtained elsewhere.
func (p *bufferPool) FreeBuffer(slice []byte) {
if slice == nil {
return
}
if cap(slice)%pageSize != 0 || cap(slice) == 0 {
return
}
pages := cap(slice) / pageSize
slice = slice[:cap(slice)]
p.getPool(pages).Put(slice)
}
// 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 (
"os"
"syscall"
)
const (
FUSE_ROOT_ID = 1
FUSE_UNKNOWN_INO = 0xffffffff
CUSE_UNRESTRICTED_IOCTL = (1 << 0)
FUSE_LK_FLOCK = (1 << 0)
FUSE_IOCTL_MAX_IOV = 256
FUSE_POLL_SCHEDULE_NOTIFY = (1 << 0)
CUSE_INIT_INFO_MAX = 4096
S_IFDIR = syscall.S_IFDIR
S_IFREG = syscall.S_IFREG
S_IFLNK = syscall.S_IFLNK
S_IFIFO = syscall.S_IFIFO
CUSE_INIT = 4096
O_ANYWRITE = uint32(os.O_WRONLY | os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_TRUNC)
logicalBlockSize = 512
)
// 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
// arbitrary values
const syscall_O_LARGEFILE = 1 << 29
const syscall_O_NOATIME = 1 << 30
// 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 (
"syscall"
)
const syscall_O_LARGEFILE = syscall.O_LARGEFILE
const syscall_O_NOATIME = syscall.O_NOATIME
// 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))
// 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 (
"os"
)
// NewDefaultRawFileSystem returns ENOSYS (not implemented) for all
// operations.
func NewDefaultRawFileSystem() RawFileSystem {
return (*defaultRawFileSystem)(nil)
}
type defaultRawFileSystem struct{}
func (fs *defaultRawFileSystem) Init(*Server) {
}
func (fs *defaultRawFileSystem) String() string {
return os.Args[0]
}
func (fs *defaultRawFileSystem) SetDebug(dbg bool) {
}
func (fs *defaultRawFileSystem) StatFs(cancel <-chan struct{}, header *InHeader, out *StatfsOut) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) Lookup(cancel <-chan struct{}, header *InHeader, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Forget(nodeID, nlookup uint64) {
}
func (fs *defaultRawFileSystem) GetAttr(cancel <-chan struct{}, input *GetAttrIn, out *AttrOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Open(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status) {
return OK
}
func (fs *defaultRawFileSystem) SetAttr(cancel <-chan struct{}, input *SetAttrIn, out *AttrOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Readlink(cancel <-chan struct{}, header *InHeader) (out []byte, code Status) {
return nil, ENOSYS
}
func (fs *defaultRawFileSystem) Mknod(cancel <-chan struct{}, input *MknodIn, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Mkdir(cancel <-chan struct{}, input *MkdirIn, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Unlink(cancel <-chan struct{}, header *InHeader, name string) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Rmdir(cancel <-chan struct{}, header *InHeader, name string) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Symlink(cancel <-chan struct{}, header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Rename(cancel <-chan struct{}, input *RenameIn, oldName string, newName string) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Link(cancel <-chan struct{}, input *LinkIn, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (size uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (n uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) Access(cancel <-chan struct{}, input *AccessIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Create(cancel <-chan struct{}, input *CreateIn, name string, out *CreateOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) OpenDir(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Read(cancel <-chan struct{}, input *ReadIn, buf []byte) (ReadResult, Status) {
return nil, ENOSYS
}
func (fs *defaultRawFileSystem) GetLk(cancel <-chan struct{}, in *LkIn, out *LkOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) SetLk(cancel <-chan struct{}, in *LkIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) SetLkw(cancel <-chan struct{}, in *LkIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Release(cancel <-chan struct{}, input *ReleaseIn) {
}
func (fs *defaultRawFileSystem) Write(cancel <-chan struct{}, input *WriteIn, data []byte) (written uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) Flush(cancel <-chan struct{}, input *FlushIn) Status {
return OK
}
func (fs *defaultRawFileSystem) Fsync(cancel <-chan struct{}, input *FsyncIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) ReadDir(cancel <-chan struct{}, input *ReadIn, l *DirEntryList) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ReadDirPlus(cancel <-chan struct{}, input *ReadIn, l *DirEntryList) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ReleaseDir(input *ReleaseIn) {
}
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
}
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
}
// 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
// all of the code for DirEntryList.
import (
"fmt"
"unsafe"
)
var eightPadding [8]byte
const direntSize = int(unsafe.Sizeof(_Dirent{}))
// DirEntry is a type for PathFileSystem and NodeFileSystem to return
// directory contents in.
type DirEntry struct {
// Mode is the file's mode. Only the high bits (eg. S_IFDIR)
// are considered.
Mode uint32
// Name is the basename of the file in the directory.
Name string
// Ino is the inode number.
Ino uint64
}
func (d DirEntry) String() string {
return fmt.Sprintf("%o: %q ino=%d", d.Mode, d.Name, d.Ino)
}
// DirEntryList holds the return value for READDIR and READDIRPLUS
// opcodes.
type DirEntryList struct {
buf []byte
// capacity of the underlying buffer
size int
// offset is the requested location in the directory. go-fuse
// currently counts in number of directory entries, but this is an
// implementation detail and may change in the future.
// If `offset` and `fs.fileEntry.dirOffset` disagree, then a
// directory seek has taken place.
offset uint64
// pointer to the last serialized _Dirent. Used by FixMode().
lastDirent *_Dirent
}
// NewDirEntryList creates a DirEntryList with the given data buffer
// and offset.
func NewDirEntryList(data []byte, off uint64) *DirEntryList {
return &DirEntryList{
buf: data[:0],
size: len(data),
offset: off,
}
}
// AddDirEntry tries to add an entry, and reports whether it
// succeeded.
func (l *DirEntryList) AddDirEntry(e DirEntry) bool {
return l.Add(0, e.Name, e.Ino, e.Mode)
}
// Add adds a direntry to the DirEntryList, returning whether it
// succeeded.
func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) bool {
if inode == 0 {
inode = FUSE_UNKNOWN_INO
}
padding := (8 - len(name)&7) & 7
delta := padding + direntSize + len(name) + prefix
oldLen := len(l.buf)
newLen := delta + oldLen
if newLen > l.size {
return false
}
l.buf = l.buf[:newLen]
oldLen += prefix
dirent := (*_Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Off = l.offset + 1
dirent.Ino = inode
dirent.NameLen = uint32(len(name))
dirent.Typ = modeToType(mode)
oldLen += direntSize
copy(l.buf[oldLen:], name)
oldLen += len(name)
if padding > 0 {
copy(l.buf[oldLen:], eightPadding[:padding])
}
l.offset = dirent.Off
return true
}
// AddDirLookupEntry is used for ReadDirPlus. If reserves and zeroizes space
// for an EntryOut struct and serializes a DirEntry.
// On success, it returns pointers to both structs.
// If not enough space was left, it returns two nil pointers.
//
// The resulting READDIRPLUS output buffer looks like this in memory:
// 1) EntryOut{}
// 2) _Dirent{}
// 3) Name (null-terminated)
// 4) Padding to align to 8 bytes
// [repeat]
func (l *DirEntryList) AddDirLookupEntry(e DirEntry) *EntryOut {
const entryOutSize = int(unsafe.Sizeof(EntryOut{}))
oldLen := len(l.buf)
ok := l.Add(entryOutSize, e.Name, e.Ino, e.Mode)
if !ok {
return nil
}
l.lastDirent = (*_Dirent)(unsafe.Pointer(&l.buf[oldLen+entryOutSize]))
entryOut := (*EntryOut)(unsafe.Pointer(&l.buf[oldLen]))
*entryOut = EntryOut{} // zeroize
return entryOut
}
// modeToType converts a file *mode* (as used in syscall.Stat_t.Mode)
// to a file *type* (as used in _Dirent.Typ).
// Equivalent to IFTODT() in libc (see man 5 dirent).
func modeToType(mode uint32) uint32 {
return (mode & 0170000) >> 12
}
// FixMode overrides the file mode of the last direntry that was added. This can
// be needed when a directory changes while READDIRPLUS is running.
// Only the file type bits of mode are considered, the rest is masked out.
func (l *DirEntryList) FixMode(mode uint32) {
l.lastDirent.Typ = modeToType(mode)
}
func (l *DirEntryList) bytes() []byte {
return l.buf
}
// 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.
// Random odds and ends.
package fuse
import (
"fmt"
"log"
"os"
"reflect"
"syscall"
"time"
"unsafe"
)
func (code Status) String() string {
if code <= 0 {
return []string{
"OK",
"NOTIFY_POLL",
"NOTIFY_INVAL_INODE",
"NOTIFY_INVAL_ENTRY",
"NOTIFY_STORE_CACHE",
"NOTIFY_RETRIEVE_CACHE",
"NOTIFY_DELETE",
}[-code]
}
return fmt.Sprintf("%d=%v", int(code), syscall.Errno(code))
}
func (code Status) Ok() bool {
return code == OK
}
// ToStatus extracts an errno number from Go error objects. If it
// fails, it logs an error and returns ENOSYS.
func ToStatus(err error) Status {
switch err {
case nil:
return OK
case os.ErrPermission:
return EPERM
case os.ErrExist:
return Status(syscall.EEXIST)
case os.ErrNotExist:
return ENOENT
case os.ErrInvalid:
return EINVAL
}
switch t := err.(type) {
case syscall.Errno:
return Status(t)
case *os.SyscallError:
return Status(t.Err.(syscall.Errno))
case *os.PathError:
return ToStatus(t.Err)
case *os.LinkError:
return ToStatus(t.Err)
}
log.Println("can't convert error type:", err)
return ENOSYS
}
func toSlice(dest *[]byte, ptr unsafe.Pointer, byteCount uintptr) {
h := (*reflect.SliceHeader)(unsafe.Pointer(dest))
*h = reflect.SliceHeader{
Data: uintptr(ptr),
Len: int(byteCount),
Cap: int(byteCount),
}
}
func CurrentOwner() *Owner {
return &Owner{
Uid: uint32(os.Getuid()),
Gid: uint32(os.Getgid()),
}
}
const _UTIME_OMIT = ((1 << 30) - 2)
// UtimeToTimespec converts a "Time" pointer as passed to Utimens to a
// "Timespec" that can be passed to the utimensat syscall.
// A nil pointer is converted to the special UTIME_OMIT value.
func UtimeToTimespec(t *time.Time) (ts syscall.Timespec) {
if t == nil {
ts.Nsec = _UTIME_OMIT
} else {
ts = syscall.NsecToTimespec(t.UnixNano())
// Go bug https://github.com/golang/go/issues/12777
if ts.Nsec < 0 {
ts.Nsec = 0
}
}
return ts
}
// 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 (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
)
func openFUSEDevice() (*os.File, error) {
fs, err := filepath.Glob("/dev/osxfuse*")
if err != nil {
return nil, err
}
if len(fs) == 0 {
bin := oldLoadBin
if _, err := os.Stat(newLoadBin); err == nil {
bin = newLoadBin
}
cmd := exec.Command(bin)
if err := cmd.Run(); err != nil {
return nil, err
}
fs, err = filepath.Glob("/dev/osxfuse*")
if err != nil {
return nil, err
}
}
for _, fn := range fs {
f, err := os.OpenFile(fn, os.O_RDWR, 0)
if err != nil {
continue
}
return f, nil
}
return nil, fmt.Errorf("all FUSE devices busy")
}
const oldLoadBin = "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs"
const newLoadBin = "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse"
const oldMountBin = "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
const newMountBin = "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse"
func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
f, err := openFUSEDevice()
if err != nil {
return 0, err
}
bin := oldMountBin
if _, err := os.Stat(newMountBin); err == nil {
bin = newMountBin
}
cmd := exec.Command(bin, "-o", strings.Join(opts.optionsStrings(), ","), "-o", fmt.Sprintf("iosize=%d", opts.MaxWrite), "3", mountPoint)
cmd.ExtraFiles = []*os.File{f}
cmd.Env = append(os.Environ(), "MOUNT_FUSEFS_CALL_BY_LIB=", "MOUNT_OSXFUSE_CALL_BY_LIB=",
"MOUNT_OSXFUSE_DAEMON_PATH="+os.Args[0],
"MOUNT_FUSEFS_DAEMON_PATH="+os.Args[0])
var out, errOut bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &errOut
if err := cmd.Start(); err != nil {
f.Close()
return 0, err
}
go func() {
err := cmd.Wait()
if err != nil {
err = fmt.Errorf("mount_osxfusefs failed: %v. Stderr: %s, Stdout: %s", err, errOut.String(), out.String())
}
ready <- err
close(ready)
}()
// The finalizer for f will close its fd so we return a dup.
defer f.Close()
return syscall.Dup(int(f.Fd()))
}
func unmount(dir string, opts *MountOptions) error {
return syscall.Unmount(dir, 0)
}
// 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 (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path"
"strings"
"syscall"
"unsafe"
)
func unixgramSocketpair() (l, r *os.File, err error) {
fd, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
if err != nil {
return nil, nil, os.NewSyscallError("socketpair",
err.(syscall.Errno))
}
l = os.NewFile(uintptr(fd[0]), "socketpair-half1")
r = os.NewFile(uintptr(fd[1]), "socketpair-half2")
return
}
// Create a FUSE FS on the specified mount point without using
// fusermount.
func mountDirect(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
fd, err = syscall.Open("/dev/fuse", os.O_RDWR, 0) // use syscall.Open since we want an int fd
if err != nil {
return
}
// managed to open dev/fuse, attempt to mount
source := opts.FsName
if source == "" {
source = opts.Name
}
var flags uintptr
flags |= syscall.MS_NOSUID | syscall.MS_NODEV
// some values we need to pass to mount, but override possible since opts.Options comes after
var r = []string{
fmt.Sprintf("fd=%d", fd),
"rootmode=40000",
"user_id=0",
"group_id=0",
}
r = append(r, opts.Options...)
if opts.AllowOther {
r = append(r, "allow_other")
}
err = syscall.Mount(opts.FsName, mountPoint, "fuse."+opts.Name, opts.DirectMountFlags, strings.Join(r, ","))
if err != nil {
syscall.Close(fd)
return
}
// success
close(ready)
return
}
// Create a FUSE FS on the specified mount point. The returned
// mount point is always absolute.
func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
if opts.DirectMount {
fd, err := mountDirect(mountPoint, opts, ready)
if err == nil {
return fd, nil
} else if opts.Debug {
log.Printf("mount: failed to do direct mount: %s", err)
}
}
local, remote, err := unixgramSocketpair()
if err != nil {
return
}
defer local.Close()
defer remote.Close()
bin, err := fusermountBinary()
if err != nil {
return 0, err
}
cmd := []string{bin, mountPoint}
if s := opts.optionsStrings(); len(s) > 0 {
cmd = append(cmd, "-o", strings.Join(s, ","))
}
proc, err := os.StartProcess(bin,
cmd,
&os.ProcAttr{
Env: []string{"_FUSE_COMMFD=3"},
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr, remote}})
if err != nil {
return
}
w, err := proc.Wait()
if err != nil {
return
}
if !w.Success() {
err = fmt.Errorf("fusermount exited with code %v\n", w.Sys())
return
}
fd, err = getConnection(local)
if err != nil {
return -1, err
}
// golang sets CLOEXEC on file descriptors when they are
// acquired through normal operations (e.g. open).
// Buf for fd, we have to set CLOEXEC manually
syscall.CloseOnExec(fd)
close(ready)
return fd, err
}
func unmount(mountPoint string, opts *MountOptions) (err error) {
if opts.DirectMount {
// Attempt to directly unmount, if fails fallback to fusermount method
err := syscall.Unmount(mountPoint, 0)
if err == nil {
return nil
}
}
bin, err := fusermountBinary()
if err != nil {
return err
}
errBuf := bytes.Buffer{}
cmd := exec.Command(bin, "-u", mountPoint)
cmd.Stderr = &errBuf
err = cmd.Run()
if errBuf.Len() > 0 {
return fmt.Errorf("%s (code %v)\n",
errBuf.String(), err)
}
return err
}
func getConnection(local *os.File) (int, error) {
var data [4]byte
control := make([]byte, 4*256)
// n, oobn, recvflags, from, errno - todo: error checking.
_, oobn, _, _,
err := syscall.Recvmsg(
int(local.Fd()), data[:], control[:], 0)
if err != nil {
return 0, err
}
message := *(*syscall.Cmsghdr)(unsafe.Pointer(&control[0]))
fd := *(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&control[0])) + syscall.SizeofCmsghdr))
if message.Type != 1 {
return 0, fmt.Errorf("getConnection: recvmsg returned wrong control type: %d", message.Type)
}
if oobn <= syscall.SizeofCmsghdr {
return 0, fmt.Errorf("getConnection: too short control message. Length: %d", oobn)
}
if fd < 0 {
return 0, fmt.Errorf("getConnection: fd < 0: %d", fd)
}
return int(fd), nil
}
// lookPathFallback - search binary in PATH and, if that fails,
// in fallbackDir. This is useful if PATH is possible empty.
func lookPathFallback(file string, fallbackDir string) (string, error) {
binPath, err := exec.LookPath(file)
if err == nil {
return binPath, nil
}
abs := path.Join(fallbackDir, file)
return exec.LookPath(abs)
}
func fusermountBinary() (string, error) {
return lookPathFallback("fusermount", "/bin")
}
func umountBinary() (string, error) {
return lookPathFallback("umount", "/bin")
}
// 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.
// This package is deprecated. New projects should use the package
// "github.com/hanwen/go-fuse/v2/fs" instead.
//
// The nodefs package offers a high level API that resembles the
// kernel's idea of what an FS looks like. File systems can have
// multiple hard-links to one file, for example. It is also suited if
// the data to represent fits in memory: you can construct the
// complete file system tree at mount time
package nodefs
import (
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
// The Node interface implements the user-defined file system
// functionality
type Node interface {
// Inode and SetInode are basic getter/setters. They are
// called by the FileSystemConnector. You get them for free by
// embedding the result of NewDefaultNode() in your node
// struct.
Inode() *Inode
SetInode(node *Inode)
// OnMount is called on the root node just after a mount is
// executed, either when the actual root is mounted, or when a
// filesystem is mounted in-process. The passed-in
// FileSystemConnector gives access to Notify methods and
// Debug settings.
OnMount(conn *FileSystemConnector)
// OnUnmount is executed just before a submount is removed,
// and when the process receives a forget for the FUSE root
// node.
OnUnmount()
// Lookup finds a child node to this node; it is only called
// for directory Nodes. Lookup may be called on nodes that are
// already known.
Lookup(out *fuse.Attr, name string, context *fuse.Context) (*Inode, fuse.Status)
// Deletable() should return true if this node may be discarded once
// the kernel forgets its reference.
// If it returns false, OnForget will never get called for this node. This
// is appropriate if the filesystem has no persistent backing store
// (in-memory filesystems) where discarding the node loses the stored data.
// Deletable will be called from within the treeLock critical section, so you
// cannot look at other nodes.
Deletable() bool
// OnForget is called when the kernel forgets its reference to this node and
// sends a FORGET request. It should perform cleanup and free memory as
// appropriate for the filesystem.
// OnForget is not called if the node is a directory and has children.
// This is called from within a treeLock critical section.
OnForget()
// Misc.
Access(mode uint32, context *fuse.Context) (code fuse.Status)
Readlink(c *fuse.Context) ([]byte, fuse.Status)
// Namespace operations; these are only called on directory Nodes.
// Mknod should create the node, add it to the receiver's
// inode, and return it
Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)
// Mkdir should create the directory Inode, add it to the
// receiver's Inode, and return it
Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)
Unlink(name string, context *fuse.Context) (code fuse.Status)
Rmdir(name string, context *fuse.Context) (code fuse.Status)
// Symlink should create a child inode to the receiver, and
// return it.
Symlink(name string, content string, context *fuse.Context) (*Inode, fuse.Status)
Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status)
// Link should return the Inode of the resulting link. In
// a POSIX conformant file system, this should add 'existing'
// to the receiver, and return the Inode corresponding to
// 'existing'.
Link(name string, existing Node, context *fuse.Context) (newNode *Inode, code fuse.Status)
// Create should return an open file, and the Inode for that file.
Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, child *Inode, code fuse.Status)
// Open opens a file, and returns a File which is associated
// with a file handle. It is OK to return (nil, OK) here. In
// that case, the Node should implement Read or Write
// directly.
Open(flags uint32, context *fuse.Context) (file File, code fuse.Status)
OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status)
Read(file File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status)
Write(file File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status)
// XAttrs
GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status)
RemoveXAttr(attr string, context *fuse.Context) fuse.Status
SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status
ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status)
// File locking
//
// GetLk returns existing lock information for file.
GetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status)
// Sets or clears the lock described by lk on file.
SetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status)
// Sets or clears the lock described by lk. This call blocks until the operation can be completed.
SetLkw(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status)
// Attributes
GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status)
Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status)
Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status)
Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status)
Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status)
Fallocate(file File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status)
StatFs() *fuse.StatfsOut
}
// A File object is returned from FileSystem.Open and
// FileSystem.Create. Include the NewDefaultFile return value into
// the struct to inherit a null implementation.
type File interface {
// Called upon registering the filehandle in the inode. This
// is useful in that PathFS API, where Create/Open have no
// access to the Inode at hand.
SetInode(*Inode)
// The String method is for debug printing.
String() string
// Wrappers around other File implementations, should return
// the inner file here.
InnerFile() File
Read(dest []byte, off int64, ctx *fuse.Context) (fuse.ReadResult, fuse.Status)
Write(data []byte, off int64, ctx *fuse.Context) (written uint32, code fuse.Status)
// File locking
GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, ctx *fuse.Context) (code fuse.Status)
SetLk(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code fuse.Status)
SetLkw(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code 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 *fuse.Context) 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. Any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead.
Release() // XXX +ctx ?
Fsync(flags int, ctx *fuse.Context) (code fuse.Status)
// The methods below may be called on closed files, due to
// concurrency. In that case, you should return EBADF.
Truncate(size uint64, ctx *fuse.Context) fuse.Status
GetAttr(out *fuse.Attr, ctx *fuse.Context) fuse.Status
Chown(uid uint32, gid uint32, ctx *fuse.Context) fuse.Status
Chmod(perms uint32, ctx *fuse.Context) fuse.Status
Utimens(atime *time.Time, mtime *time.Time, ctx *fuse.Context) fuse.Status
Allocate(off uint64, size uint64, mode uint32, ctx *fuse.Context) (code fuse.Status)
}
// Wrap a File return in this to set FUSE flags. Also used internally
// to store open file data.
type WithFlags struct {
File
// For debugging.
Description string
// Put FOPEN_* flags here.
FuseFlags uint32
// O_RDWR, O_TRUNCATE, etc.
OpenFlags uint32
}
// Options contains time out options for a node FileSystem. The
// default copied from libfuse and set in NewMountOptions() is
// (1s,1s,0s).
type Options struct {
EntryTimeout time.Duration
AttrTimeout time.Duration
NegativeTimeout time.Duration
// If set, replace all uids with given UID.
// NewOptions() will set this to the daemon's
// uid/gid.
*fuse.Owner
// This option exists for compatibility and is ignored.
PortableInodes bool
// If set, print debug information.
Debug bool
// If set, issue Lookup rather than GetAttr calls for known
// children. This allows the filesystem to update its inode
// hierarchy in response to kernel calls.
LookupKnownChildren bool
}
// 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 nodefs
import (
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
type defaultFile struct{}
// NewDefaultFile returns a File instance that returns ENOSYS for
// every operation.
func NewDefaultFile() File {
return (*defaultFile)(nil)
}
func (f *defaultFile) SetInode(*Inode) {
}
func (f *defaultFile) InnerFile() File {
return nil
}
func (f *defaultFile) String() string {
return "defaultFile"
}
func (f *defaultFile) Read(buf []byte, off int64, ctx *fuse.Context) (fuse.ReadResult, fuse.Status) {
return nil, fuse.ENOSYS
}
func (f *defaultFile) Write(data []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) {
return 0, fuse.ENOSYS
}
func (f *defaultFile) GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, ctx *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *defaultFile) SetLk(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *defaultFile) SetLkw(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *defaultFile) Flush(ctx *fuse.Context) fuse.Status {
return fuse.OK
}
func (f *defaultFile) Release() {
}
func (f *defaultFile) GetAttr(_ *fuse.Attr, ctx *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (f *defaultFile) Fsync(flags int, ctx *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *defaultFile) Utimens(atime *time.Time, mtime *time.Time, ctx *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (f *defaultFile) Truncate(size uint64, ctx *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (f *defaultFile) Chown(uid uint32, gid uint32, ctx *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (f *defaultFile) Chmod(perms uint32, ctx *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (f *defaultFile) Allocate(off uint64, size uint64, mode uint32, ctx *fuse.Context) (code fuse.Status) {
return fuse.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 nodefs
import (
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
// NewDefaultNode returns an implementation of Node that returns
// ENOSYS for all operations.
func NewDefaultNode() Node {
return &defaultNode{}
}
type defaultNode struct {
inode *Inode
}
func (fs *defaultNode) OnUnmount() {
}
func (fs *defaultNode) OnMount(conn *FileSystemConnector) {
}
func (n *defaultNode) StatFs() *fuse.StatfsOut {
return nil
}
func (n *defaultNode) SetInode(node *Inode) {
n.inode = node
}
func (n *defaultNode) Deletable() bool {
return true
}
func (n *defaultNode) Inode() *Inode {
return n.inode
}
func (n *defaultNode) OnForget() {
}
func (n *defaultNode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (node *Inode, code fuse.Status) {
return nil, fuse.ENOENT
}
func (n *defaultNode) Access(mode uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
return nil, fuse.ENOSYS
}
func (n *defaultNode) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (newNode *Inode, code fuse.Status) {
return nil, fuse.ENOSYS
}
func (n *defaultNode) Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status) {
return nil, fuse.ENOSYS
}
func (n *defaultNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Symlink(name string, content string, context *fuse.Context) (newNode *Inode, code fuse.Status) {
return nil, fuse.ENOSYS
}
func (n *defaultNode) Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Link(name string, existing Node, context *fuse.Context) (newNode *Inode, code fuse.Status) {
return nil, fuse.ENOSYS
}
func (n *defaultNode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, newNode *Inode, code fuse.Status) {
return nil, nil, fuse.ENOSYS
}
func (n *defaultNode) Open(flags uint32, context *fuse.Context) (file File, code fuse.Status) {
return nil, fuse.ENOSYS
}
func (n *defaultNode) Flush(file File, openFlags uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
ch := n.Inode().Children()
s := make([]fuse.DirEntry, 0, len(ch))
for name, child := range ch {
if child.mountPoint != nil {
continue
}
var a fuse.Attr
code := child.Node().GetAttr(&a, nil, context)
if code.Ok() {
s = append(s, fuse.DirEntry{Name: name, Mode: a.Mode})
}
}
return s, fuse.OK
}
func (n *defaultNode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
return nil, fuse.ENOATTR
}
func (n *defaultNode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (n *defaultNode) SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (n *defaultNode) ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status) {
return nil, fuse.ENOSYS
}
func (n *defaultNode) GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status) {
if file != nil {
return file.GetAttr(out, context)
}
if n.Inode().IsDir() {
out.Mode = fuse.S_IFDIR | 0755
} else {
out.Mode = fuse.S_IFREG | 0644
}
return fuse.OK
}
func (n *defaultNode) GetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) SetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) SetLkw(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Fallocate(file File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Read(file File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) {
if file != nil {
return file.Read(dest, off, context)
}
return nil, fuse.ENOSYS
}
func (n *defaultNode) Write(file File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status) {
if file != nil {
return file.Write(data, off, context)
}
return 0, fuse.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 nodefs
import (
"log"
"sync"
"github.com/hanwen/go-fuse/v2/fuse"
)
type connectorDir struct {
node Node
inode *Inode
rawFS fuse.RawFileSystem
// Protect stream and lastOffset. These are written in case
// there is a seek on the directory.
mu sync.Mutex
stream []fuse.DirEntry
}
func (d *connectorDir) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
d.mu.Lock()
defer d.mu.Unlock()
// rewinddir() should be as if reopening directory.
// TODO - test this.
if d.stream == nil || input.Offset == 0 {
d.stream, code = d.node.OpenDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if !code.Ok() {
return code
}
d.stream = append(d.stream, d.inode.getMountDirEntries()...)
d.stream = append(d.stream,
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "."},
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".."})
}
if input.Offset > uint64(len(d.stream)) {
// See https://github.com/hanwen/go-fuse/issues/297
// This can happen for FUSE exported over NFS. This
// seems incorrect, (maybe the kernel is using offsets
// from other opendir/readdir calls), it is harmless to reinforce that
// we have reached EOF.
return fuse.OK
}
todo := d.stream[input.Offset:]
for _, e := range todo {
if e.Name == "" {
log.Printf("got empty directory entry, mode %o.", e.Mode)
continue
}
ok := out.AddDirEntry(e)
if !ok {
break
}
}
return fuse.OK
}
func (d *connectorDir) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
d.mu.Lock()
defer d.mu.Unlock()
// rewinddir() should be as if reopening directory.
if d.stream == nil || input.Offset == 0 {
d.stream, code = d.node.OpenDir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if !code.Ok() {
return code
}
d.stream = append(d.stream, d.inode.getMountDirEntries()...)
d.stream = append(d.stream,
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "."},
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".."})
}
if input.Offset > uint64(len(d.stream)) {
// See comment in Readdir
return fuse.OK
}
todo := d.stream[input.Offset:]
for _, e := range todo {
if e.Name == "" {
log.Printf("got empty directory entry, mode %o.", e.Mode)
continue
}
// we have to be sure entry will fit if we try to add
// it, or we'll mess up the lookup counts.
entryDest := out.AddDirLookupEntry(e)
if entryDest == nil {
break
}
entryDest.Ino = uint64(fuse.FUSE_UNKNOWN_INO)
// No need to fill attributes for . and ..
if e.Name == "." || e.Name == ".." {
continue
}
d.rawFS.Lookup(cancel, &input.InHeader, e.Name, entryDest)
}
return fuse.OK
}
type rawDir interface {
ReadDir(out *fuse.DirEntryList, input *fuse.ReadIn, c *fuse.Context) fuse.Status
ReadDirPlus(out *fuse.DirEntryList, input *fuse.ReadIn, c *fuse.Context) fuse.Status
}
// 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 nodefs
import (
"fmt"
"os"
"sync"
"syscall"
"github.com/hanwen/go-fuse/v2/fuse"
)
// DataFile is for implementing read-only filesystems. This
// assumes we already have the data in memory.
type dataFile struct {
data []byte
File
}
func (f *dataFile) String() string {
l := len(f.data)
if l > 10 {
l = 10
}
return fmt.Sprintf("dataFile(%x)", f.data[:l])
}
func (f *dataFile) GetAttr(out *fuse.Attr, ctx *fuse.Context) fuse.Status {
out.Mode = fuse.S_IFREG | 0644
out.Size = uint64(len(f.data))
return fuse.OK
}
func NewDataFile(data []byte) File {
f := new(dataFile)
f.data = data
f.File = NewDefaultFile()
return f
}
func (f *dataFile) Read(buf []byte, off int64, ctx *fuse.Context) (res fuse.ReadResult, code fuse.Status) {
end := int(off) + int(len(buf))
if end > len(f.data) {
end = len(f.data)
}
return fuse.ReadResultData(f.data[off:end]), fuse.OK
}
type devNullFile struct {
File
}
// NewDevNullFile returns a file that accepts any write, and always
// returns EOF for reads.
func NewDevNullFile() File {
return &devNullFile{
File: NewDefaultFile(),
}
}
func (f *devNullFile) Allocate(off uint64, size uint64, mode uint32, ctx *fuse.Context) (code fuse.Status) {
return fuse.OK
}
func (f *devNullFile) String() string {
return "devNullFile"
}
func (f *devNullFile) Read(buf []byte, off int64, ctx *fuse.Context) (fuse.ReadResult, fuse.Status) {
return fuse.ReadResultData(nil), fuse.OK
}
func (f *devNullFile) Write(content []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) {
return uint32(len(content)), fuse.OK
}
func (f *devNullFile) Flush(ctx *fuse.Context) fuse.Status {
return fuse.OK
}
func (f *devNullFile) Fsync(flags int, ctx *fuse.Context) (code fuse.Status) {
return fuse.OK
}
func (f *devNullFile) Truncate(size uint64, ctx *fuse.Context) (code fuse.Status) {
return fuse.OK
}
////////////////
// LoopbackFile delegates all operations back to an underlying os.File.
func NewLoopbackFile(f *os.File) File {
return &loopbackFile{File: f}
}
type loopbackFile struct {
File *os.File
// os.File is not threadsafe. Although fd themselves are
// constant during the lifetime of an open file, the OS may
// reuse the fd number after it is closed. When open races
// with another close, they may lead to confusion as which
// file gets written in the end.
lock sync.Mutex
}
func (f *loopbackFile) InnerFile() File {
return nil
}
func (f *loopbackFile) SetInode(n *Inode) {
}
func (f *loopbackFile) String() string {
return fmt.Sprintf("loopbackFile(%s)", f.File.Name())
}
func (f *loopbackFile) Read(buf []byte, off int64, ctx *fuse.Context) (res fuse.ReadResult, code fuse.Status) {
f.lock.Lock()
// This is not racy by virtue of the kernel properly
// synchronizing the open/write/close.
r := fuse.ReadResultFd(f.File.Fd(), off, len(buf))
f.lock.Unlock()
return r, fuse.OK
}
func (f *loopbackFile) Write(data []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) {
f.lock.Lock()
n, err := f.File.WriteAt(data, off)
f.lock.Unlock()
return uint32(n), fuse.ToStatus(err)
}
func (f *loopbackFile) Release() {
f.lock.Lock()
f.File.Close()
f.lock.Unlock()
}
func (f *loopbackFile) Flush(ctx *fuse.Context) fuse.Status {
f.lock.Lock()
// 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(int(f.File.Fd()))
f.lock.Unlock()
if err != nil {
return fuse.ToStatus(err)
}
err = syscall.Close(newFd)
return fuse.ToStatus(err)
}
func (f *loopbackFile) Fsync(flags int, ctx *fuse.Context) (code fuse.Status) {
f.lock.Lock()
r := fuse.ToStatus(syscall.Fsync(int(f.File.Fd())))
f.lock.Unlock()
return r
}
const (
F_OFD_GETLK = 36
F_OFD_SETLK = 37
F_OFD_SETLKW = 38
)
func (f *loopbackFile) GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, ctx *fuse.Context) (code fuse.Status) {
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
code = fuse.ToStatus(syscall.FcntlFlock(f.File.Fd(), F_OFD_GETLK, &flk))
out.FromFlockT(&flk)
return
}
func (f *loopbackFile) SetLk(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code fuse.Status) {
return f.setLock(owner, lk, flags, false)
}
func (f *loopbackFile) SetLkw(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code fuse.Status) {
return f.setLock(owner, lk, flags, true)
}
func (f *loopbackFile) setLock(owner uint64, lk *fuse.FileLock, flags uint32, blocking bool) (code fuse.Status) {
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 fuse.EINVAL
}
if !blocking {
op |= syscall.LOCK_NB
}
return fuse.ToStatus(syscall.Flock(int(f.File.Fd()), op))
} else {
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
var op int
if blocking {
op = F_OFD_SETLKW
} else {
op = F_OFD_SETLK
}
return fuse.ToStatus(syscall.FcntlFlock(f.File.Fd(), op, &flk))
}
}
func (f *loopbackFile) Truncate(size uint64, ctx *fuse.Context) fuse.Status {
f.lock.Lock()
r := fuse.ToStatus(syscall.Ftruncate(int(f.File.Fd()), int64(size)))
f.lock.Unlock()
return r
}
func (f *loopbackFile) Chmod(mode uint32, ctx *fuse.Context) fuse.Status {
f.lock.Lock()
r := fuse.ToStatus(f.File.Chmod(os.FileMode(mode)))
f.lock.Unlock()
return r
}
func (f *loopbackFile) Chown(uid uint32, gid uint32, ctx *fuse.Context) fuse.Status {
f.lock.Lock()
r := fuse.ToStatus(f.File.Chown(int(uid), int(gid)))
f.lock.Unlock()
return r
}
func (f *loopbackFile) GetAttr(a *fuse.Attr, ctx *fuse.Context) fuse.Status {
st := syscall.Stat_t{}
f.lock.Lock()
err := syscall.Fstat(int(f.File.Fd()), &st)
f.lock.Unlock()
if err != nil {
return fuse.ToStatus(err)
}
a.FromStat(&st)
return fuse.OK
}
// Utimens implemented in files_linux.go
// Allocate implemented in files_linux.go
////////////////////////////////////////////////////////////////
// NewReadOnlyFile wraps a File so all write operations are denied.
func NewReadOnlyFile(f File) File {
return &readOnlyFile{File: f}
}
type readOnlyFile struct {
File
}
func (f *readOnlyFile) InnerFile() File {
return f.File
}
func (f *readOnlyFile) String() string {
return fmt.Sprintf("readOnlyFile(%s)", f.File.String())
}
func (f *readOnlyFile) Write(data []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) {
return 0, fuse.EPERM
}
func (f *readOnlyFile) Fsync(flag int, ctx *fuse.Context) (code fuse.Status) {
return fuse.OK
}
func (f *readOnlyFile) Truncate(size uint64, ctx *fuse.Context) fuse.Status {
return fuse.EPERM
}
func (f *readOnlyFile) Chmod(mode uint32, ctx *fuse.Context) fuse.Status {
return fuse.EPERM
}
func (f *readOnlyFile) Chown(uid uint32, gid uint32, ctx *fuse.Context) fuse.Status {
return fuse.EPERM
}
func (f *readOnlyFile) Allocate(off uint64, sz uint64, mode uint32, ctx *fuse.Context) fuse.Status {
return fuse.EPERM
}
// 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 nodefs
import (
"syscall"
"time"
"unsafe"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal/utimens"
)
func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
// TODO: Handle `mode` parameter.
// From `man fcntl` on OSX:
// The F_PREALLOCATE command operates on the following structure:
//
// typedef struct fstore {
// u_int32_t fst_flags; /* IN: flags word */
// int fst_posmode; /* IN: indicates offset field */
// off_t fst_offset; /* IN: start of the region */
// off_t fst_length; /* IN: size of the region */
// off_t fst_bytesalloc; /* OUT: number of bytes allocated */
// } fstore_t;
//
// The flags (fst_flags) for the F_PREALLOCATE command are as follows:
//
// F_ALLOCATECONTIG Allocate contiguous space.
//
// F_ALLOCATEALL Allocate all requested space or no space at all.
//
// The position modes (fst_posmode) for the F_PREALLOCATE command indicate how to use the offset field. The modes are as fol-
// lows:
//
// F_PEOFPOSMODE Allocate from the physical end of file.
//
// F_VOLPOSMODE Allocate from the volume offset.
k := struct {
Flags uint32 // u_int32_t
Posmode int64 // int
Offset int64 // off_t
Length int64 // off_t
Bytesalloc int64 // off_t
}{
0,
0,
int64(off),
int64(sz),
0,
}
// Linux version for reference:
// err := syscall.Fallocate(int(f.File.Fd()), mode, int64(off), int64(sz))
f.lock.Lock()
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.File.Fd(), uintptr(syscall.F_PREALLOCATE), uintptr(unsafe.Pointer(&k)))
f.lock.Unlock()
if errno != 0 {
return fuse.ToStatus(errno)
}
return fuse.OK
}
// timeToTimeval - Convert time.Time to syscall.Timeval
//
// Note: This does not use syscall.NsecToTimespec because
// that does not work properly for times before 1970,
// see https://github.com/golang/go/issues/12777
func timeToTimeval(t *time.Time) syscall.Timeval {
var tv syscall.Timeval
tv.Usec = int32(t.Nanosecond() / 1000)
tv.Sec = t.Unix()
return tv
}
// MacOS before High Sierra lacks utimensat() and UTIME_OMIT.
// We emulate using utimes() and extra GetAttr() calls.
func (f *loopbackFile) Utimens(a *time.Time, m *time.Time) fuse.Status {
var attr fuse.Attr
if a == nil || m == nil {
var status fuse.Status
status = f.GetAttr(&attr)
if !status.Ok() {
return status
}
}
tv := utimens.Fill(a, m, &attr)
f.lock.Lock()
err := syscall.Futimes(int(f.File.Fd()), tv)
f.lock.Unlock()
return fuse.ToStatus(err)
}
// 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 nodefs
import (
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32, ctx *fuse.Context) fuse.Status {
f.lock.Lock()
err := syscall.Fallocate(int(f.File.Fd()), mode, int64(off), int64(sz)) // XXX cancel not propagated
f.lock.Unlock()
if err != nil {
return fuse.ToStatus(err)
}
return fuse.OK
}
// Utimens - file handle based version of loopbackFileSystem.Utimens()
func (f *loopbackFile) Utimens(a *time.Time, m *time.Time, ctx *fuse.Context) fuse.Status {
var ts [2]syscall.Timespec
ts[0] = fuse.UtimeToTimespec(a)
ts[1] = fuse.UtimeToTimespec(m)
f.lock.Lock()
err := futimens(int(f.File.Fd()), &ts) // XXX cancel not propagated
f.lock.Unlock()
return fuse.ToStatus(err)
}
// 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 nodefs
import (
"log"
"sync"
"unsafe"
"github.com/hanwen/go-fuse/v2/fuse"
)
// openedFile stores either an open dir or an open file.
type openedFile struct {
handled
WithFlags
dir *connectorDir
}
type fileSystemMount struct {
// Node that we were mounted on.
mountInode *Inode
// Parent to the mountInode.
parentInode *Inode
// Options for the mount.
options *Options
// Protects the "children" and "parents" hashmaps of the inodes
// within the mount.
// treeLock should be acquired before openFilesLock.
//
// If multiple treeLocks must be acquired, the treeLocks
// closer to the root must be acquired first.
treeLock sync.RWMutex
// Manage filehandles of open files.
openFiles handleMap
Debug bool
connector *FileSystemConnector
}
// Must called with lock for parent held.
func (m *fileSystemMount) mountName() string {
for k, v := range m.parentInode.children {
if m.mountInode == v {
return k
}
}
panic("not found")
}
func (m *fileSystemMount) setOwner(attr *fuse.Attr) {
if m.options.Owner != nil {
attr.Owner = *m.options.Owner
}
}
func (m *fileSystemMount) fillEntry(out *fuse.EntryOut) {
out.SetEntryTimeout(m.options.EntryTimeout)
out.SetAttrTimeout(m.options.AttrTimeout)
m.setOwner(&out.Attr)
if out.Mode&fuse.S_IFDIR == 0 && out.Nlink == 0 {
out.Nlink = 1
}
}
func (m *fileSystemMount) fillAttr(out *fuse.AttrOut, nodeId uint64) {
out.SetTimeout(m.options.AttrTimeout)
m.setOwner(&out.Attr)
if out.Ino == 0 {
out.Ino = nodeId
}
}
func (m *fileSystemMount) getOpenedFile(h uint64) *openedFile {
var b *openedFile
if h != 0 {
b = (*openedFile)(unsafe.Pointer(m.openFiles.Decode(h)))
}
if b != nil && m.connector.debug && b.WithFlags.Description != "" {
log.Printf("File %d = %q", h, b.WithFlags.Description)
}
return b
}
func (m *fileSystemMount) unregisterFileHandle(handle uint64, node *Inode) *openedFile {
_, obj := m.openFiles.Forget(handle, 1)
opened := (*openedFile)(unsafe.Pointer(obj))
node.openFilesMutex.Lock()
idx := -1
for i, v := range node.openFiles {
if v == opened {
idx = i
break
}
}
l := len(node.openFiles)
if idx == l-1 {
node.openFiles[idx] = nil
} else {
node.openFiles[idx] = node.openFiles[l-1]
}
node.openFiles = node.openFiles[:l-1]
node.openFilesMutex.Unlock()
return opened
}
// registerFileHandle registers f or dir to have a handle.
//
// The handle is then used as file-handle in communications with kernel.
//
// If dir != nil the handle is registered for OpenDir and the inner file (see
// below) must be nil. If dir = nil the handle is registered for regular open &
// friends.
//
// f can be nil, or a WithFlags that leads to File=nil. For !OpenDir, if that
// is the case, returned handle will be 0 to indicate a handleless open, and
// the filesystem operations on the opened file will be routed to be served by
// the node.
//
// other arguments:
//
// node - Inode for which f or dir were opened,
// flags - file open flags, like O_RDWR.
func (m *fileSystemMount) registerFileHandle(node *Inode, dir *connectorDir, f File, flags uint32) (handle uint64, opened *openedFile) {
b := &openedFile{
dir: dir,
WithFlags: WithFlags{
File: f,
OpenFlags: flags,
},
}
for {
withFlags, ok := f.(*WithFlags)
if !ok {
break
}
b.WithFlags.File = withFlags.File
b.WithFlags.FuseFlags |= withFlags.FuseFlags
b.WithFlags.Description += withFlags.Description
f = withFlags.File
}
// don't allow both dir and file
if dir != nil && b.WithFlags.File != nil {
panic("registerFileHandle: both dir and file are set.")
}
if b.WithFlags.File == nil && dir == nil {
// it was just WithFlags{...}, but the file itself is nil
return 0, b
}
if b.WithFlags.File != nil {
b.WithFlags.File.SetInode(node)
}
node.openFilesMutex.Lock()
node.openFiles = append(node.openFiles, b)
handle, _ = m.openFiles.Register(&b.handled)
node.openFilesMutex.Unlock()
return handle, b
}
// Creates a return entry for a non-existent path.
func (m *fileSystemMount) negativeEntry(out *fuse.EntryOut) bool {
if m.options.NegativeTimeout > 0.0 {
out.NodeId = 0
out.SetEntryTimeout(m.options.NegativeTimeout)
return true
}
return false
}
// 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 nodefs
import (
"github.com/hanwen/go-fuse/v2/fuse"
)
// Mount mounts a filesystem with the given root node on the given directory.
// Convenience wrapper around fuse.NewServer
func Mount(mountpoint string, root Node, mountOptions *fuse.MountOptions, nodefsOptions *Options) (*fuse.Server, *FileSystemConnector, error) {
conn := NewFileSystemConnector(root, nodefsOptions)
s, err := fuse.NewServer(conn.RawFS(), mountpoint, mountOptions)
if err != nil {
return nil, nil, err
}
return s, conn, nil
}
// MountRoot is like Mount but uses default fuse mount options.
func MountRoot(mountpoint string, root Node, opts *Options) (*fuse.Server, *FileSystemConnector, error) {
mountOpts := &fuse.MountOptions{}
if opts != nil && opts.Debug {
mountOpts.Debug = opts.Debug
}
return Mount(mountpoint, root, mountOpts, opts)
}
// 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 nodefs
import (
"log"
"sync"
)
// HandleMap translates objects in Go space to 64-bit handles that can
// be given out to -say- the linux kernel as NodeIds.
//
// The 32 bits version of this is a threadsafe wrapper around a map.
//
// To use it, include "handled" as first member of the structure
// you wish to export.
//
// This structure is thread-safe.
type handleMap interface {
// Register stores "obj" and returns a unique (NodeId, generation) tuple.
Register(obj *handled) (handle, generation uint64)
Count() int
// Decode retrieves a stored object from its 64-bit handle.
Decode(uint64) *handled
// Forget decrements the reference counter for "handle" by "count" and drops
// the object if the refcount reaches zero.
// Returns a boolean whether the object was dropped and the object itself.
Forget(handle uint64, count int) (bool, *handled)
// Handle gets the object's NodeId.
Handle(obj *handled) uint64
// Has checks if NodeId is stored.
Has(uint64) bool
}
type handled struct {
handle uint64
generation uint64
count int
}
func (h *handled) verify() {
if h.count < 0 {
log.Panicf("negative lookup count %d", h.count)
}
if (h.count == 0) != (h.handle == 0) {
log.Panicf("registration mismatch: lookup %d id %d", h.count, h.handle)
}
}
const _ALREADY_MSG = "Object already has a handle"
////////////////////////////////////////////////////////////////
// portable version using 32 bit integers.
type portableHandleMap struct {
sync.RWMutex
// The generation counter is incremented each time a NodeId is reused,
// hence the (NodeId, Generation) tuple is always unique.
generation uint64
// Number of currently used handles
used int
// Array of Go objects indexed by NodeId
handles []*handled
// Free slots in the "handles" array
freeIds []uint64
}
func newPortableHandleMap() *portableHandleMap {
return &portableHandleMap{
// Avoid handing out ID 0 and 1.
handles: []*handled{nil, nil},
}
}
func (m *portableHandleMap) Register(obj *handled) (handle, generation uint64) {
m.Lock()
defer m.Unlock()
// Reuse existing handle
if obj.count != 0 {
obj.count++
return obj.handle, obj.generation
}
// Create a new handle number or recycle one on from the free list
if len(m.freeIds) == 0 {
obj.handle = uint64(len(m.handles))
m.handles = append(m.handles, obj)
} else {
obj.handle = m.freeIds[len(m.freeIds)-1]
m.freeIds = m.freeIds[:len(m.freeIds)-1]
m.handles[obj.handle] = obj
}
// Increment generation number to guarantee the (handle, generation) tuple
// is unique
m.generation++
m.used++
obj.generation = m.generation
obj.count++
return obj.handle, obj.generation
}
func (m *portableHandleMap) Handle(obj *handled) (h uint64) {
m.RLock()
if obj.count == 0 {
h = 0
} else {
h = obj.handle
}
m.RUnlock()
return h
}
func (m *portableHandleMap) Count() int {
m.RLock()
c := m.used
m.RUnlock()
return c
}
func (m *portableHandleMap) Decode(h uint64) *handled {
m.RLock()
v := m.handles[h]
m.RUnlock()
return v
}
func (m *portableHandleMap) Forget(h uint64, count int) (forgotten bool, obj *handled) {
m.Lock()
obj = m.handles[h]
obj.count -= count
if obj.count < 0 {
log.Panicf("underflow: handle %d, count %d, object %d", h, count, obj.count)
} else if obj.count == 0 {
m.handles[h] = nil
m.freeIds = append(m.freeIds, h)
m.used--
forgotten = true
obj.handle = 0
}
m.Unlock()
return forgotten, obj
}
func (m *portableHandleMap) Has(h uint64) bool {
m.RLock()
ok := m.handles[h] != nil
m.RUnlock()
return ok
}
// 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 nodefs
import (
"fmt"
"log"
"sync"
"github.com/hanwen/go-fuse/v2/fuse"
)
type parentData struct {
parent *Inode
name string
}
// An Inode reflects the kernel's idea of the inode. Inodes have IDs
// that are communicated to the kernel, and they have a tree
// structure: a directory Inode may contain named children. Each
// Inode object is paired with a Node object, which file system
// implementers should supply.
type Inode struct {
handled handled
// Number of open files and its protection.
openFilesMutex sync.Mutex
openFiles []*openedFile
fsInode Node
// Each inode belongs to exactly one fileSystemMount. This
// pointer is constant during the lifetime, except upon
// Unmount() when it is set to nil.
mount *fileSystemMount
// All data below is protected by treeLock.
children map[string]*Inode
// Due to hard links, an Inode can have many parents.
parents map[parentData]struct{}
// Non-nil if this inode is a mountpoint, ie. the Root of a
// NodeFileSystem.
mountPoint *fileSystemMount
}
func newInode(isDir bool, fsNode Node) *Inode {
me := new(Inode)
me.parents = map[parentData]struct{}{}
if isDir {
me.children = make(map[string]*Inode, initDirSize)
}
me.fsInode = fsNode
me.fsInode.SetInode(me)
return me
}
// public methods.
// Print the inode. The default print method may not be used for
// debugging, as dumping the map requires synchronization.
func (n *Inode) String() string {
return fmt.Sprintf("node{%d}", n.handled.handle)
}
// Returns any open file, preferably a r/w one.
func (n *Inode) AnyFile() (file File) {
n.openFilesMutex.Lock()
for _, f := range n.openFiles {
if file == nil || f.WithFlags.OpenFlags&fuse.O_ANYWRITE != 0 {
file = f.WithFlags.File
}
}
n.openFilesMutex.Unlock()
return file
}
// Children returns all children of this inode.
func (n *Inode) Children() (out map[string]*Inode) {
n.mount.treeLock.RLock()
out = make(map[string]*Inode, len(n.children))
for k, v := range n.children {
out[k] = v
}
n.mount.treeLock.RUnlock()
return out
}
// Parent returns a random parent and the name this inode has under this parent.
// This function can be used to walk up the directory tree. It will not cross
// sub-mounts.
func (n *Inode) Parent() (parent *Inode, name string) {
if n.mountPoint != nil {
return nil, ""
}
n.mount.treeLock.RLock()
defer n.mount.treeLock.RUnlock()
for k := range n.parents {
return k.parent, k.name
}
return nil, ""
}
// FsChildren returns all the children from the same filesystem. It
// will skip mountpoints.
func (n *Inode) FsChildren() (out map[string]*Inode) {
n.mount.treeLock.RLock()
out = map[string]*Inode{}
for k, v := range n.children {
if v.mount == n.mount {
out[k] = v
}
}
n.mount.treeLock.RUnlock()
return out
}
// Node returns the file-system specific node.
func (n *Inode) Node() Node {
return n.fsInode
}
// Files() returns an opens file that have bits in common with the
// give mask. Use mask==0 to return all files.
func (n *Inode) Files(mask uint32) (files []WithFlags) {
n.openFilesMutex.Lock()
for _, f := range n.openFiles {
if mask == 0 || f.WithFlags.OpenFlags&mask != 0 {
files = append(files, f.WithFlags)
}
}
n.openFilesMutex.Unlock()
return files
}
// IsDir returns true if this is a directory.
func (n *Inode) IsDir() bool {
return n.children != nil
}
// NewChild adds a new child inode to this inode.
func (n *Inode) NewChild(name string, isDir bool, fsi Node) *Inode {
ch := newInode(isDir, fsi)
ch.mount = n.mount
n.AddChild(name, ch)
return ch
}
// GetChild returns a child inode with the given name, or nil if it
// does not exist.
func (n *Inode) GetChild(name string) (child *Inode) {
n.mount.treeLock.RLock()
child = n.children[name]
n.mount.treeLock.RUnlock()
return child
}
// AddChild adds a child inode. The parent inode must be a directory
// node.
func (n *Inode) AddChild(name string, child *Inode) {
if child == nil {
log.Panicf("adding nil child as %q", name)
}
n.mount.treeLock.Lock()
n.addChild(name, child)
n.mount.treeLock.Unlock()
}
// TreeWatcher is an additional interface that Nodes can implement.
// If they do, the OnAdd and OnRemove are called for operations on the
// file system tree. These functions run under a lock, so they should
// not do blocking operations.
type TreeWatcher interface {
OnAdd(parent *Inode, name string)
OnRemove(parent *Inode, name string)
}
// RmChild removes an inode by name, and returns it. It returns nil if
// child does not exist.
func (n *Inode) RmChild(name string) (ch *Inode) {
n.mount.treeLock.Lock()
ch = n.rmChild(name)
n.mount.treeLock.Unlock()
return
}
//////////////////////////////////////////////////////////////
// private
// addChild adds "child" to our children under name "name".
// Must be called with treeLock for the mount held.
func (n *Inode) addChild(name string, child *Inode) {
if paranoia {
ch := n.children[name]
if ch != nil {
log.Panicf("Already have an Inode with same name: %v: %v", name, ch)
}
}
n.children[name] = child
child.parents[parentData{n, name}] = struct{}{}
if w, ok := child.Node().(TreeWatcher); ok && child.mountPoint == nil {
w.OnAdd(n, name)
}
}
// rmChild throws out child "name". This means (1) deleting "name" from our
// "children" map and (2) deleting ourself from the child's "parents" map.
// Must be called with treeLock for the mount held.
func (n *Inode) rmChild(name string) *Inode {
ch := n.children[name]
if ch != nil {
delete(n.children, name)
delete(ch.parents, parentData{n, name})
if w, ok := ch.Node().(TreeWatcher); ok && ch.mountPoint == nil {
w.OnRemove(n, name)
}
}
return ch
}
// Can only be called on untouched root inodes.
func (n *Inode) mountFs(opts *Options) {
n.mountPoint = &fileSystemMount{
openFiles: newPortableHandleMap(),
mountInode: n,
options: opts,
}
n.mount = n.mountPoint
}
// Must be called with treeLock held.
func (n *Inode) canUnmount() bool {
for _, v := range n.children {
if v.mountPoint != nil {
// This access may be out of date, but it is no
// problem to err on the safe side.
return false
}
if !v.canUnmount() {
return false
}
}
n.openFilesMutex.Lock()
ok := len(n.openFiles) == 0
n.openFilesMutex.Unlock()
return ok
}
func (n *Inode) getMountDirEntries() (out []fuse.DirEntry) {
n.mount.treeLock.RLock()
for k, v := range n.children {
if v.mountPoint != nil {
out = append(out, fuse.DirEntry{
Name: k,
Mode: fuse.S_IFDIR,
})
}
}
n.mount.treeLock.RUnlock()
return out
}
const initDirSize = 20
func (n *Inode) verify(cur *fileSystemMount) {
n.handled.verify()
if n.mountPoint != nil {
if n != n.mountPoint.mountInode {
log.Panicf("mountpoint mismatch %v %v", n, n.mountPoint.mountInode)
}
cur = n.mountPoint
cur.treeLock.Lock()
defer cur.treeLock.Unlock()
}
if n.mount != cur {
log.Panicf("n.mount not set correctly %v %v", n.mount, cur)
}
for nm, ch := range n.children {
if ch == nil {
log.Panicf("Found nil child: %q", nm)
}
ch.verify(cur)
}
}
// 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 nodefs
import (
"fmt"
"sync"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
type lockingFile struct {
mu *sync.Mutex
file File
}
// NewLockingFile serializes operations an existing File.
func NewLockingFile(mu *sync.Mutex, f File) File {
return &lockingFile{
mu: mu,
file: f,
}
}
func (f *lockingFile) SetInode(*Inode) {
}
func (f *lockingFile) InnerFile() File {
return f.file
}
func (f *lockingFile) String() string {
return fmt.Sprintf("lockingFile(%s)", f.file.String())
}
func (f *lockingFile) Read(buf []byte, off int64, ctx *fuse.Context) (fuse.ReadResult, fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Read(buf, off, ctx)
}
func (f *lockingFile) Write(data []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Write(data, off, ctx)
}
func (f *lockingFile) Flush(ctx *fuse.Context) fuse.Status {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Flush(ctx)
}
func (f *lockingFile) GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, ctx *fuse.Context) (code fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.GetLk(owner, lk, flags, out, ctx)
}
func (f *lockingFile) SetLk(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.SetLk(owner, lk, flags, ctx)
}
func (f *lockingFile) SetLkw(owner uint64, lk *fuse.FileLock, flags uint32, ctx *fuse.Context) (code fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.SetLkw(owner, lk, flags, ctx)
}
func (f *lockingFile) Release() {
f.mu.Lock()
defer f.mu.Unlock()
f.file.Release()
}
func (f *lockingFile) GetAttr(a *fuse.Attr, ctx *fuse.Context) fuse.Status {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.GetAttr(a, ctx)
}
func (f *lockingFile) Fsync(flags int, ctx *fuse.Context) (code fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Fsync(flags, ctx)
}
func (f *lockingFile) Utimens(atime *time.Time, mtime *time.Time, ctx *fuse.Context) fuse.Status {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Utimens(atime, mtime, ctx)
}
func (f *lockingFile) Truncate(size uint64, ctx *fuse.Context) fuse.Status {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Truncate(size, ctx)
}
func (f *lockingFile) Chown(uid uint32, gid uint32, ctx *fuse.Context) fuse.Status {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Chown(uid, gid, ctx)
}
func (f *lockingFile) Chmod(perms uint32, ctx *fuse.Context) fuse.Status {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Chmod(perms, ctx)
}
func (f *lockingFile) Allocate(off uint64, size uint64, mode uint32, ctx *fuse.Context) (code fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Allocate(off, size, mode, ctx)
}
// 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 nodefs
import (
"fmt"
"os"
"sync"
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
// NewMemNodeFSRoot creates an in-memory node-based filesystem. Files
// are written into a backing store under the given prefix.
func NewMemNodeFSRoot(prefix string) Node {
fs := &memNodeFs{
backingStorePrefix: prefix,
}
fs.root = fs.newNode()
return fs.root
}
type memNodeFs struct {
backingStorePrefix string
root *memNode
mutex sync.Mutex
nextFree int
}
func (fs *memNodeFs) String() string {
return fmt.Sprintf("MemNodeFs(%s)", fs.backingStorePrefix)
}
func (fs *memNodeFs) Root() Node {
return fs.root
}
func (fs *memNodeFs) SetDebug(bool) {
}
func (fs *memNodeFs) OnMount(*FileSystemConnector) {
}
func (fs *memNodeFs) OnUnmount() {
}
func (fs *memNodeFs) newNode() *memNode {
fs.mutex.Lock()
id := fs.nextFree
fs.nextFree++
fs.mutex.Unlock()
n := &memNode{
Node: NewDefaultNode(),
fs: fs,
id: id,
}
now := time.Now()
n.info.SetTimes(&now, &now, &now)
n.info.Mode = fuse.S_IFDIR | 0777
return n
}
func (fs *memNodeFs) Filename(n *Inode) string {
mn := n.Node().(*memNode)
return mn.filename()
}
type memNode struct {
Node
fs *memNodeFs
id int
mu sync.Mutex
link string
info fuse.Attr
}
func (n *memNode) filename() string {
return fmt.Sprintf("%s%d", n.fs.backingStorePrefix, n.id)
}
func (n *memNode) Deletable() bool {
return false
}
func (n *memNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
n.mu.Lock()
defer n.mu.Unlock()
return []byte(n.link), fuse.OK
}
func (n *memNode) StatFs() *fuse.StatfsOut {
return &fuse.StatfsOut{}
}
func (n *memNode) Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status) {
ch := n.fs.newNode()
ch.info.Mode = mode | fuse.S_IFDIR
n.Inode().NewChild(name, true, ch)
return ch.Inode(), fuse.OK
}
func (n *memNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
ch := n.Inode().RmChild(name)
if ch == nil {
return fuse.ENOENT
}
return fuse.OK
}
func (n *memNode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
return n.Unlink(name, context)
}
func (n *memNode) Symlink(name string, content string, context *fuse.Context) (newNode *Inode, code fuse.Status) {
ch := n.fs.newNode()
ch.info.Mode = fuse.S_IFLNK | 0777
ch.link = content
n.Inode().NewChild(name, false, ch)
return ch.Inode(), fuse.OK
}
func (n *memNode) Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status) {
ch := n.Inode().RmChild(oldName)
newParent.Inode().RmChild(newName)
newParent.Inode().AddChild(newName, ch)
return fuse.OK
}
func (n *memNode) Link(name string, existing Node, context *fuse.Context) (*Inode, fuse.Status) {
n.Inode().AddChild(name, existing.Inode())
return existing.Inode(), fuse.OK
}
func (n *memNode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, node *Inode, code fuse.Status) {
ch := n.fs.newNode()
ch.info.Mode = mode | fuse.S_IFREG
f, err := os.Create(ch.filename())
if err != nil {
return nil, nil, fuse.ToStatus(err)
}
n.Inode().NewChild(name, false, ch)
return ch.newFile(f), ch.Inode(), fuse.OK
}
type memNodeFile struct {
File
node *memNode
}
func (n *memNodeFile) String() string {
return fmt.Sprintf("memNodeFile(%s)", n.File.String())
}
func (n *memNodeFile) InnerFile() File {
return n.File
}
func (n *memNodeFile) Flush(ctx *fuse.Context) fuse.Status {
code := n.File.Flush(ctx)
if !code.Ok() {
return code
}
st := syscall.Stat_t{}
err := syscall.Stat(n.node.filename(), &st)
n.node.mu.Lock()
defer n.node.mu.Unlock()
n.node.info.Size = uint64(st.Size)
n.node.info.Blocks = uint64(st.Blocks)
return fuse.ToStatus(err)
}
func (n *memNode) newFile(f *os.File) File {
return &memNodeFile{
File: NewLoopbackFile(f),
node: n,
}
}
func (n *memNode) Open(flags uint32, context *fuse.Context) (file File, code fuse.Status) {
f, err := os.OpenFile(n.filename(), int(flags), 0666)
if err != nil {
return nil, fuse.ToStatus(err)
}
return n.newFile(f), fuse.OK
}
func (n *memNode) GetAttr(fi *fuse.Attr, file File, context *fuse.Context) (code fuse.Status) {
n.mu.Lock()
defer n.mu.Unlock()
*fi = n.info
return fuse.OK
}
func (n *memNode) Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status) {
if file != nil {
code = file.Truncate(size, context)
} else {
err := os.Truncate(n.filename(), int64(size))
code = fuse.ToStatus(err)
}
if code.Ok() {
now := time.Now()
n.mu.Lock()
defer n.mu.Unlock()
n.info.SetTimes(nil, nil, &now)
// TODO - should update mtime too?
n.info.Size = size
}
return code
}
func (n *memNode) Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
c := time.Now()
n.mu.Lock()
defer n.mu.Unlock()
n.info.SetTimes(atime, mtime, &c)
return fuse.OK
}
func (n *memNode) Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status) {
n.info.Mode = (n.info.Mode &^ 07777) | perms
now := time.Now()
n.mu.Lock()
defer n.mu.Unlock()
n.info.SetTimes(nil, nil, &now)
return fuse.OK
}
func (n *memNode) Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
n.info.Uid = uid
n.info.Gid = gid
now := time.Now()
n.mu.Lock()
defer n.mu.Unlock()
n.info.SetTimes(nil, nil, &now)
return fuse.OK
}
// 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 nodefs
import (
"syscall"
"unsafe"
)
// futimens - futimens(3) calls utimensat(2) with "pathname" set to null and
// "flags" set to zero
func futimens(fd int, times *[2]syscall.Timespec) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(fd), 0, uintptr(unsafe.Pointer(times)), uintptr(0), 0, 0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}
package fuse
// Go 1.9 introduces polling for file I/O. The implementation causes
// the runtime's epoll to take up the last GOMAXPROCS slot, and if
// that happens, we won't have any threads left to service FUSE's
// _OP_POLL request. Prevent this by forcing _OP_POLL to happen, so we
// can say ENOSYS and prevent further _OP_POLL requests.
const pollHackName = ".go-fuse-epoll-hack"
const pollHackInode = ^uint64(0)
func doPollHackLookup(ms *Server, req *request) {
attr := Attr{
Ino: pollHackInode,
Mode: S_IFREG | 0644,
Nlink: 1,
}
switch req.inHeader.Opcode {
case _OP_LOOKUP:
out := (*EntryOut)(req.outData())
*out = EntryOut{
NodeId: pollHackInode,
Attr: attr,
}
req.status = OK
case _OP_OPEN:
out := (*OpenOut)(req.outData())
*out = OpenOut{
Fh: pollHackInode,
}
req.status = OK
case _OP_GETATTR:
out := (*AttrOut)(req.outData())
out.Attr = attr
req.status = OK
case _OP_POLL:
req.status = ENOSYS
default:
// We want to avoid switching off features through our
// poll hack, so don't use ENOSYS
req.status = ERANGE
}
}
package fuse
import (
"path/filepath"
"syscall"
"unsafe"
)
type pollFd struct {
Fd int32
Events int16
Revents int16
}
func sysPoll(fds []pollFd, timeout int) (n int, err error) {
r0, _, e1 := syscall.Syscall(syscall.SYS_POLL, uintptr(unsafe.Pointer(&fds[0])),
uintptr(len(fds)), uintptr(timeout))
n = int(r0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return n, err
}
func pollHack(mountPoint string) error {
const (
POLLIN = 0x1
POLLPRI = 0x2
POLLOUT = 0x4
POLLRDHUP = 0x2000
POLLERR = 0x8
POLLHUP = 0x10
)
fd, err := syscall.Open(filepath.Join(mountPoint, pollHackName), syscall.O_CREAT|syscall.O_TRUNC|syscall.O_RDWR, 0644)
if err != nil {
return err
}
pollData := []pollFd{{
Fd: int32(fd),
Events: POLLIN | POLLPRI | POLLOUT,
}}
// Trigger _OP_POLL, so we can say ENOSYS. We don't care about
// the return value.
sysPoll(pollData, 0)
syscall.Close(fd)
return nil
}
package fuse
import (
"path/filepath"
"syscall"
"golang.org/x/sys/unix"
)
func pollHack(mountPoint string) error {
fd, err := syscall.Open(filepath.Join(mountPoint, pollHackName), syscall.O_RDONLY, 0)
if err != nil {
return err
}
pollData := []unix.PollFd{{
Fd: int32(fd),
Events: unix.POLLIN | unix.POLLPRI | unix.POLLOUT,
}}
// Trigger _OP_POLL, so we can say ENOSYS. We don't care about
// the return value.
unix.Poll(pollData, 0)
syscall.Close(fd)
return nil
}
// 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"
"os"
"strings"
"syscall"
)
var (
writeFlagNames = map[int64]string{
WRITE_CACHE: "CACHE",
WRITE_LOCKOWNER: "LOCKOWNER",
}
readFlagNames = map[int64]string{
READ_LOCKOWNER: "LOCKOWNER",
}
initFlagNames = map[int64]string{
CAP_ASYNC_READ: "ASYNC_READ",
CAP_POSIX_LOCKS: "POSIX_LOCKS",
CAP_FILE_OPS: "FILE_OPS",
CAP_ATOMIC_O_TRUNC: "ATOMIC_O_TRUNC",
CAP_EXPORT_SUPPORT: "EXPORT_SUPPORT",
CAP_BIG_WRITES: "BIG_WRITES",
CAP_DONT_MASK: "DONT_MASK",
CAP_SPLICE_WRITE: "SPLICE_WRITE",
CAP_SPLICE_MOVE: "SPLICE_MOVE",
CAP_SPLICE_READ: "SPLICE_READ",
CAP_FLOCK_LOCKS: "FLOCK_LOCKS",
CAP_IOCTL_DIR: "IOCTL_DIR",
CAP_AUTO_INVAL_DATA: "AUTO_INVAL_DATA",
CAP_READDIRPLUS: "READDIRPLUS",
CAP_READDIRPLUS_AUTO: "READDIRPLUS_AUTO",
CAP_ASYNC_DIO: "ASYNC_DIO",
CAP_WRITEBACK_CACHE: "WRITEBACK_CACHE",
CAP_NO_OPEN_SUPPORT: "NO_OPEN_SUPPORT",
CAP_PARALLEL_DIROPS: "PARALLEL_DIROPS",
CAP_POSIX_ACL: "POSIX_ACL",
CAP_HANDLE_KILLPRIV: "HANDLE_KILLPRIV",
CAP_ABORT_ERROR: "ABORT_ERROR",
CAP_MAX_PAGES: "MAX_PAGES",
CAP_CACHE_SYMLINKS: "CACHE_SYMLINKS",
CAP_NO_OPENDIR_SUPPORT: "NO_OPENDIR_SUPPORT",
CAP_EXPLICIT_INVAL_DATA: "EXPLICIT_INVAL_DATA",
}
releaseFlagNames = map[int64]string{
RELEASE_FLUSH: "FLUSH",
}
openFlagNames = map[int64]string{
int64(os.O_WRONLY): "WRONLY",
int64(os.O_RDWR): "RDWR",
int64(os.O_APPEND): "APPEND",
int64(syscall.O_ASYNC): "ASYNC",
int64(os.O_CREATE): "CREAT",
int64(os.O_EXCL): "EXCL",
int64(syscall.O_NOCTTY): "NOCTTY",
int64(syscall.O_NONBLOCK): "NONBLOCK",
int64(os.O_SYNC): "SYNC",
int64(os.O_TRUNC): "TRUNC",
int64(syscall.O_CLOEXEC): "CLOEXEC",
int64(syscall.O_DIRECTORY): "DIRECTORY",
}
fuseOpenFlagNames = map[int64]string{
FOPEN_DIRECT_IO: "DIRECT",
FOPEN_KEEP_CACHE: "CACHE",
FOPEN_NONSEEKABLE: "NONSEEK",
FOPEN_CACHE_DIR: "CACHE_DIR",
FOPEN_STREAM: "STREAM",
}
accessFlagName = map[int64]string{
X_OK: "x",
W_OK: "w",
R_OK: "r",
}
)
func flagString(names map[int64]string, fl int64, def string) string {
s := []string{}
for k, v := range names {
if fl&k != 0 {
s = append(s, v)
fl ^= k
}
}
if len(s) == 0 && def != "" {
s = []string{def}
}
if fl != 0 {
s = append(s, fmt.Sprintf("0x%x", fl))
}
return strings.Join(s, ",")
}
func (in *ForgetIn) string() string {
return fmt.Sprintf("{Nlookup=%d}", in.Nlookup)
}
func (in *_BatchForgetIn) string() string {
return fmt.Sprintf("{Count=%d}", in.Count)
}
func (in *MkdirIn) string() string {
return fmt.Sprintf("{0%o (0%o)}", in.Mode, in.Umask)
}
func (in *Rename1In) string() string {
return fmt.Sprintf("{i%d}", in.Newdir)
}
func (in *RenameIn) string() string {
return fmt.Sprintf("{i%d %x}", in.Newdir, in.Flags)
}
func (in *SetAttrIn) string() string {
s := []string{}
if in.Valid&FATTR_MODE != 0 {
s = append(s, fmt.Sprintf("mode 0%o", in.Mode))
}
if in.Valid&FATTR_UID != 0 {
s = append(s, fmt.Sprintf("uid %d", in.Uid))
}
if in.Valid&FATTR_GID != 0 {
s = append(s, fmt.Sprintf("gid %d", in.Gid))
}
if in.Valid&FATTR_SIZE != 0 {
s = append(s, fmt.Sprintf("size %d", in.Size))
}
if in.Valid&FATTR_ATIME != 0 {
s = append(s, fmt.Sprintf("atime %d.%09d", in.Atime, in.Atimensec))
}
if in.Valid&FATTR_MTIME != 0 {
s = append(s, fmt.Sprintf("mtime %d.%09d", in.Mtime, in.Mtimensec))
}
if in.Valid&FATTR_FH != 0 {
s = append(s, fmt.Sprintf("fh %d", in.Fh))
}
// TODO - FATTR_ATIME_NOW = (1 << 7), FATTR_MTIME_NOW = (1 << 8), FATTR_LOCKOWNER = (1 << 9)
return fmt.Sprintf("{%s}", strings.Join(s, ", "))
}
func (in *ReleaseIn) string() string {
return fmt.Sprintf("{Fh %d %s %s L%d}",
in.Fh, flagString(openFlagNames, int64(in.Flags), ""),
flagString(releaseFlagNames, int64(in.ReleaseFlags), ""),
in.LockOwner)
}
func (in *OpenIn) string() string {
return fmt.Sprintf("{%s}", flagString(openFlagNames, int64(in.Flags), "O_RDONLY"))
}
func (in *OpenOut) string() string {
return fmt.Sprintf("{Fh %d %s}", in.Fh,
flagString(fuseOpenFlagNames, int64(in.OpenFlags), ""))
}
func (in *InitIn) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s}",
in.Major, in.Minor, in.MaxReadAhead,
flagString(initFlagNames, int64(in.Flags), ""))
}
func (o *InitOut) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s %d/%d Wr 0x%x Tg 0x%x}",
o.Major, o.Minor, o.MaxReadAhead,
flagString(initFlagNames, int64(o.Flags), ""),
o.CongestionThreshold, o.MaxBackground, o.MaxWrite,
o.TimeGran)
}
func (s *FsyncIn) string() string {
return fmt.Sprintf("{Fh %d Flags %x}", s.Fh, s.FsyncFlags)
}
func (in *SetXAttrIn) string() string {
return fmt.Sprintf("{sz %d f%o}", in.Size, in.Flags)
}
func (in *GetXAttrIn) string() string {
return fmt.Sprintf("{sz %d}", in.Size)
}
func (o *GetXAttrOut) string() string {
return fmt.Sprintf("{sz %d}", o.Size)
}
func (in *AccessIn) string() string {
return fmt.Sprintf("{u=%d g=%d %s}",
in.Uid,
in.Gid,
flagString(accessFlagName, int64(in.Mask), ""))
}
func (in *FlushIn) string() string {
return fmt.Sprintf("{Fh %d}", in.Fh)
}
func (o *AttrOut) string() string {
return fmt.Sprintf(
"{tA=%gs %v}",
ft(o.AttrValid, o.AttrValidNsec), &o.Attr)
}
// ft converts (seconds , nanoseconds) -> float(seconds)
func ft(tsec uint64, tnsec uint32) float64 {
return float64(tsec) + float64(tnsec)*1E-9
}
// Returned by LOOKUP
func (o *EntryOut) string() string {
return fmt.Sprintf("{i%d g%d tE=%gs tA=%gs %v}",
o.NodeId, o.Generation, ft(o.EntryValid, o.EntryValidNsec),
ft(o.AttrValid, o.AttrValidNsec), &o.Attr)
}
func (o *CreateOut) string() string {
return fmt.Sprintf("{i%d g%d %v %v}", o.NodeId, o.Generation, &o.EntryOut, &o.OpenOut)
}
func (o *StatfsOut) string() string {
return fmt.Sprintf(
"{blocks (%d,%d)/%d files %d/%d bs%d nl%d frs%d}",
o.Bfree, o.Bavail, o.Blocks, o.Ffree, o.Files,
o.Bsize, o.NameLen, o.Frsize)
}
func (o *NotifyInvalEntryOut) string() string {
return fmt.Sprintf("{parent i%d sz %d}", o.Parent, o.NameLen)
}
func (o *NotifyInvalInodeOut) string() string {
return fmt.Sprintf("{i%d [%d +%d)}", o.Ino, o.Off, o.Length)
}
func (o *NotifyInvalDeleteOut) string() string {
return fmt.Sprintf("{parent i%d ch i%d sz %d}", o.Parent, o.Child, o.NameLen)
}
func (o *NotifyStoreOut) string() string {
return fmt.Sprintf("{i%d [%d +%d)}", o.Nodeid, o.Offset, o.Size)
}
func (o *NotifyRetrieveOut) string() string {
return fmt.Sprintf("{> %d: i%d [%d +%d)}", o.NotifyUnique, o.Nodeid, o.Offset, o.Size)
}
func (i *NotifyRetrieveIn) string() string {
return fmt.Sprintf("{[%d +%d)}", i.Offset, i.Size)
}
func (f *FallocateIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) mod 0%o}",
f.Fh, f.Offset, f.Length, f.Mode)
}
func (f *LinkIn) string() string {
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 {
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
func Print(obj interface{}) string {
t, ok := obj.(interface {
string() string
})
if ok {
return t.string()
}
return fmt.Sprintf("%T: %v", obj, obj)
}
// 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"
)
func init() {
initFlagNames[CAP_XTIMES] = "XTIMES"
initFlagNames[CAP_VOL_RENAME] = "VOL_RENAME"
initFlagNames[CAP_CASE_INSENSITIVE] = "CASE_INSENSITIVE"
}
func (a *Attr) string() string {
return fmt.Sprintf(
"{M0%o SZ=%d L=%d "+
"%d:%d "+
"%d %d:%d "+
"A %f "+
"M %f "+
"C %f}",
a.Mode, a.Size, a.Nlink,
a.Uid, a.Gid,
a.Blocks,
a.Rdev, a.Ino, ft(a.Atime, a.Atimensec), ft(a.Mtime, a.Mtimensec),
ft(a.Ctime, a.Ctimensec))
}
func (me *CreateIn) string() string {
return fmt.Sprintf(
"{0%o [%s]}", me.Mode,
flagString(openFlagNames, int64(me.Flags), "O_RDONLY"))
}
func (me *GetAttrIn) string() string { return "" }
func (me *MknodIn) string() string {
return fmt.Sprintf("{0%o, %d}", me.Mode, me.Rdev)
}
func (me *ReadIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s}",
me.Fh, me.Offset, me.Size,
flagString(readFlagNames, int64(me.ReadFlags), ""))
}
func (me *WriteIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s}",
me.Fh, me.Offset, me.Size,
flagString(writeFlagNames, int64(me.WriteFlags), ""))
}
// 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"
"syscall"
)
func init() {
openFlagNames[syscall.O_DIRECT] = "DIRECT"
openFlagNames[syscall.O_LARGEFILE] = "LARGEFILE"
openFlagNames[syscall_O_NOATIME] = "NOATIME"
}
func (a *Attr) string() string {
return fmt.Sprintf(
"{M0%o SZ=%d L=%d "+
"%d:%d "+
"B%d*%d i%d:%d "+
"A %f "+
"M %f "+
"C %f}",
a.Mode, a.Size, a.Nlink,
a.Uid, a.Gid,
a.Blocks, a.Blksize,
a.Rdev, a.Ino, ft(a.Atime, a.Atimensec), ft(a.Mtime, a.Mtimensec),
ft(a.Ctime, a.Ctimensec))
}
func (in *CreateIn) string() string {
return fmt.Sprintf(
"{0%o [%s] (0%o)}", in.Mode,
flagString(openFlagNames, int64(in.Flags), "O_RDONLY"), in.Umask)
}
func (in *GetAttrIn) string() string {
return fmt.Sprintf("{Fh %d}", in.Fh_)
}
func (in *MknodIn) string() string {
return fmt.Sprintf("{0%o (0%o), %d}", in.Mode, in.Umask, in.Rdev)
}
func (in *ReadIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s L %d %s}",
in.Fh, in.Offset, in.Size,
flagString(readFlagNames, int64(in.ReadFlags), ""),
in.LockOwner,
flagString(openFlagNames, int64(in.Flags), "RDONLY"))
}
func (in *WriteIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s L %d %s}",
in.Fh, in.Offset, in.Size,
flagString(writeFlagNames, int64(in.WriteFlags), ""),
in.LockOwner,
flagString(openFlagNames, int64(in.Flags), "RDONLY"))
}
This diff is collapsed.
// 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
func (a *Attr) String() string {
return Print(a)
}
This diff is collapsed.
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