dir.go 2.96 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 36 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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"syscall"

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

type Dir interface {
	fs.Node
	fs.HandleReadDirAller
	fs.NodeStringLookuper
	fs.NodeMkdirer
	fs.NodeCreater
	fs.NodeRemover
	fs.NodeRenamer
}

var _ Dir = &dir{}

type dir struct {
	fs   afero.Fs
	file afero.File
}

func attrs(a *fuse.Attr, s os.FileInfo) {
	a.Mode = s.Mode()
	a.Mtime = s.ModTime()
	a.Ctime = s.ModTime()
	a.Crtime = s.ModTime()
	a.Size = uint64(s.Size())
}

func (d *dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	s, err := d.file.Stat()
	if err != nil {
		return err
	}
	if s.IsDir() {
		attr.Mode = os.ModeDir | os.ModePerm
		return nil
	}
	attrs(attr, s)
	return nil
}

func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	logrus.Debugf("ReadDirAll: %s", d.file.Name())
	fs, err := afero.ReadDir(d.fs, d.file.Name())
	if err != nil {
		return nil, syscall.ENOENT
	}
	var out []fuse.Dirent
	for i := range fs {
		var t fuse.DirentType
		if fs[i].IsDir() {
			t = fuse.DT_Dir
		} else {
			t = fuse.DT_File
		}
		out = append(out, fuse.Dirent{
			Inode: uint64(i),
			Type:  t,
			Name:  fs[i].Name(),
		})
	}
	logrus.Debugf("ReadDirAll: %s = %+v", d.file.Name(), out)
	return out, nil
}

func (d *dir) path(name string) string {
	path := filepath.Join(d.file.Name(), name)
	if strings.HasPrefix(path, "/") {
		path = path[1:]
	}
	return path
}

func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
	path := d.path(name)
	logrus.Debugf("Lookup %s: %s", d.file.Name(), name)
	f, err := d.fs.Open(path)
	if err != nil {
		logrus.Errorf("failed to Lookup %s: %v", name, err)
		return nil, syscall.ENOENT
	}
	s, err := f.Stat()
	if err != nil {
		logrus.Errorf("failed to get stats %s: %v", name, err)
		return nil, syscall.ENOENT
	}
	if s.IsDir() {
		return &dir{
			fs:   d.fs,
			file: f,
		}, nil
	}
	return &file{file: f, fs: d.fs}, nil
}

func (d *dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
	logrus.Debugf("Mkdir %s: %s", d.file.Name(), req.Name)
	if err := d.fs.Mkdir(d.path(req.Name), req.Mode|req.Umask); err != nil {
		logrus.Errorf("Mkdir %s: %s: %v", d.file.Name(), req.Name, err)
		return nil, err
	}
	return d.Lookup(ctx, req.Name)
}

func (d *dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
	logrus.Debugf("Create %s: %s", d.file.Name(), req.Name)
	f, err := d.fs.Create(d.path(req.Name))
	if err != nil {
		logrus.Errorf("Create %s: %s: %v", d.file.Name(), req.Name, err)
		return nil, nil, err
	}
	file := &file{file: f, fs: d.fs}
	return file, file, nil
}

func (d *dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	return d.fs.Remove(req.Name)
}

func (d *dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
	if err := d.fs.Rename(d.path(req.OldName), req.NewName); err != nil {
		return syscall.ENOENT
	}
	return nil
}