mirror of
https://github.com/rclone/rclone.git
synced 2024-12-26 00:49:22 +01:00
932 lines
26 KiB
Go
932 lines
26 KiB
Go
/*
|
|
* host.go
|
|
*
|
|
* Copyright 2017-2018 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 fuse
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// FileSystemHost is used to host a file system.
|
|
type FileSystemHost struct {
|
|
fsop FileSystemInterface
|
|
fuse *c_struct_fuse
|
|
mntp string
|
|
sigc chan os.Signal
|
|
|
|
capCaseInsensitive, capReaddirPlus bool
|
|
}
|
|
|
|
var (
|
|
hostGuard = sync.Mutex{}
|
|
hostTable = map[unsafe.Pointer]*FileSystemHost{}
|
|
)
|
|
|
|
func hostHandleNew(host *FileSystemHost) unsafe.Pointer {
|
|
p := c_malloc(1)
|
|
hostGuard.Lock()
|
|
hostTable[p] = host
|
|
hostGuard.Unlock()
|
|
return p
|
|
}
|
|
|
|
func hostHandleDel(p unsafe.Pointer) {
|
|
hostGuard.Lock()
|
|
delete(hostTable, p)
|
|
hostGuard.Unlock()
|
|
c_free(p)
|
|
}
|
|
|
|
func hostHandleGet(p unsafe.Pointer) *FileSystemHost {
|
|
hostGuard.Lock()
|
|
host, _ := hostTable[p]
|
|
hostGuard.Unlock()
|
|
return host
|
|
}
|
|
|
|
func copyCstatvfsFromFusestatfs(dst *c_fuse_statvfs_t, src *Statfs_t) {
|
|
c_hostCstatvfsFromFusestatfs(dst,
|
|
c_uint64_t(src.Bsize),
|
|
c_uint64_t(src.Frsize),
|
|
c_uint64_t(src.Blocks),
|
|
c_uint64_t(src.Bfree),
|
|
c_uint64_t(src.Bavail),
|
|
c_uint64_t(src.Files),
|
|
c_uint64_t(src.Ffree),
|
|
c_uint64_t(src.Favail),
|
|
c_uint64_t(src.Fsid),
|
|
c_uint64_t(src.Flag),
|
|
c_uint64_t(src.Namemax))
|
|
}
|
|
|
|
func copyCstatFromFusestat(dst *c_fuse_stat_t, src *Stat_t) {
|
|
c_hostCstatFromFusestat(dst,
|
|
c_uint64_t(src.Dev),
|
|
c_uint64_t(src.Ino),
|
|
c_uint32_t(src.Mode),
|
|
c_uint32_t(src.Nlink),
|
|
c_uint32_t(src.Uid),
|
|
c_uint32_t(src.Gid),
|
|
c_uint64_t(src.Rdev),
|
|
c_int64_t(src.Size),
|
|
c_int64_t(src.Atim.Sec), c_int64_t(src.Atim.Nsec),
|
|
c_int64_t(src.Mtim.Sec), c_int64_t(src.Mtim.Nsec),
|
|
c_int64_t(src.Ctim.Sec), c_int64_t(src.Ctim.Nsec),
|
|
c_int64_t(src.Blksize),
|
|
c_int64_t(src.Blocks),
|
|
c_int64_t(src.Birthtim.Sec), c_int64_t(src.Birthtim.Nsec),
|
|
c_uint32_t(src.Flags))
|
|
}
|
|
|
|
func copyFusetimespecFromCtimespec(dst *Timespec, src *c_fuse_timespec_t) {
|
|
dst.Sec = int64(src.tv_sec)
|
|
dst.Nsec = int64(src.tv_nsec)
|
|
}
|
|
|
|
func recoverAsErrno(errc0 *c_int) {
|
|
if r := recover(); nil != r {
|
|
switch e := r.(type) {
|
|
case Error:
|
|
*errc0 = c_int(e)
|
|
default:
|
|
*errc0 = -c_int(EIO)
|
|
}
|
|
}
|
|
}
|
|
|
|
func hostGetattr(path0 *c_char, stat0 *c_fuse_stat_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
stat := &Stat_t{}
|
|
errc := fsop.Getattr(path, stat, ^uint64(0))
|
|
copyCstatFromFusestat(stat0, stat)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostReadlink(path0 *c_char, buff0 *c_char, size0 c_size_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc, rslt := fsop.Readlink(path)
|
|
buff := (*[1 << 30]byte)(unsafe.Pointer(buff0))
|
|
copy(buff[:size0-1], rslt)
|
|
rlen := len(rslt)
|
|
if c_size_t(rlen) < size0 {
|
|
buff[rlen] = 0
|
|
}
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostMknod(path0 *c_char, mode0 c_fuse_mode_t, dev0 c_fuse_dev_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Mknod(path, uint32(mode0), uint64(dev0))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostMkdir(path0 *c_char, mode0 c_fuse_mode_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Mkdir(path, uint32(mode0))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostUnlink(path0 *c_char) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Unlink(path)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostRmdir(path0 *c_char) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Rmdir(path)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostSymlink(target0 *c_char, newpath0 *c_char) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
target, newpath := c_GoString(target0), c_GoString(newpath0)
|
|
errc := fsop.Symlink(target, newpath)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostRename(oldpath0 *c_char, newpath0 *c_char) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
oldpath, newpath := c_GoString(oldpath0), c_GoString(newpath0)
|
|
errc := fsop.Rename(oldpath, newpath)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostLink(oldpath0 *c_char, newpath0 *c_char) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
oldpath, newpath := c_GoString(oldpath0), c_GoString(newpath0)
|
|
errc := fsop.Link(oldpath, newpath)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostChmod(path0 *c_char, mode0 c_fuse_mode_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Chmod(path, uint32(mode0))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostChown(path0 *c_char, uid0 c_fuse_uid_t, gid0 c_fuse_gid_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Chown(path, uint32(uid0), uint32(gid0))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostTruncate(path0 *c_char, size0 c_fuse_off_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Truncate(path, int64(size0), ^uint64(0))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostOpen(path0 *c_char, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc, rslt := fsop.Open(path, int(fi0.flags))
|
|
fi0.fh = c_uint64_t(rslt)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostRead(path0 *c_char, buff0 *c_char, size0 c_size_t, ofst0 c_fuse_off_t,
|
|
fi0 *c_struct_fuse_file_info) (nbyt0 c_int) {
|
|
defer recoverAsErrno(&nbyt0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
buff := (*[1 << 30]byte)(unsafe.Pointer(buff0))
|
|
nbyt := fsop.Read(path, buff[:size0], int64(ofst0), uint64(fi0.fh))
|
|
return c_int(nbyt)
|
|
}
|
|
|
|
func hostWrite(path0 *c_char, buff0 *c_char, size0 c_size_t, ofst0 c_fuse_off_t,
|
|
fi0 *c_struct_fuse_file_info) (nbyt0 c_int) {
|
|
defer recoverAsErrno(&nbyt0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
buff := (*[1 << 30]byte)(unsafe.Pointer(buff0))
|
|
nbyt := fsop.Write(path, buff[:size0], int64(ofst0), uint64(fi0.fh))
|
|
return c_int(nbyt)
|
|
}
|
|
|
|
func hostStatfs(path0 *c_char, stat0 *c_fuse_statvfs_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
stat := &Statfs_t{}
|
|
errc := fsop.Statfs(path, stat)
|
|
if -ENOSYS == errc {
|
|
stat = &Statfs_t{}
|
|
errc = 0
|
|
}
|
|
copyCstatvfsFromFusestatfs(stat0, stat)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostFlush(path0 *c_char, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Flush(path, uint64(fi0.fh))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostRelease(path0 *c_char, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Release(path, uint64(fi0.fh))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostFsync(path0 *c_char, datasync c_int, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Fsync(path, 0 != datasync, uint64(fi0.fh))
|
|
if -ENOSYS == errc {
|
|
errc = 0
|
|
}
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostSetxattr(path0 *c_char, name0 *c_char, buff0 *c_char, size0 c_size_t,
|
|
flags c_int) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
name := c_GoString(name0)
|
|
buff := (*[1 << 30]byte)(unsafe.Pointer(buff0))
|
|
errc := fsop.Setxattr(path, name, buff[:size0], int(flags))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostGetxattr(path0 *c_char, name0 *c_char, buff0 *c_char, size0 c_size_t) (nbyt0 c_int) {
|
|
defer recoverAsErrno(&nbyt0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
name := c_GoString(name0)
|
|
errc, rslt := fsop.Getxattr(path, name)
|
|
if 0 != errc {
|
|
return c_int(errc)
|
|
}
|
|
if 0 != size0 {
|
|
if len(rslt) > int(size0) {
|
|
return -c_int(ERANGE)
|
|
}
|
|
buff := (*[1 << 30]byte)(unsafe.Pointer(buff0))
|
|
copy(buff[:size0], rslt)
|
|
}
|
|
return c_int(len(rslt))
|
|
}
|
|
|
|
func hostListxattr(path0 *c_char, buff0 *c_char, size0 c_size_t) (nbyt0 c_int) {
|
|
defer recoverAsErrno(&nbyt0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
buff := (*[1 << 30]byte)(unsafe.Pointer(buff0))
|
|
size := int(size0)
|
|
nbyt := 0
|
|
fill := func(name1 string) bool {
|
|
nlen := len(name1)
|
|
if 0 != size {
|
|
if nbyt+nlen+1 > size {
|
|
return false
|
|
}
|
|
copy(buff[nbyt:nbyt+nlen], name1)
|
|
buff[nbyt+nlen] = 0
|
|
}
|
|
nbyt += nlen + 1
|
|
return true
|
|
}
|
|
errc := fsop.Listxattr(path, fill)
|
|
if 0 != errc {
|
|
return c_int(errc)
|
|
}
|
|
return c_int(nbyt)
|
|
}
|
|
|
|
func hostRemovexattr(path0 *c_char, name0 *c_char) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
name := c_GoString(name0)
|
|
errc := fsop.Removexattr(path, name)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostOpendir(path0 *c_char, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc, rslt := fsop.Opendir(path)
|
|
if -ENOSYS == errc {
|
|
errc = 0
|
|
}
|
|
fi0.fh = c_uint64_t(rslt)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostReaddir(path0 *c_char, buff0 unsafe.Pointer, fill0 c_fuse_fill_dir_t, ofst0 c_fuse_off_t,
|
|
fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
fill := func(name1 string, stat1 *Stat_t, off1 int64) bool {
|
|
name := c_CString(name1)
|
|
defer c_free(unsafe.Pointer(name))
|
|
if nil == stat1 {
|
|
return 0 == c_hostFilldir(fill0, buff0, name, nil, c_fuse_off_t(off1))
|
|
} else {
|
|
stat_ex := c_fuse_stat_ex_t{} // support WinFsp fuse_stat_ex
|
|
stat := (*c_fuse_stat_t)(unsafe.Pointer(&stat_ex))
|
|
copyCstatFromFusestat(stat, stat1)
|
|
return 0 == c_hostFilldir(fill0, buff0, name, stat, c_fuse_off_t(off1))
|
|
}
|
|
}
|
|
errc := fsop.Readdir(path, fill, int64(ofst0), uint64(fi0.fh))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostReleasedir(path0 *c_char, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Releasedir(path, uint64(fi0.fh))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostFsyncdir(path0 *c_char, datasync c_int, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Fsyncdir(path, 0 != datasync, uint64(fi0.fh))
|
|
if -ENOSYS == errc {
|
|
errc = 0
|
|
}
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostInit(conn0 *c_struct_fuse_conn_info) (user_data unsafe.Pointer) {
|
|
defer recover()
|
|
fctx := c_fuse_get_context()
|
|
user_data = fctx.private_data
|
|
host := hostHandleGet(user_data)
|
|
host.fuse = fctx.fuse
|
|
c_hostAsgnCconninfo(conn0,
|
|
c_bool(host.capCaseInsensitive),
|
|
c_bool(host.capReaddirPlus))
|
|
if nil != host.sigc {
|
|
signal.Notify(host.sigc, syscall.SIGINT, syscall.SIGTERM)
|
|
}
|
|
host.fsop.Init()
|
|
return
|
|
}
|
|
|
|
func hostDestroy(user_data unsafe.Pointer) {
|
|
defer recover()
|
|
if "netbsd" == runtime.GOOS {
|
|
user_data = c_fuse_get_context().private_data
|
|
}
|
|
host := hostHandleGet(user_data)
|
|
host.fsop.Destroy()
|
|
if nil != host.sigc {
|
|
signal.Stop(host.sigc)
|
|
}
|
|
host.fuse = nil
|
|
}
|
|
|
|
func hostAccess(path0 *c_char, mask0 c_int) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Access(path, uint32(mask0))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostCreate(path0 *c_char, mode0 c_fuse_mode_t, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc, rslt := fsop.Create(path, int(fi0.flags), uint32(mode0))
|
|
if -ENOSYS == errc {
|
|
errc = fsop.Mknod(path, S_IFREG|uint32(mode0), 0)
|
|
if 0 == errc {
|
|
errc, rslt = fsop.Open(path, int(fi0.flags))
|
|
}
|
|
}
|
|
fi0.fh = c_uint64_t(rslt)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostFtruncate(path0 *c_char, size0 c_fuse_off_t, fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
errc := fsop.Truncate(path, int64(size0), uint64(fi0.fh))
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostFgetattr(path0 *c_char, stat0 *c_fuse_stat_t,
|
|
fi0 *c_struct_fuse_file_info) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
stat := &Stat_t{}
|
|
errc := fsop.Getattr(path, stat, uint64(fi0.fh))
|
|
copyCstatFromFusestat(stat0, stat)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostUtimens(path0 *c_char, tmsp0 *c_fuse_timespec_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
path := c_GoString(path0)
|
|
if nil == tmsp0 {
|
|
errc := fsop.Utimens(path, nil)
|
|
return c_int(errc)
|
|
} else {
|
|
tmsp := [2]Timespec{}
|
|
tmsa := (*[2]c_fuse_timespec_t)(unsafe.Pointer(tmsp0))
|
|
copyFusetimespecFromCtimespec(&tmsp[0], &tmsa[0])
|
|
copyFusetimespecFromCtimespec(&tmsp[1], &tmsa[1])
|
|
errc := fsop.Utimens(path, tmsp[:])
|
|
return c_int(errc)
|
|
}
|
|
}
|
|
|
|
func hostSetchgtime(path0 *c_char, tmsp0 *c_fuse_timespec_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
intf, ok := fsop.(FileSystemSetchgtime)
|
|
if !ok {
|
|
// say we did it!
|
|
return 0
|
|
}
|
|
path := c_GoString(path0)
|
|
tmsp := Timespec{}
|
|
copyFusetimespecFromCtimespec(&tmsp, tmsp0)
|
|
errc := intf.Setchgtime(path, tmsp)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostSetcrtime(path0 *c_char, tmsp0 *c_fuse_timespec_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
intf, ok := fsop.(FileSystemSetcrtime)
|
|
if !ok {
|
|
// say we did it!
|
|
return 0
|
|
}
|
|
path := c_GoString(path0)
|
|
tmsp := Timespec{}
|
|
copyFusetimespecFromCtimespec(&tmsp, tmsp0)
|
|
errc := intf.Setcrtime(path, tmsp)
|
|
return c_int(errc)
|
|
}
|
|
|
|
func hostChflags(path0 *c_char, flags c_uint32_t) (errc0 c_int) {
|
|
defer recoverAsErrno(&errc0)
|
|
fsop := hostHandleGet(c_fuse_get_context().private_data).fsop
|
|
intf, ok := fsop.(FileSystemChflags)
|
|
if !ok {
|
|
// say we did it!
|
|
return 0
|
|
}
|
|
path := c_GoString(path0)
|
|
errc := intf.Chflags(path, uint32(flags))
|
|
return c_int(errc)
|
|
}
|
|
|
|
// NewFileSystemHost creates a file system host.
|
|
func NewFileSystemHost(fsop FileSystemInterface) *FileSystemHost {
|
|
host := &FileSystemHost{}
|
|
host.fsop = fsop
|
|
return host
|
|
}
|
|
|
|
// SetCapCaseInsensitive informs the host that the hosted file system is case insensitive
|
|
// [OSX and Windows only].
|
|
func (host *FileSystemHost) SetCapCaseInsensitive(value bool) {
|
|
host.capCaseInsensitive = value
|
|
}
|
|
|
|
// SetCapReaddirPlus informs the host that the hosted file system has the readdir-plus
|
|
// capability [Windows only]. A file system that has the readdir-plus capability can send
|
|
// full stat information during Readdir, thus avoiding extraneous Getattr calls.
|
|
func (host *FileSystemHost) SetCapReaddirPlus(value bool) {
|
|
host.capReaddirPlus = value
|
|
}
|
|
|
|
// Mount mounts a file system on the given mountpoint with the mount options in opts.
|
|
//
|
|
// Many of the mount options in opts are specific to the underlying FUSE implementation.
|
|
// Some of the common options include:
|
|
//
|
|
// -h --help print help
|
|
// -V --version print FUSE version
|
|
// -d -o debug enable FUSE debug output
|
|
// -s disable multi-threaded operation
|
|
//
|
|
// Please refer to the individual FUSE implementation documentation for additional options.
|
|
//
|
|
// It is allowed for the mountpoint to be the empty string ("") in which case opts is assumed
|
|
// to contain the mountpoint. It is also allowed for opts to be nil, although in this case the
|
|
// mountpoint must be non-empty.
|
|
func (host *FileSystemHost) Mount(mountpoint string, opts []string) bool {
|
|
if 0 == c_hostFuseInit() {
|
|
panic("cgofuse: cannot find winfsp")
|
|
}
|
|
|
|
/*
|
|
* Command line handling
|
|
*
|
|
* We must prepare a command line to send to FUSE. This command line will look like this:
|
|
*
|
|
* execname [mountpoint] "-f" [opts...] NULL
|
|
*
|
|
* We add the "-f" option because Go cannot handle daemonization (at least on OSX).
|
|
*/
|
|
exec := "<UNKNOWN>"
|
|
if 0 < len(os.Args) {
|
|
exec = os.Args[0]
|
|
}
|
|
argc := len(opts) + 2
|
|
if "" != mountpoint {
|
|
argc++
|
|
}
|
|
argv := make([]*c_char, argc+1)
|
|
argv[0] = c_CString(exec)
|
|
defer c_free(unsafe.Pointer(argv[0]))
|
|
opti := 1
|
|
if "" != mountpoint {
|
|
argv[1] = c_CString(mountpoint)
|
|
defer c_free(unsafe.Pointer(argv[1]))
|
|
opti++
|
|
}
|
|
argv[opti] = c_CString("-f")
|
|
defer c_free(unsafe.Pointer(argv[opti]))
|
|
opti++
|
|
for i := 0; len(opts) > i; i++ {
|
|
argv[i+opti] = c_CString(opts[i])
|
|
defer c_free(unsafe.Pointer(argv[i+opti]))
|
|
}
|
|
|
|
/*
|
|
* Mountpoint extraction
|
|
*
|
|
* We need to determine the mountpoint that FUSE is going (to try) to use, so that we
|
|
* can unmount later.
|
|
*/
|
|
if "" != mountpoint {
|
|
host.mntp = mountpoint
|
|
} else {
|
|
outargs, _ := OptParse(opts, "")
|
|
if 1 <= len(outargs) {
|
|
host.mntp = outargs[0]
|
|
}
|
|
}
|
|
if "" != host.mntp {
|
|
if "windows" != runtime.GOOS || 2 != len(host.mntp) || ':' != host.mntp[1] {
|
|
abs, err := filepath.Abs(host.mntp)
|
|
if nil == err {
|
|
host.mntp = abs
|
|
}
|
|
}
|
|
}
|
|
defer func() {
|
|
host.mntp = ""
|
|
}()
|
|
|
|
/*
|
|
* Handle zombie mounts
|
|
*
|
|
* FUSE on UNIX does not automatically unmount the file system, leaving behind "zombie"
|
|
* mounts. So set things up to always unmount the file system (unless forcibly terminated).
|
|
* This has the added benefit that the file system Destroy() always gets called.
|
|
*
|
|
* On Windows (WinFsp) this is handled by the FUSE layer and we do not have to do anything.
|
|
*/
|
|
if "windows" != runtime.GOOS {
|
|
done := make(chan bool)
|
|
defer func() {
|
|
<-done
|
|
}()
|
|
host.sigc = make(chan os.Signal, 1)
|
|
defer close(host.sigc)
|
|
go func() {
|
|
_, ok := <-host.sigc
|
|
if ok {
|
|
host.Unmount()
|
|
}
|
|
close(done)
|
|
}()
|
|
}
|
|
|
|
/*
|
|
* Tell FUSE to do its job!
|
|
*/
|
|
hndl := hostHandleNew(host)
|
|
defer hostHandleDel(hndl)
|
|
return 0 != c_hostMount(c_int(argc), &argv[0], hndl)
|
|
}
|
|
|
|
// Unmount unmounts a mounted file system.
|
|
// Unmount may be called at any time after the Init() method has been called
|
|
// and before the Destroy() method has been called.
|
|
func (host *FileSystemHost) Unmount() bool {
|
|
if nil == host.fuse {
|
|
return false
|
|
}
|
|
var mntp *c_char
|
|
if "" != host.mntp {
|
|
mntp = c_CString(host.mntp)
|
|
}
|
|
return 0 != c_hostUnmount(host.fuse, mntp)
|
|
}
|
|
|
|
// Getcontext gets information related to a file system operation.
|
|
func Getcontext() (uid uint32, gid uint32, pid int) {
|
|
context := c_fuse_get_context()
|
|
uid = uint32(context.uid)
|
|
gid = uint32(context.gid)
|
|
pid = int(context.pid)
|
|
return
|
|
}
|
|
|
|
func optNormBool(opt string) string {
|
|
if i := strings.Index(opt, "=%"); -1 != i {
|
|
switch opt[i+2:] {
|
|
case "d", "o", "x", "X":
|
|
return opt
|
|
case "v":
|
|
return opt[:i+1]
|
|
default:
|
|
panic("unknown format " + opt[i+1:])
|
|
}
|
|
} else {
|
|
return opt
|
|
}
|
|
}
|
|
|
|
func optNormInt(opt string, modf string) string {
|
|
if i := strings.Index(opt, "=%"); -1 != i {
|
|
switch opt[i+2:] {
|
|
case "d", "o", "x", "X":
|
|
return opt[:i+2] + modf + opt[i+2:]
|
|
case "v":
|
|
return opt[:i+2] + modf + "i"
|
|
default:
|
|
panic("unknown format " + opt[i+1:])
|
|
}
|
|
} else if strings.HasSuffix(opt, "=") {
|
|
return opt + "%" + modf + "i"
|
|
} else {
|
|
return opt + "=%" + modf + "i"
|
|
}
|
|
}
|
|
|
|
func optNormStr(opt string) string {
|
|
if i := strings.Index(opt, "=%"); -1 != i {
|
|
switch opt[i+2:] {
|
|
case "s", "v":
|
|
return opt[:i+2] + "s"
|
|
default:
|
|
panic("unknown format " + opt[i+1:])
|
|
}
|
|
} else if strings.HasSuffix(opt, "=") {
|
|
return opt + "%s"
|
|
} else {
|
|
return opt + "=%s"
|
|
}
|
|
}
|
|
|
|
// OptParse parses the FUSE command line arguments in args as determined by format
|
|
// and stores the resulting values in vals, which must be pointers. It returns a
|
|
// list of unparsed arguments or nil if an error happens.
|
|
//
|
|
// The format may be empty or non-empty. An empty format is taken as a special
|
|
// instruction to OptParse to only return all non-option arguments in outargs.
|
|
//
|
|
// A non-empty format is a space separated list of acceptable FUSE options. Each
|
|
// option is matched with a corresponding pointer value in vals. The combination
|
|
// of the option and the type of the corresponding pointer value, determines how
|
|
// the option is used. The allowed pointer types are pointer to bool, pointer to
|
|
// an integer type and pointer to string.
|
|
//
|
|
// For pointer to bool types:
|
|
//
|
|
// -x Match -x without parameter.
|
|
// -foo --foo As above for -foo or --foo.
|
|
// foo Match "-o foo".
|
|
// -x= -foo= --foo= foo= Match option with parameter.
|
|
// -x=%VERB ... foo=%VERB Match option with parameter of syntax.
|
|
// Allowed verbs: d,o,x,X,v
|
|
// - d,o,x,X: set to true if parameter non-0.
|
|
// - v: set to true if parameter present.
|
|
//
|
|
// The formats -x=, and -x=%v are equivalent.
|
|
//
|
|
// For pointer to other types:
|
|
//
|
|
// -x Match -x with parameter (-x=PARAM).
|
|
// -foo --foo As above for -foo or --foo.
|
|
// foo Match "-o foo=PARAM".
|
|
// -x= -foo= --foo= foo= Match option with parameter.
|
|
// -x=%VERB ... foo=%VERB Match option with parameter of syntax.
|
|
// Allowed verbs for pointer to int types: d,o,x,X,v
|
|
// Allowed verbs for pointer to string types: s,v
|
|
//
|
|
// The formats -x, -x=, and -x=%v are equivalent.
|
|
//
|
|
// For example:
|
|
//
|
|
// var f bool
|
|
// var set_attr_timeout bool
|
|
// var attr_timeout int
|
|
// var umask uint32
|
|
// outargs, err := OptParse(args, "-f attr_timeout= attr_timeout umask=%o",
|
|
// &f, &set_attr_timeout, &attr_timeout, &umask)
|
|
//
|
|
// Will accept a command line of:
|
|
//
|
|
// $ program -f -o attr_timeout=42,umask=077
|
|
//
|
|
// And will set variables as follows:
|
|
//
|
|
// f == true
|
|
// set_attr_timeout == true
|
|
// attr_timeout == 42
|
|
// umask == 077
|
|
//
|
|
func OptParse(args []string, format string, vals ...interface{}) (outargs []string, err error) {
|
|
if 0 == c_hostFuseInit() {
|
|
panic("cgofuse: cannot find winfsp")
|
|
}
|
|
|
|
defer func() {
|
|
if r := recover(); nil != r {
|
|
if s, ok := r.(string); ok {
|
|
outargs = nil
|
|
err = errors.New("OptParse: " + s)
|
|
} else {
|
|
panic(r)
|
|
}
|
|
}
|
|
}()
|
|
|
|
var opts []string
|
|
var nonopts bool
|
|
if "" == format {
|
|
opts = make([]string, 0)
|
|
nonopts = true
|
|
} else {
|
|
opts = strings.Split(format, " ")
|
|
}
|
|
|
|
align := int(2 * unsafe.Sizeof(c_size_t(0))) // match malloc alignment (usually 8 or 16)
|
|
|
|
fuse_opts := make([]c_struct_fuse_opt, len(opts)+1)
|
|
for i := 0; len(opts) > i; i++ {
|
|
var templ *c_char
|
|
switch vals[i].(type) {
|
|
case *bool:
|
|
templ = c_CString(optNormBool(opts[i]))
|
|
case *int:
|
|
templ = c_CString(optNormInt(opts[i], ""))
|
|
case *int8:
|
|
templ = c_CString(optNormInt(opts[i], "hh"))
|
|
case *int16:
|
|
templ = c_CString(optNormInt(opts[i], "h"))
|
|
case *int32:
|
|
templ = c_CString(optNormInt(opts[i], ""))
|
|
case *int64:
|
|
templ = c_CString(optNormInt(opts[i], "ll"))
|
|
case *uint:
|
|
templ = c_CString(optNormInt(opts[i], ""))
|
|
case *uint8:
|
|
templ = c_CString(optNormInt(opts[i], "hh"))
|
|
case *uint16:
|
|
templ = c_CString(optNormInt(opts[i], "h"))
|
|
case *uint32:
|
|
templ = c_CString(optNormInt(opts[i], ""))
|
|
case *uint64:
|
|
templ = c_CString(optNormInt(opts[i], "ll"))
|
|
case *uintptr:
|
|
templ = c_CString(optNormInt(opts[i], "ll"))
|
|
case *string:
|
|
templ = c_CString(optNormStr(opts[i]))
|
|
}
|
|
defer c_free(unsafe.Pointer(templ))
|
|
|
|
c_hostOptSet(&fuse_opts[i], templ, c_fuse_opt_offset_t(i*align), 1)
|
|
}
|
|
|
|
fuse_args := c_struct_fuse_args{}
|
|
defer c_fuse_opt_free_args(&fuse_args)
|
|
argc := 1 + len(args)
|
|
argp := c_calloc(c_size_t(argc+1), c_size_t(unsafe.Sizeof((*c_char)(nil))))
|
|
argv := (*[1 << 16]*c_char)(argp)
|
|
argv[0] = c_CString("<UNKNOWN>")
|
|
for i := 0; len(args) > i; i++ {
|
|
argv[1+i] = c_CString(args[i])
|
|
}
|
|
fuse_args.allocated = 1
|
|
fuse_args.argc = c_int(argc)
|
|
fuse_args.argv = (**c_char)(&argv[0])
|
|
|
|
data := c_calloc(c_size_t(len(opts)), c_size_t(align))
|
|
defer c_free(data)
|
|
|
|
if -1 == c_hostOptParse(&fuse_args, data, &fuse_opts[0], c_bool(nonopts)) {
|
|
panic("failed")
|
|
}
|
|
|
|
for i := 0; len(opts) > i; i++ {
|
|
switch v := vals[i].(type) {
|
|
case *bool:
|
|
*v = 0 != int(*(*c_int)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *int:
|
|
*v = int(*(*c_int)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *int8:
|
|
*v = int8(*(*c_int8_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *int16:
|
|
*v = int16(*(*c_int16_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *int32:
|
|
*v = int32(*(*c_int32_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *int64:
|
|
*v = int64(*(*c_int64_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *uint:
|
|
*v = uint(*(*c_unsigned)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *uint8:
|
|
*v = uint8(*(*c_uint8_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *uint16:
|
|
*v = uint16(*(*c_uint16_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *uint32:
|
|
*v = uint32(*(*c_uint32_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *uint64:
|
|
*v = uint64(*(*c_uint64_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *uintptr:
|
|
*v = uintptr(*(*c_uintptr_t)(unsafe.Pointer(uintptr(data) + uintptr(i*align))))
|
|
case *string:
|
|
s := *(**c_char)(unsafe.Pointer(uintptr(data) + uintptr(i*align)))
|
|
*v = c_GoString(s)
|
|
c_free(unsafe.Pointer(s))
|
|
}
|
|
}
|
|
|
|
if 1 >= fuse_args.argc {
|
|
outargs = make([]string, 0)
|
|
} else {
|
|
outargs = make([]string, fuse_args.argc-1)
|
|
for i := 1; int(fuse_args.argc) > i; i++ {
|
|
outargs[i-1] = c_GoString((*[1 << 16]*c_char)(unsafe.Pointer(fuse_args.argv))[i])
|
|
}
|
|
}
|
|
|
|
if nonopts && 1 <= len(outargs) && "--" == outargs[0] {
|
|
outargs = outargs[1:]
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func init() {
|
|
c_hostStaticInit()
|
|
}
|