357 lines
8.6 KiB
Go
357 lines
8.6 KiB
Go
//go:build cgo && (linux || darwin || freebsd || netbsd || openbsd)
|
|
|
|
package fs
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"sync"
|
|
"syscall"
|
|
|
|
"sirherobrine23.com.br/Sirherobrine23/cgofuse/fuse"
|
|
"sirherobrine23.com.br/Sirherobrine23/cgofuse/fuse/utils"
|
|
)
|
|
|
|
type _fuse[T File, F FileSystem[T]] struct {
|
|
fuse.FileSystemBase
|
|
FS F
|
|
FdOpen *sync.Map
|
|
}
|
|
|
|
type _FileSystemMount[T File, F FileSystem[T]] struct {
|
|
Target string
|
|
Fuse *fuse.FileSystemHost
|
|
fs F
|
|
}
|
|
|
|
func (fs _FileSystemMount[_, F]) FS() F { return fs.fs }
|
|
func (fs _FileSystemMount[_, _]) Done() { fs.Fuse.Done() }
|
|
func (fs _FileSystemMount[_, _]) Mount(ctx context.Context) error {
|
|
return fs.Fuse.Mount(ctx, fs.Target)
|
|
}
|
|
|
|
// Mount fuse
|
|
func New[T File](target string, fs FileSystem[T]) FileSystemMount[T, FileSystem[T]] {
|
|
return &_FileSystemMount[T, FileSystem[T]]{
|
|
Target: target,
|
|
fs: fs,
|
|
Fuse: fuse.NewFileSystemHost(&_fuse[T, FileSystem[T]]{FS: fs, FdOpen: &sync.Map{}}),
|
|
}
|
|
}
|
|
|
|
func (fs _FileSystemMount[_, _]) SetOption(args ...any) {
|
|
if len(args) >= 1 {
|
|
switch v := args[0].(type) {
|
|
case string:
|
|
switch v {
|
|
case "capCaseInsensitive":
|
|
if len(args) >= 2 {
|
|
if v, ok := args[1].(bool); ok {
|
|
fs.Fuse.SetCapCaseInsensitive(v)
|
|
}
|
|
} else {
|
|
fs.Fuse.SetCapCaseInsensitive(true)
|
|
}
|
|
case "capReaddirPlus":
|
|
if len(args) >= 2 {
|
|
if v, ok := args[1].(bool); ok {
|
|
fs.Fuse.SetCapReaddirPlus(v)
|
|
}
|
|
} else {
|
|
fs.Fuse.SetCapReaddirPlus(true)
|
|
}
|
|
case "capReadlink":
|
|
if len(args) >= 2 {
|
|
if v, ok := args[1].(bool); ok {
|
|
fs.Fuse.SetCapReaddirPlus(v)
|
|
}
|
|
} else {
|
|
fs.Fuse.SetCapReaddirPlus(true)
|
|
}
|
|
case "capDeleteAccess":
|
|
if len(args) >= 2 {
|
|
if v, ok := args[1].(bool); ok {
|
|
fs.Fuse.SetCapDeleteAccess(v)
|
|
}
|
|
} else {
|
|
fs.Fuse.SetCapDeleteAccess(true)
|
|
}
|
|
case "capOpenTrunc":
|
|
if len(args) >= 2 {
|
|
if v, ok := args[1].(bool); ok {
|
|
fs.Fuse.SetCapOpenTrunc(v)
|
|
}
|
|
} else {
|
|
fs.Fuse.SetCapOpenTrunc(true)
|
|
}
|
|
case "directIO":
|
|
if len(args) >= 2 {
|
|
if v, ok := args[1].(bool); ok {
|
|
fs.Fuse.SetDirectIO(v)
|
|
}
|
|
} else {
|
|
fs.Fuse.SetDirectIO(true)
|
|
}
|
|
case "useIno":
|
|
if len(args) >= 2 {
|
|
if v, ok := args[1].(bool); ok {
|
|
fs.Fuse.SetUseIno(v)
|
|
}
|
|
} else {
|
|
fs.Fuse.SetUseIno(true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Init() {
|
|
if v, ok := any(fused.FS).(FileSystemInitDestroy); ok {
|
|
v.Init()
|
|
}
|
|
}
|
|
func (fused *_fuse[T, F]) Destroy() {
|
|
if v, ok := any(fused.FS).(FileSystemInitDestroy); ok {
|
|
v.Destroy()
|
|
}
|
|
}
|
|
|
|
// Create creates and opens a file.
|
|
func (fused *_fuse[T, F]) Create(path string, flags int, mode uint32) (int, uint64) {
|
|
file, err := fused.FS.OpenFile(path, flags, fs.FileMode(mode).Perm())
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err), ^uint64(0)
|
|
}
|
|
fd := uint64(file.Fd())
|
|
fused.FdOpen.Store(fd, file)
|
|
return 0, fd
|
|
}
|
|
|
|
// Open opens a file.
|
|
func (fused *_fuse[T, F]) Open(path string, flags int) (int, uint64) {
|
|
file, err := fused.FS.OpenFile(path, flags, 0)
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err), ^uint64(0)
|
|
}
|
|
fd := uint64(file.Fd())
|
|
fused.FdOpen.Store(fd, file)
|
|
return 0, fd
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Release(path string, fh uint64) int {
|
|
if v, ok := fused.FdOpen.LoadAndDelete(fh); ok {
|
|
return utils.ErrorToStatus(v.(File).Close())
|
|
}
|
|
return -fuse.ENOENT
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Read(path string, buff []byte, ofst int64, fh uint64) int {
|
|
if v, ok := fused.FdOpen.Load(fh); ok {
|
|
file := v.(File)
|
|
n, err := file.ReadAt(buff, ofst)
|
|
if err != nil {
|
|
if err == io.EOF && n > 0 {
|
|
return n
|
|
}
|
|
return utils.ErrorToStatus(err)
|
|
}
|
|
return n
|
|
}
|
|
return -fuse.ENOENT
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Write(path string, buff []byte, ofst int64, fh uint64) int {
|
|
if v, ok := fused.FdOpen.Load(fh); ok {
|
|
n, err := v.(File).WriteAt(buff, ofst)
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err)
|
|
}
|
|
return n
|
|
}
|
|
return -fuse.ENOENT
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Flush(path string, fh uint64) int {
|
|
if v, ok := fused.FdOpen.Load(fh); ok {
|
|
return utils.ErrorToStatus(v.(File).Sync())
|
|
}
|
|
return -fuse.ENOENT
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Fsync(path string, datasync bool, fh uint64) int {
|
|
if v, ok := fused.FdOpen.Load(fh); ok {
|
|
return utils.ErrorToStatus(v.(File).Sync())
|
|
}
|
|
return -fuse.ENOENT
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Mkdir(path string, mode uint32) int {
|
|
return utils.ErrorToStatus(fused.FS.Mkdir(path, fs.FileMode(mode)))
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Unlink(path string) int {
|
|
if v, ok := any(fused.FS).(FileSystemUnlink); ok {
|
|
return utils.ErrorToStatus(v.Unlink(path))
|
|
}
|
|
return utils.ErrorToStatus(fused.FS.Remove(path))
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Rmdir(path string) int {
|
|
if v, ok := any(fused.FS).(FileSystemRmdir); ok {
|
|
return utils.ErrorToStatus(v.Rmdir(path))
|
|
}
|
|
return utils.ErrorToStatus(fused.FS.Remove(path))
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Access(path string, mask uint32) int {
|
|
if v, ok := any(fused.FS).(FileSystemAccess); ok {
|
|
return utils.ErrorToStatus(v.Access(path, mask))
|
|
}
|
|
return -fuse.ENOSYS
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Link(oldpath string, newpath string) int {
|
|
if v, ok := any(fused.FS).(FileSystemLink); ok {
|
|
return utils.ErrorToStatus(v.Link(oldpath, newpath))
|
|
}
|
|
return -fuse.ENOSYS
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Symlink(target string, newpath string) int {
|
|
if v, ok := any(fused.FS).(FileSystemSymlink); ok {
|
|
return utils.ErrorToStatus(v.Symlink(target, newpath))
|
|
}
|
|
return -fuse.ENOSYS
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Readlink(path string) (int, string) {
|
|
if v, ok := any(fused.FS).(FileSystemReadlink); ok {
|
|
target, err := v.Readlink(path)
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err), ""
|
|
}
|
|
return 0, target
|
|
}
|
|
return -fuse.ENOSYS, ""
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Rename(oldpath string, newpath string) int {
|
|
err := fused.FS.Rename(oldpath, newpath)
|
|
return utils.ErrorToStatus(err)
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Chmod(path string, mode uint32) int {
|
|
if v, ok := any(fused.FS).(FileSystemChmod); ok {
|
|
return utils.ErrorToStatus(v.Chmod(path, fs.FileMode(mode)))
|
|
}
|
|
return -fuse.ENOSYS
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Chown(path string, uid uint32, gid uint32) int {
|
|
if v, ok := any(fused.FS).(FileSystemChown); ok {
|
|
return utils.ErrorToStatus(v.Chown(path, int(uid), int(gid)))
|
|
}
|
|
return -fuse.ENOSYS
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Getattr(path string, stat *fuse.Stat_t, fh uint64) int {
|
|
fsstat, err := fs.FileInfo(nil), error(nil)
|
|
if v, ok := fused.FdOpen.Load(fh); ok {
|
|
fsstat, err = v.(T).Stat()
|
|
} else {
|
|
fsstat, err = fused.FS.Stat(path)
|
|
}
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err)
|
|
}
|
|
utils.AppendStat(fsstat, stat)
|
|
return 0
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Statfs(path string, stat *fuse.Statfs_t) int {
|
|
stat.Namemax = syscall.NAME_MAX
|
|
|
|
stat.Bsize = 4096
|
|
stat.Frsize = 4096
|
|
stat.Flag = 4096
|
|
|
|
stat.Files = 0
|
|
stat.Ffree = 0
|
|
stat.Favail = 0
|
|
|
|
stat.Blocks = 1204 << 25
|
|
stat.Bfree = uint64(float64(stat.Blocks) * (0.99))
|
|
stat.Bavail = stat.Bfree
|
|
|
|
if v, ok := any(fused.FS).(FileSystemStatFS); ok {
|
|
if tota, free, err := v.Statfs(path); err == nil {
|
|
stat.Blocks = tota
|
|
stat.Bfree = free
|
|
stat.Bavail = free
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Truncate(path string, size int64, fh uint64) int {
|
|
if v, ok := fused.FdOpen.Load(fh); ok {
|
|
return utils.ErrorToStatus(v.(File).Truncate(size))
|
|
}
|
|
fd, err := fused.FS.OpenFile(path, os.O_RDWR, 0)
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err)
|
|
}
|
|
defer fd.Close()
|
|
return utils.ErrorToStatus(fd.Truncate(size))
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Opendir(path string) (int, uint64) {
|
|
file, err := fused.FS.OpenFile(path, syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err), ^uint64(0)
|
|
}
|
|
fd := uint64(file.Fd())
|
|
fused.FdOpen.Store(fd, file)
|
|
return 0, fd
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Releasedir(path string, fh uint64) int {
|
|
if v, ok := fused.FdOpen.LoadAndDelete(fh); ok {
|
|
return utils.ErrorToStatus(v.(File).Close())
|
|
}
|
|
return -fuse.ENOENT
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Fsyncdir(path string, datasync bool, fh uint64) int {
|
|
if v, ok := fused.FdOpen.Load(fh); ok {
|
|
return utils.ErrorToStatus(v.(File).Sync())
|
|
}
|
|
return -fuse.ENOENT
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Readdir(path string, fill fuse.StatFill, ofst int64, fh uint64) int {
|
|
files, err := fused.FS.ReadDir(path)
|
|
if err != nil {
|
|
return utils.ErrorToStatus(err)
|
|
}
|
|
fill(".", nil, 0)
|
|
fill("..", nil, 0)
|
|
for _, file := range files {
|
|
stat := new(fuse.Stat_t)
|
|
utils.AppendDirEntry(file, stat)
|
|
if !fill(file.Name(), stat, 0) {
|
|
break
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (fused *_fuse[T, F]) Utimens(path string, tmsp []fuse.Timespec) int {
|
|
if v, ok := any(fused.FS).(FileSystemUtimens); ok {
|
|
return utils.ErrorToStatus(v.Utimens(path, tmsp[0].Time(), tmsp[1].Time()))
|
|
}
|
|
return -fuse.ENOSYS
|
|
}
|