fs.go 1.52 KB
Newer Older
Adphi's avatar
Adphi committed
1
package affuse
Adphi's avatar
Adphi committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

import (
	"context"
	"errors"
	"fmt"
	"sync"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
)

type FS interface {
	fs.FS
	Mount(ctx context.Context, mountPoint string) error
	Unmount() error
}

func New(afs afero.Fs) (FS, error) {
	if afs == nil {
		return nil, errors.New("afs cannot be nil")
	}
	root, err := afs.Open("/")
	if err != nil {
		return nil, fmt.Errorf("failed to open file system root: %v", err)
	}
	s, err := root.Stat()
	if err != nil {
		return nil, fmt.Errorf("failed get file system root stats: %v", err)
	}
	if !s.IsDir() {
		return nil, errors.New("file system root is not a directory")
	}
Adphi's avatar
Adphi committed
36
	fuse.MaxReadahead(128*1024)
Adphi's avatar
Adphi committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
	return &aferoFs{fs: afs, root: root}, nil
}

type aferoFs struct {
	fs   afero.Fs
	root afero.File
	mu sync.Mutex
	mount string
}

func (a *aferoFs) Root() (fs.Node, error) {
	logrus.Debugf("Root: %s", a.root.Name())
	return &dir{
		fs:   a.fs,
		file: a.root,
	}, nil
}

func (a *aferoFs) Mount(ctx context.Context, p string) error {
	a.mu.Lock()
	c, err := fuse.Mount(p)
	if err != nil {
		a.mu.Unlock()
		return err
	}
	defer c.Close()
	a.mount = p
	a.mu.Unlock()
	go func() {
		for {
			select {
			case <- ctx.Done():
				a.Unmount()
			}
		}
	}()
	if err := fs.Serve(c, a); err != nil {
		return err
	}
	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		return err
	}
	return nil
}

func (a *aferoFs) Unmount() error {
	a.mu.Lock()
	defer a.mu.Unlock()
	return fuse.Unmount(a.mount)
}