Support multiple file access (#17)

Implement a file handler to keep track of files, making possible have several of them open.

Signed-off-by: Antonio Navarro Perez <antnavper@gmail.com>
This commit is contained in:
Antonio Navarro Perez 2020-11-09 19:37:15 +01:00 committed by GitHub
parent ab7d379408
commit 0d7f74717b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 148 additions and 43 deletions

View file

@ -72,7 +72,7 @@ func (s *Handler) Mount(mpc *config.MountPoint, ef config.EventFunc) error {
return err return err
} }
host := fuse.NewFileSystemHost(&FS{FSS: torrents}) host := fuse.NewFileSystemHost(NewFS(torrents))
// TODO improve error handling here // TODO improve error handling here
go func() { go func() {

View file

@ -1,6 +1,7 @@
package fuse package fuse
import ( import (
"errors"
"io" "io"
"math" "math"
"os" "os"
@ -13,37 +14,48 @@ import (
type FS struct { type FS struct {
fuse.FileSystemBase fuse.FileSystemBase
fh *fileHandler
}
lock sync.Mutex func NewFS(fss []fs.Filesystem) fuse.FileSystemInterface {
FSS []fs.Filesystem return &FS{
fh: &fileHandler{fss: fss},
}
} }
func (fs *FS) Open(path string, flags int) (errc int, fh uint64) { func (fs *FS) Open(path string, flags int) (errc int, fh uint64) {
return 0, 0 fh, err := fs.fh.OpenHolder(path)
if err == os.ErrNotExist {
logrus.WithField("path", path).Warn("file does not exists")
return -fuse.ENOENT, fhNone
}
if err != nil {
logrus.WithError(err).WithField("path", path).Error("error opening file")
return -fuse.EIO, fhNone
} }
func (fs *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { return 0, fh
defer fs.synchronize()() }
func (fs *FS) Opendir(path string) (errc int, fh uint64) {
return fs.Open(path, 0)
}
func (cfs *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
if path == "/" { if path == "/" {
stat.Mode = fuse.S_IFDIR | 0555 stat.Mode = fuse.S_IFDIR | 0555
return 0 return 0
} }
file, err := fs.findFile(path) file, err := cfs.fh.GetFile(path, fh)
if err != nil {
logrus.WithField("path", path).WithError(err).Warn("error finding file")
return -fuse.EIO
}
if err == os.ErrNotExist { if err == os.ErrNotExist {
logrus.WithField("path", path).Warn("file does not exists") logrus.WithField("path", path).Warn("file does not exists")
return -fuse.ENOENT return -fuse.ENOENT
} }
if err != nil { if err != nil {
logrus.WithField("path", path).WithError(err).Error("error reading file") logrus.WithError(err).WithField("path", path).Error("error getting holder when reading file attributes")
return -fuse.EIO return -fuse.EIO
} }
@ -58,16 +70,14 @@ func (fs *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
} }
func (fs *FS) Read(path string, dest []byte, off int64, fh uint64) int { func (fs *FS) Read(path string, dest []byte, off int64, fh uint64) int {
defer fs.synchronize()() file, err := fs.fh.GetFile(path, fh)
file, err := fs.findFile(path)
if err == os.ErrNotExist { if err == os.ErrNotExist {
logrus.WithField("path", path).Warn("file does not exists") logrus.WithField("path", path).Error("file not found on READ operation")
return -fuse.ENOENT return -fuse.ENOENT
} }
if err != nil { if err != nil {
logrus.WithField("path", path).WithError(err).Warn("error finding file") logrus.WithError(err).WithField("path", path).Error("error getting holder reading data from file")
return -fuse.EIO return -fuse.EIO
} }
@ -80,7 +90,7 @@ func (fs *FS) Read(path string, dest []byte, off int64, fh uint64) int {
n, err := file.ReadAt(buf, off) n, err := file.ReadAt(buf, off)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
logrus.WithError(err).Error("error reading data") logrus.WithError(err).WithField("path", path).Error("error reading data")
return -fuse.EIO return -fuse.EIO
} }
@ -90,37 +100,140 @@ func (fs *FS) Read(path string, dest []byte, off int64, fh uint64) int {
} }
func (fs *FS) Release(path string, fh uint64) (errc int) { func (fs *FS) Release(path string, fh uint64) (errc int) {
defer fs.synchronize()() if err := fs.fh.Remove(fh); err != nil {
logrus.WithError(err).WithField("path", path).Error("error getting holder when releasing file")
return -fuse.EIO
}
return 0 return 0
} }
func (fs *FS) Releasedir(path string, fh uint64) int {
return fs.Release(path, fh)
}
func (fs *FS) Readdir(path string, func (fs *FS) Readdir(path string,
fill func(name string, stat *fuse.Stat_t, ofst int64) bool, fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
ofst int64, ofst int64,
fh uint64) (errc int) { fh uint64) (errc int) {
defer fs.synchronize()()
fill(".", nil, 0) fill(".", nil, 0)
fill("..", nil, 0) fill("..", nil, 0)
for _, ifs := range fs.FSS { //TODO improve this function to make use of fh index if possible
files, err := ifs.ReadDir(path) paths, err := fs.fh.ListDir(path)
if err != nil { if err != nil {
logrus.WithField("path", path).Error("error reading directory")
return -fuse.EIO return -fuse.EIO
} }
for p := range files {
for _, p := range paths {
if !fill(p, nil, 0) { if !fill(p, nil, 0) {
logrus.WithField("path", p).Error("error adding directory") logrus.WithField("path", p).Error("error adding directory")
break break
} }
} }
}
return 0 return 0
} }
func (fs *FS) findFile(path string) (fs.File, error) { const fhNone = ^uint64(0)
for _, f := range fs.FSS {
var ErrHolderEmpty = errors.New("file holder is empty")
var ErrBadHolderIndex = errors.New("holder index too big")
type fileHandler struct {
mu sync.Mutex
opened []fs.File
fss []fs.Filesystem
}
func (fh *fileHandler) GetFile(path string, fhi uint64) (fs.File, error) {
fh.mu.Lock()
defer fh.mu.Unlock()
if fhi == fhNone {
return fh.lookupFile(path)
}
return fh.get(fhi)
}
func (fh *fileHandler) ListDir(path string) ([]string, error) {
fh.mu.Lock()
defer fh.mu.Unlock()
var out []string
for _, ifs := range fh.fss {
files, err := ifs.ReadDir(path)
if err != nil {
return nil, err
}
for p := range files {
out = append(out, p)
}
}
return out, nil
}
func (fh *fileHandler) OpenHolder(path string) (uint64, error) {
fh.mu.Lock()
defer fh.mu.Unlock()
file, err := fh.lookupFile(path)
if err != nil {
return fhNone, err
}
for i, old := range fh.opened {
if old == nil {
fh.opened[i] = file
return uint64(i), nil
}
}
fh.opened = append(fh.opened, file)
return uint64(len(fh.opened) - 1), nil
}
func (fh *fileHandler) get(fhi uint64) (fs.File, error) {
if int(fhi) >= len(fh.opened) {
return nil, ErrBadHolderIndex
}
// TODO check opened slice to avoid panics
h := fh.opened[int(fhi)]
if h == nil {
return nil, ErrHolderEmpty
}
return h, nil
}
func (fh *fileHandler) Remove(fhi uint64) error {
fh.mu.Lock()
defer fh.mu.Unlock()
if fhi == fhNone {
return nil
}
// TODO check opened slice to avoid panics
f := fh.opened[int(fhi)]
if f == nil {
return ErrHolderEmpty
}
if err := f.Close(); err != nil {
return err
}
fh.opened[int(fhi)] = nil
return nil
}
func (fh *fileHandler) lookupFile(path string) (fs.File, error) {
for _, f := range fh.fss {
file, err := f.Open(path) file, err := f.Open(path)
if err == os.ErrNotExist { if err == os.ErrNotExist {
continue continue
@ -129,7 +242,6 @@ func (fs *FS) findFile(path string) (fs.File, error) {
return nil, err return nil, err
} }
// TODO add file to list of opened files to be able to close it when not in use
if file != nil { if file != nil {
return file, nil return file, nil
} }
@ -137,10 +249,3 @@ func (fs *FS) findFile(path string) (fs.File, error) {
return nil, os.ErrNotExist return nil, os.ErrNotExist
} }
func (fs *FS) synchronize() func() {
fs.lock.Lock()
return func() {
fs.lock.Unlock()
}
}