cmount: Vendor github.com/billziss-gh/cgofuse
This commit is contained in:
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* hellofs.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 (
|
||||
"os"
|
||||
|
||||
"github.com/billziss-gh/cgofuse/fuse"
|
||||
)
|
||||
|
||||
const (
|
||||
filename = "hello"
|
||||
contents = "hello, world\n"
|
||||
)
|
||||
|
||||
type Hellofs struct {
|
||||
fuse.FileSystemBase
|
||||
}
|
||||
|
||||
func (self *Hellofs) Open(path string, flags int) (errc int, fh uint64) {
|
||||
switch path {
|
||||
case "/" + filename:
|
||||
return 0, 0
|
||||
default:
|
||||
return -fuse.ENOENT, ^uint64(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Hellofs) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
|
||||
switch path {
|
||||
case "/":
|
||||
stat.Mode = fuse.S_IFDIR | 0555
|
||||
return 0
|
||||
case "/" + filename:
|
||||
stat.Mode = fuse.S_IFREG | 0444
|
||||
stat.Size = int64(len(contents))
|
||||
return 0
|
||||
default:
|
||||
return -fuse.ENOENT
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Hellofs) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||
endofst := ofst + int64(len(buff))
|
||||
if endofst > int64(len(contents)) {
|
||||
endofst = int64(len(contents))
|
||||
}
|
||||
if endofst < ofst {
|
||||
return 0
|
||||
}
|
||||
n = copy(buff, contents[ofst:endofst])
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Hellofs) Readdir(path string,
|
||||
fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
|
||||
ofst int64,
|
||||
fh uint64) (errc int) {
|
||||
fill(".", nil, 0)
|
||||
fill("..", nil, 0)
|
||||
fill(filename, nil, 0)
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
hellofs := &Hellofs{}
|
||||
host := fuse.NewFileSystemHost(hellofs)
|
||||
host.Mount("", os.Args[1:])
|
||||
}
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
* 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:])
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
// +build darwin linux
|
||||
|
||||
/*
|
||||
* passthrough.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"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"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 errno(err error) int {
|
||||
if nil != err {
|
||||
return -int(err.(syscall.Errno))
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
_host *fuse.FileSystemHost
|
||||
)
|
||||
|
||||
type Ptfs struct {
|
||||
fuse.FileSystemBase
|
||||
root string
|
||||
}
|
||||
|
||||
func (self *Ptfs) Init() {
|
||||
defer trace()()
|
||||
e := syscall.Chdir(self.root)
|
||||
if nil == e {
|
||||
self.root = "./"
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Ptfs) Statfs(path string, stat *fuse.Statfs_t) (errc int) {
|
||||
defer trace(path)(&errc, stat)
|
||||
path = filepath.Join(self.root, path)
|
||||
stgo := syscall.Statfs_t{}
|
||||
errc = errno(syscall.Statfs(path, &stgo))
|
||||
copyFusestatfsFromGostatfs(stat, &stgo)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Ptfs) Mknod(path string, mode uint32, dev uint64) (errc int) {
|
||||
defer trace(path, mode, dev)(&errc)
|
||||
defer setuidgid()()
|
||||
path = filepath.Join(self.root, path)
|
||||
return errno(syscall.Mknod(path, mode, int(dev)))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Mkdir(path string, mode uint32) (errc int) {
|
||||
defer trace(path, mode)(&errc)
|
||||
defer setuidgid()()
|
||||
path = filepath.Join(self.root, path)
|
||||
return errno(syscall.Mkdir(path, mode))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Unlink(path string) (errc int) {
|
||||
defer trace(path)(&errc)
|
||||
path = filepath.Join(self.root, path)
|
||||
return errno(syscall.Unlink(path))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Rmdir(path string) (errc int) {
|
||||
defer trace(path)(&errc)
|
||||
path = filepath.Join(self.root, path)
|
||||
return errno(syscall.Rmdir(path))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Link(oldpath string, newpath string) (errc int) {
|
||||
defer trace(oldpath, newpath)(&errc)
|
||||
defer setuidgid()()
|
||||
oldpath = filepath.Join(self.root, oldpath)
|
||||
newpath = filepath.Join(self.root, newpath)
|
||||
return errno(syscall.Link(oldpath, newpath))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Symlink(target string, newpath string) (errc int) {
|
||||
defer trace(target, newpath)(&errc)
|
||||
defer setuidgid()()
|
||||
newpath = filepath.Join(self.root, newpath)
|
||||
return errno(syscall.Symlink(target, newpath))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Readlink(path string) (errc int, target string) {
|
||||
defer trace(path)(&errc, &target)
|
||||
path = filepath.Join(self.root, path)
|
||||
buff := [1024]byte{}
|
||||
n, e := syscall.Readlink(path, buff[:])
|
||||
if nil != e {
|
||||
return errno(e), ""
|
||||
}
|
||||
return 0, string(buff[:n])
|
||||
}
|
||||
|
||||
func (self *Ptfs) Rename(oldpath string, newpath string) (errc int) {
|
||||
defer trace(oldpath, newpath)(&errc)
|
||||
defer setuidgid()()
|
||||
oldpath = filepath.Join(self.root, oldpath)
|
||||
newpath = filepath.Join(self.root, newpath)
|
||||
return errno(syscall.Rename(oldpath, newpath))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Chmod(path string, mode uint32) (errc int) {
|
||||
defer trace(path, mode)(&errc)
|
||||
path = filepath.Join(self.root, path)
|
||||
return errno(syscall.Chmod(path, mode))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Chown(path string, uid uint32, gid uint32) (errc int) {
|
||||
defer trace(path, uid, gid)(&errc)
|
||||
path = filepath.Join(self.root, path)
|
||||
return errno(syscall.Lchown(path, int(uid), int(gid)))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Utimens(path string, tmsp1 []fuse.Timespec) (errc int) {
|
||||
defer trace(path, tmsp1)(&errc)
|
||||
path = filepath.Join(self.root, path)
|
||||
tmsp := [2]syscall.Timespec{}
|
||||
tmsp[0].Sec, tmsp[0].Nsec = tmsp1[0].Sec, tmsp1[0].Nsec
|
||||
tmsp[1].Sec, tmsp[1].Nsec = tmsp1[1].Sec, tmsp1[1].Nsec
|
||||
return errno(syscall.UtimesNano(path, tmsp[:]))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Create(path string, flags int, mode uint32) (errc int, fh uint64) {
|
||||
defer trace(path, flags, mode)(&errc, &fh)
|
||||
defer setuidgid()()
|
||||
return self.open(path, flags, mode)
|
||||
}
|
||||
|
||||
func (self *Ptfs) Open(path string, flags int) (errc int, fh uint64) {
|
||||
defer trace(path, flags)(&errc, &fh)
|
||||
return self.open(path, flags, 0)
|
||||
}
|
||||
|
||||
func (self *Ptfs) open(path string, flags int, mode uint32) (errc int, fh uint64) {
|
||||
path = filepath.Join(self.root, path)
|
||||
f, e := syscall.Open(path, flags, mode)
|
||||
if nil != e {
|
||||
return errno(e), ^uint64(0)
|
||||
}
|
||||
return 0, uint64(f)
|
||||
}
|
||||
|
||||
func (self *Ptfs) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
|
||||
defer trace(path, fh)(&errc, stat)
|
||||
stgo := syscall.Stat_t{}
|
||||
if ^uint64(0) == fh {
|
||||
path = filepath.Join(self.root, path)
|
||||
errc = errno(syscall.Lstat(path, &stgo))
|
||||
} else {
|
||||
errc = errno(syscall.Fstat(int(fh), &stgo))
|
||||
}
|
||||
copyFusestatFromGostat(stat, &stgo)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Ptfs) Truncate(path string, size int64, fh uint64) (errc int) {
|
||||
defer trace(path, size, fh)(&errc)
|
||||
if ^uint64(0) == fh {
|
||||
path = filepath.Join(self.root, path)
|
||||
errc = errno(syscall.Truncate(path, size))
|
||||
} else {
|
||||
errc = errno(syscall.Ftruncate(int(fh), size))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Ptfs) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||
defer trace(path, buff, ofst, fh)(&n)
|
||||
n, e := syscall.Pread(int(fh), buff, ofst)
|
||||
if nil != e {
|
||||
return errno(e)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (self *Ptfs) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||
defer trace(path, buff, ofst, fh)(&n)
|
||||
n, e := syscall.Pwrite(int(fh), buff, ofst)
|
||||
if nil != e {
|
||||
return errno(e)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (self *Ptfs) Release(path string, fh uint64) (errc int) {
|
||||
defer trace(path, fh)(&errc)
|
||||
return errno(syscall.Close(int(fh)))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Fsync(path string, datasync bool, fh uint64) (errc int) {
|
||||
defer trace(path, datasync, fh)(&errc)
|
||||
return errno(syscall.Fsync(int(fh)))
|
||||
}
|
||||
|
||||
func (self *Ptfs) Opendir(path string) (errc int, fh uint64) {
|
||||
defer trace(path)(&errc, &fh)
|
||||
path = filepath.Join(self.root, path)
|
||||
f, e := syscall.Open(path, syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
|
||||
if nil != e {
|
||||
return errno(e), ^uint64(0)
|
||||
}
|
||||
return 0, uint64(f)
|
||||
}
|
||||
|
||||
func (self *Ptfs) 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)
|
||||
path = filepath.Join(self.root, path)
|
||||
file, e := os.Open(path)
|
||||
if nil != e {
|
||||
return errno(e)
|
||||
}
|
||||
defer file.Close()
|
||||
nams, e := file.Readdirnames(0)
|
||||
if nil != e {
|
||||
return errno(e)
|
||||
}
|
||||
nams = append([]string{".", ".."}, nams...)
|
||||
for _, name := range nams {
|
||||
if !fill(name, nil, 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *Ptfs) Releasedir(path string, fh uint64) (errc int) {
|
||||
defer trace(path, fh)(&errc)
|
||||
return errno(syscall.Close(int(fh)))
|
||||
}
|
||||
|
||||
func main() {
|
||||
syscall.Umask(0)
|
||||
ptfs := Ptfs{}
|
||||
args := os.Args
|
||||
if 3 <= len(args) && '-' != args[len(args)-2][0] && '-' != args[len(args)-1][0] {
|
||||
ptfs.root, _ = filepath.Abs(args[len(args)-2])
|
||||
args = append(args[:len(args)-2], args[len(args)-1])
|
||||
}
|
||||
_host = fuse.NewFileSystemHost(&ptfs)
|
||||
_host.Mount("", args[1:])
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// +build darwin
|
||||
|
||||
/*
|
||||
* struct_darwin.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 (
|
||||
"syscall"
|
||||
|
||||
"github.com/billziss-gh/cgofuse/fuse"
|
||||
)
|
||||
|
||||
func setuidgid() func() {
|
||||
euid := syscall.Geteuid()
|
||||
if 0 == euid {
|
||||
uid, gid, _ := fuse.Getcontext()
|
||||
egid := syscall.Getegid()
|
||||
syscall.Setegid(int(gid))
|
||||
syscall.Seteuid(int(uid))
|
||||
return func() {
|
||||
syscall.Seteuid(euid)
|
||||
syscall.Setegid(egid)
|
||||
}
|
||||
}
|
||||
return func() {
|
||||
}
|
||||
}
|
||||
|
||||
func copyFusestatfsFromGostatfs(dst *fuse.Statfs_t, src *syscall.Statfs_t) {
|
||||
*dst = fuse.Statfs_t{}
|
||||
dst.Bsize = uint64(src.Bsize)
|
||||
dst.Frsize = 1
|
||||
dst.Blocks = uint64(src.Blocks)
|
||||
dst.Bfree = uint64(src.Bfree)
|
||||
dst.Bavail = uint64(src.Bavail)
|
||||
dst.Files = uint64(src.Files)
|
||||
dst.Ffree = uint64(src.Ffree)
|
||||
dst.Favail = uint64(src.Ffree)
|
||||
dst.Namemax = 255 //uint64(src.Namelen)
|
||||
}
|
||||
|
||||
func copyFusestatFromGostat(dst *fuse.Stat_t, src *syscall.Stat_t) {
|
||||
*dst = fuse.Stat_t{}
|
||||
dst.Dev = uint64(src.Dev)
|
||||
dst.Ino = uint64(src.Ino)
|
||||
dst.Mode = uint32(src.Mode)
|
||||
dst.Nlink = uint32(src.Nlink)
|
||||
dst.Uid = uint32(src.Uid)
|
||||
dst.Gid = uint32(src.Gid)
|
||||
dst.Rdev = uint64(src.Rdev)
|
||||
dst.Size = int64(src.Size)
|
||||
dst.Atim.Sec, dst.Atim.Nsec = src.Atimespec.Sec, src.Atimespec.Nsec
|
||||
dst.Mtim.Sec, dst.Mtim.Nsec = src.Mtimespec.Sec, src.Mtimespec.Nsec
|
||||
dst.Ctim.Sec, dst.Ctim.Nsec = src.Ctimespec.Sec, src.Ctimespec.Nsec
|
||||
dst.Blksize = int64(src.Blksize)
|
||||
dst.Blocks = int64(src.Blocks)
|
||||
dst.Birthtim.Sec, dst.Birthtim.Nsec = src.Birthtimespec.Sec, src.Birthtimespec.Nsec
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
* struct_linux.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 (
|
||||
"syscall"
|
||||
|
||||
"github.com/billziss-gh/cgofuse/fuse"
|
||||
)
|
||||
|
||||
func setuidgid() func() {
|
||||
euid := syscall.Geteuid()
|
||||
if 0 == euid {
|
||||
uid, gid, _ := fuse.Getcontext()
|
||||
egid := syscall.Getegid()
|
||||
syscall.Setregid(-1, int(gid))
|
||||
syscall.Setreuid(-1, int(uid))
|
||||
return func() {
|
||||
syscall.Setreuid(-1, int(euid))
|
||||
syscall.Setregid(-1, int(egid))
|
||||
}
|
||||
}
|
||||
return func() {
|
||||
}
|
||||
}
|
||||
|
||||
func copyFusestatfsFromGostatfs(dst *fuse.Statfs_t, src *syscall.Statfs_t) {
|
||||
*dst = fuse.Statfs_t{}
|
||||
dst.Bsize = uint64(src.Bsize)
|
||||
dst.Frsize = 1
|
||||
dst.Blocks = uint64(src.Blocks)
|
||||
dst.Bfree = uint64(src.Bfree)
|
||||
dst.Bavail = uint64(src.Bavail)
|
||||
dst.Files = uint64(src.Files)
|
||||
dst.Ffree = uint64(src.Ffree)
|
||||
dst.Favail = uint64(src.Ffree)
|
||||
dst.Namemax = 255 //uint64(src.Namelen)
|
||||
}
|
||||
|
||||
func copyFusestatFromGostat(dst *fuse.Stat_t, src *syscall.Stat_t) {
|
||||
*dst = fuse.Stat_t{}
|
||||
dst.Dev = uint64(src.Dev)
|
||||
dst.Ino = uint64(src.Ino)
|
||||
dst.Mode = uint32(src.Mode)
|
||||
dst.Nlink = uint32(src.Nlink)
|
||||
dst.Uid = uint32(src.Uid)
|
||||
dst.Gid = uint32(src.Gid)
|
||||
dst.Rdev = uint64(src.Rdev)
|
||||
dst.Size = int64(src.Size)
|
||||
dst.Atim.Sec, dst.Atim.Nsec = src.Atim.Sec, src.Atim.Nsec
|
||||
dst.Mtim.Sec, dst.Mtim.Nsec = src.Mtim.Sec, src.Mtim.Nsec
|
||||
dst.Ctim.Sec, dst.Ctim.Nsec = src.Ctim.Sec, src.Ctim.Nsec
|
||||
dst.Blksize = int64(src.Blksize)
|
||||
dst.Blocks = int64(src.Blocks)
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* trace.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 shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
TracePattern = os.Getenv("CGOFUSE_TRACE")
|
||||
)
|
||||
|
||||
func traceJoin(deref bool, vals []interface{}) string {
|
||||
rslt := ""
|
||||
for _, v := range vals {
|
||||
if deref {
|
||||
switch i := v.(type) {
|
||||
case *bool:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *int:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *int8:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *int16:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *int32:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *int64:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *uint:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *uint8:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *uint16:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *uint32:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *uint64:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *uintptr:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *float32:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *float64:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *complex64:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *complex128:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
case *string:
|
||||
rslt += fmt.Sprintf(", %#v", *i)
|
||||
default:
|
||||
rslt += fmt.Sprintf(", %#v", v)
|
||||
}
|
||||
} else {
|
||||
rslt += fmt.Sprintf(", %#v", v)
|
||||
}
|
||||
}
|
||||
if len(rslt) > 0 {
|
||||
rslt = rslt[2:]
|
||||
}
|
||||
return rslt
|
||||
}
|
||||
|
||||
func Trace(skip int, prfx string, vals ...interface{}) func(vals ...interface{}) {
|
||||
if "" == TracePattern {
|
||||
return func(vals ...interface{}) {
|
||||
}
|
||||
}
|
||||
pc, _, _, ok := runtime.Caller(skip + 1)
|
||||
name := "<UNKNOWN>"
|
||||
if ok {
|
||||
fn := runtime.FuncForPC(pc)
|
||||
name = fn.Name()
|
||||
if m, _ := filepath.Match(TracePattern, name); !m {
|
||||
return func(vals ...interface{}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if "" != prfx {
|
||||
prfx = prfx + ": "
|
||||
}
|
||||
args := traceJoin(false, vals)
|
||||
return func(vals ...interface{}) {
|
||||
form := "%v%v(%v) = %v"
|
||||
rslt := ""
|
||||
rcvr := recover()
|
||||
if nil != rcvr {
|
||||
rslt = fmt.Sprintf("!PANIC:%v", rcvr)
|
||||
} else {
|
||||
if len(vals) != 1 {
|
||||
form = "%v%v(%v) = (%v)"
|
||||
}
|
||||
rslt = traceJoin(true, vals)
|
||||
}
|
||||
log.Printf(form, prfx, name, args, rslt)
|
||||
if nil != rcvr {
|
||||
panic(rcvr)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user