Files
cgofuse/fs/fs_unix.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
}