rclone/vendor/github.com/billziss-gh/cgofuse/examples/memfs/memfs.go

546 lines
12 KiB
Go
Raw Normal View History

/*
* memfs.go
*
* Copyright 2017 Bill Zissimopoulos
*/
/*
* This file is part of Cgofuse.
*
* It is licensed under the MIT license. The full license text can be found
* in the License.txt file at the root of this project.
*/
package main
import (
"fmt"
"os"
"strings"
"sync"
"github.com/billziss-gh/cgofuse/examples/shared"
"github.com/billziss-gh/cgofuse/fuse"
)
func trace(vals ...interface{}) func(vals ...interface{}) {
uid, gid, _ := fuse.Getcontext()
return shared.Trace(1, fmt.Sprintf("[uid=%v,gid=%v]", uid, gid), vals...)
}
func split(path string) []string {
return strings.Split(path, "/")
}
func resize(slice []byte, size int64, zeroinit bool) []byte {
const allocunit = 64 * 1024
allocsize := (size + allocunit - 1) / allocunit * allocunit
if cap(slice) != int(allocsize) {
var newslice []byte
{
defer func() {
if r := recover(); nil != r {
panic(fuse.Error(-fuse.ENOSPC))
}
}()
newslice = make([]byte, size, allocsize)
}
copy(newslice, slice)
slice = newslice
} else if zeroinit {
i := len(slice)
slice = slice[:size]
for ; len(slice) > i; i++ {
slice[i] = 0
}
}
return slice
}
type node_t struct {
stat fuse.Stat_t
xatr map[string][]byte
chld map[string]*node_t
data []byte
opencnt int
}
func newNode(dev uint64, ino uint64, mode uint32, uid uint32, gid uint32) *node_t {
tmsp := fuse.Now()
self := node_t{
fuse.Stat_t{
Dev: dev,
Ino: ino,
Mode: mode,
Nlink: 1,
Uid: uid,
Gid: gid,
Atim: tmsp,
Mtim: tmsp,
Ctim: tmsp,
Birthtim: tmsp,
},
nil,
nil,
nil,
0}
if fuse.S_IFDIR == self.stat.Mode&fuse.S_IFMT {
self.chld = map[string]*node_t{}
}
return &self
}
type Memfs struct {
fuse.FileSystemBase
lock sync.Mutex
ino uint64
root *node_t
openmap map[uint64]*node_t
}
func (self *Memfs) Mknod(path string, mode uint32, dev uint64) (errc int) {
defer trace(path, mode, dev)(&errc)
defer self.synchronize()()
return self.makeNode(path, mode, dev, nil)
}
func (self *Memfs) Mkdir(path string, mode uint32) (errc int) {
defer trace(path, mode)(&errc)
defer self.synchronize()()
return self.makeNode(path, fuse.S_IFDIR|(mode&07777), 0, nil)
}
func (self *Memfs) Unlink(path string) (errc int) {
defer trace(path)(&errc)
defer self.synchronize()()
return self.removeNode(path, false)
}
func (self *Memfs) Rmdir(path string) (errc int) {
defer trace(path)(&errc)
defer self.synchronize()()
return self.removeNode(path, true)
}
func (self *Memfs) Link(oldpath string, newpath string) (errc int) {
defer trace(oldpath, newpath)(&errc)
defer self.synchronize()()
_, _, oldnode := self.lookupNode(oldpath, nil)
if nil == oldnode {
return -fuse.ENOENT
}
newprnt, newname, newnode := self.lookupNode(newpath, nil)
if nil == newprnt {
return -fuse.ENOENT
}
if nil != newnode {
return -fuse.EEXIST
}
oldnode.stat.Nlink++
newprnt.chld[newname] = oldnode
tmsp := fuse.Now()
oldnode.stat.Ctim = tmsp
newprnt.stat.Ctim = tmsp
newprnt.stat.Mtim = tmsp
return 0
}
func (self *Memfs) Symlink(target string, newpath string) (errc int) {
defer trace(target, newpath)(&errc)
defer self.synchronize()()
return self.makeNode(newpath, fuse.S_IFLNK|00777, 0, []byte(target))
}
func (self *Memfs) Readlink(path string) (errc int, target string) {
defer trace(path)(&errc, &target)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT, ""
}
if fuse.S_IFLNK != node.stat.Mode&fuse.S_IFMT {
return -fuse.EINVAL, ""
}
return 0, string(node.data)
}
func (self *Memfs) Rename(oldpath string, newpath string) (errc int) {
defer trace(oldpath, newpath)(&errc)
defer self.synchronize()()
oldprnt, oldname, oldnode := self.lookupNode(oldpath, nil)
if nil == oldnode {
return -fuse.ENOENT
}
newprnt, newname, newnode := self.lookupNode(newpath, oldnode)
if nil == newprnt {
return -fuse.ENOENT
}
if "" == newname {
// guard against directory loop creation
return -fuse.EINVAL
}
if oldprnt == newprnt && oldname == newname {
return 0
}
if nil != newnode {
errc = self.removeNode(newpath, fuse.S_IFDIR == oldnode.stat.Mode&fuse.S_IFMT)
if 0 != errc {
return errc
}
}
delete(oldprnt.chld, oldname)
newprnt.chld[newname] = oldnode
return 0
}
func (self *Memfs) Chmod(path string, mode uint32) (errc int) {
defer trace(path, mode)(&errc)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT
}
node.stat.Mode = (node.stat.Mode & fuse.S_IFMT) | mode&07777
node.stat.Ctim = fuse.Now()
return 0
}
func (self *Memfs) Chown(path string, uid uint32, gid uint32) (errc int) {
defer trace(path, uid, gid)(&errc)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT
}
if ^uint32(0) != uid {
node.stat.Uid = uid
}
if ^uint32(0) != gid {
node.stat.Gid = gid
}
node.stat.Ctim = fuse.Now()
return 0
}
func (self *Memfs) Utimens(path string, tmsp []fuse.Timespec) (errc int) {
defer trace(path, tmsp)(&errc)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT
}
if nil == tmsp {
tmsp0 := fuse.Now()
tmsa := [2]fuse.Timespec{tmsp0, tmsp0}
tmsp = tmsa[:]
}
node.stat.Atim = tmsp[0]
node.stat.Mtim = tmsp[1]
return 0
}
func (self *Memfs) Open(path string, flags int) (errc int, fh uint64) {
defer trace(path, flags)(&errc, &fh)
defer self.synchronize()()
return self.openNode(path, false)
}
func (self *Memfs) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
defer trace(path, fh)(&errc, stat)
defer self.synchronize()()
node := self.getNode(path, fh)
if nil == node {
return -fuse.ENOENT
}
*stat = node.stat
return 0
}
func (self *Memfs) Truncate(path string, size int64, fh uint64) (errc int) {
defer trace(path, size, fh)(&errc)
defer self.synchronize()()
node := self.getNode(path, fh)
if nil == node {
return -fuse.ENOENT
}
node.data = resize(node.data, size, true)
node.stat.Size = size
tmsp := fuse.Now()
node.stat.Ctim = tmsp
node.stat.Mtim = tmsp
return 0
}
func (self *Memfs) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
defer trace(path, buff, ofst, fh)(&n)
defer self.synchronize()()
node := self.getNode(path, fh)
if nil == node {
return -fuse.ENOENT
}
endofst := ofst + int64(len(buff))
if endofst > node.stat.Size {
endofst = node.stat.Size
}
if endofst < ofst {
return 0
}
n = copy(buff, node.data[ofst:endofst])
node.stat.Atim = fuse.Now()
return
}
func (self *Memfs) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
defer trace(path, buff, ofst, fh)(&n)
defer self.synchronize()()
node := self.getNode(path, fh)
if nil == node {
return -fuse.ENOENT
}
endofst := ofst + int64(len(buff))
if endofst > node.stat.Size {
node.data = resize(node.data, endofst, true)
node.stat.Size = endofst
}
n = copy(node.data[ofst:endofst], buff)
tmsp := fuse.Now()
node.stat.Ctim = tmsp
node.stat.Mtim = tmsp
return
}
func (self *Memfs) Release(path string, fh uint64) (errc int) {
defer trace(path, fh)(&errc)
defer self.synchronize()()
return self.closeNode(fh)
}
func (self *Memfs) Opendir(path string) (errc int, fh uint64) {
defer trace(path)(&errc, &fh)
defer self.synchronize()()
return self.openNode(path, true)
}
func (self *Memfs) Readdir(path string,
fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
ofst int64,
fh uint64) (errc int) {
defer trace(path, fill, ofst, fh)(&errc)
defer self.synchronize()()
node := self.openmap[fh]
fill(".", &node.stat, 0)
fill("..", nil, 0)
for name, chld := range node.chld {
if !fill(name, &chld.stat, 0) {
break
}
}
return 0
}
func (self *Memfs) Releasedir(path string, fh uint64) (errc int) {
defer trace(path, fh)(&errc)
defer self.synchronize()()
return self.closeNode(fh)
}
func (self *Memfs) Setxattr(path string, name string, value []byte, flags int) (errc int) {
defer trace(path, name, value, flags)(&errc)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT
}
if "com.apple.ResourceFork" == name {
return -fuse.ENOTSUP
}
if fuse.XATTR_CREATE == flags {
if _, ok := node.xatr[name]; ok {
return -fuse.EEXIST
}
} else if fuse.XATTR_REPLACE == flags {
if _, ok := node.xatr[name]; !ok {
return -fuse.ENOATTR
}
}
xatr := make([]byte, len(value))
copy(xatr, value)
if nil == node.xatr {
node.xatr = map[string][]byte{}
}
node.xatr[name] = xatr
return 0
}
func (self *Memfs) Getxattr(path string, name string) (errc int, xatr []byte) {
defer trace(path, name)(&errc, &xatr)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT, nil
}
if "com.apple.ResourceFork" == name {
return -fuse.ENOTSUP, nil
}
xatr, ok := node.xatr[name]
if !ok {
return -fuse.ENOATTR, nil
}
return 0, xatr
}
func (self *Memfs) Removexattr(path string, name string) (errc int) {
defer trace(path, name)(&errc)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT
}
if "com.apple.ResourceFork" == name {
return -fuse.ENOTSUP
}
if _, ok := node.xatr[name]; !ok {
return -fuse.ENOATTR
}
delete(node.xatr, name)
return 0
}
func (self *Memfs) Listxattr(path string, fill func(name string) bool) (errc int) {
defer trace(path, fill)(&errc)
defer self.synchronize()()
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT
}
for name := range node.xatr {
if !fill(name) {
return -fuse.ERANGE
}
}
return 0
}
func (self *Memfs) lookupNode(path string, ancestor *node_t) (prnt *node_t, name string, node *node_t) {
prnt = self.root
name = ""
node = self.root
for _, c := range split(path) {
if "" != c {
if 255 < len(c) {
panic(fuse.Error(-fuse.ENAMETOOLONG))
}
prnt, name = node, c
node = node.chld[c]
if nil != ancestor && node == ancestor {
name = "" // special case loop condition
return
}
}
}
return
}
func (self *Memfs) makeNode(path string, mode uint32, dev uint64, data []byte) int {
prnt, name, node := self.lookupNode(path, nil)
if nil == prnt {
return -fuse.ENOENT
}
if nil != node {
return -fuse.EEXIST
}
self.ino++
uid, gid, _ := fuse.Getcontext()
node = newNode(dev, self.ino, mode, uid, gid)
if nil != data {
node.data = make([]byte, len(data))
node.stat.Size = int64(len(data))
copy(node.data, data)
}
prnt.chld[name] = node
prnt.stat.Ctim = node.stat.Ctim
prnt.stat.Mtim = node.stat.Ctim
return 0
}
func (self *Memfs) removeNode(path string, dir bool) int {
prnt, name, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT
}
if !dir && fuse.S_IFDIR == node.stat.Mode&fuse.S_IFMT {
return -fuse.EISDIR
}
if dir && fuse.S_IFDIR != node.stat.Mode&fuse.S_IFMT {
return -fuse.ENOTDIR
}
if 0 < len(node.chld) {
return -fuse.ENOTEMPTY
}
node.stat.Nlink--
delete(prnt.chld, name)
tmsp := fuse.Now()
node.stat.Ctim = tmsp
prnt.stat.Ctim = tmsp
prnt.stat.Mtim = tmsp
return 0
}
func (self *Memfs) openNode(path string, dir bool) (int, uint64) {
_, _, node := self.lookupNode(path, nil)
if nil == node {
return -fuse.ENOENT, ^uint64(0)
}
if !dir && fuse.S_IFDIR == node.stat.Mode&fuse.S_IFMT {
return -fuse.EISDIR, ^uint64(0)
}
if dir && fuse.S_IFDIR != node.stat.Mode&fuse.S_IFMT {
return -fuse.ENOTDIR, ^uint64(0)
}
node.opencnt++
if 1 == node.opencnt {
self.openmap[node.stat.Ino] = node
}
return 0, node.stat.Ino
}
func (self *Memfs) closeNode(fh uint64) int {
node := self.openmap[fh]
node.opencnt--
if 0 == node.opencnt {
delete(self.openmap, node.stat.Ino)
}
return 0
}
func (self *Memfs) getNode(path string, fh uint64) *node_t {
if ^uint64(0) == fh {
_, _, node := self.lookupNode(path, nil)
return node
} else {
return self.openmap[fh]
}
}
func (self *Memfs) synchronize() func() {
self.lock.Lock()
return func() {
self.lock.Unlock()
}
}
func NewMemfs() *Memfs {
self := Memfs{}
defer self.synchronize()()
self.ino++
self.root = newNode(0, self.ino, fuse.S_IFDIR|00777, 0, 0)
self.openmap = map[uint64]*node_t{}
return &self
}
func main() {
memfs := NewMemfs()
host := fuse.NewFileSystemHost(memfs)
host.SetCapReaddirPlus(true)
host.Mount("", os.Args[1:])
}