162 lines
3.9 KiB
Go
162 lines
3.9 KiB
Go
package fs
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/barakmich/kubelwagen"
|
|
"github.com/hanwen/go-fuse/fuse"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type LocalFs struct {
|
|
base string
|
|
open map[int]*os.File
|
|
}
|
|
|
|
func NewLocalFs(path string) *LocalFs {
|
|
return &LocalFs{
|
|
base: path,
|
|
open: make(map[int]*os.File),
|
|
}
|
|
}
|
|
|
|
func (fs *LocalFs) Handle(r *kubelwagen.Request) *kubelwagen.Response {
|
|
switch r.Method {
|
|
case kubelwagen.MethodOpenDir:
|
|
return fs.openDir(r)
|
|
case kubelwagen.MethodGetAttr:
|
|
return fs.getAttr(r)
|
|
case kubelwagen.MethodStatFs:
|
|
return fs.statFs(r)
|
|
case kubelwagen.MethodChmod:
|
|
return fs.serveFromErr(r, os.Chmod(fs.getPath(r), os.FileMode(r.Mode)))
|
|
case kubelwagen.MethodChown:
|
|
return fs.serveFromErr(r, os.Chown(fs.getPath(r), int(r.UID), int(r.GID)))
|
|
case kubelwagen.MethodTruncate:
|
|
return fs.serveFromErr(r, os.Truncate(fs.getPath(r), r.Offset))
|
|
case kubelwagen.MethodMknod:
|
|
return fs.serveFromErr(r, syscall.Mknod(fs.getPath(r), r.Mode, int(r.Dev)))
|
|
case kubelwagen.MethodMkdir:
|
|
return fs.serveFromErr(r, os.Mkdir(fs.getPath(r), os.FileMode(r.Mode)))
|
|
case kubelwagen.MethodUnlink:
|
|
return fs.serveFromErr(r, syscall.Unlink(fs.getPath(r)))
|
|
case kubelwagen.MethodRmdir:
|
|
return fs.serveFromErr(r, syscall.Rmdir(fs.getPath(r)))
|
|
case kubelwagen.MethodSymlink:
|
|
return fs.serveFromErr(r, os.Symlink(r.NewPath, fs.getPath(r)))
|
|
case kubelwagen.MethodRename:
|
|
return fs.serveFromErr(r, os.Rename(fs.getPath(r), filepath.Join(fs.base, r.NewPath)))
|
|
case kubelwagen.MethodLink:
|
|
return fs.serveFromErr(r, os.Link(fs.getPath(r), filepath.Join(fs.base, r.NewPath)))
|
|
case kubelwagen.MethodReadLink:
|
|
f, err := os.Readlink(fs.getPath(r))
|
|
if err != nil {
|
|
return kubelwagen.ErrorResp(r, nil)
|
|
}
|
|
out := fs.serveFromErr(r, err)
|
|
out.LinkStr = f
|
|
return out
|
|
case kubelwagen.MethodAccess:
|
|
return fs.serveFromErr(r, syscall.Access(fs.getPath(r), r.Mode))
|
|
}
|
|
return &kubelwagen.Response{
|
|
ID: r.ID,
|
|
Code: fuse.ENOSYS,
|
|
}
|
|
}
|
|
|
|
func (fs *LocalFs) getPath(r *kubelwagen.Request) string {
|
|
return filepath.Join(fs.base, r.Path)
|
|
}
|
|
|
|
func (fs *LocalFs) openDir(r *kubelwagen.Request) *kubelwagen.Response {
|
|
out := &kubelwagen.Response{
|
|
ID: r.ID,
|
|
Code: fuse.OK,
|
|
}
|
|
// returns dirents and status
|
|
|
|
f, err := os.Open(fs.getPath(r))
|
|
if err != nil {
|
|
logrus.Errorf("LocalFS openDir (%s): %s\n", r.Path, err)
|
|
return kubelwagen.ErrorResp(r, err)
|
|
}
|
|
want := 100
|
|
output := make([]fuse.DirEntry, 0, want)
|
|
for {
|
|
infos, err := f.Readdir(want)
|
|
for i := range infos {
|
|
// workaround for https://code.google.com/p/go/issues/detail?id=5960
|
|
if infos[i] == nil {
|
|
continue
|
|
}
|
|
n := infos[i].Name()
|
|
d := fuse.DirEntry{
|
|
Name: n,
|
|
}
|
|
if s := fuse.ToStatT(infos[i]); s != nil {
|
|
d.Mode = uint32(s.Mode)
|
|
d.Ino = s.Ino
|
|
} else {
|
|
logrus.Printf("ReadDir entry %q for %q has no stat info", n, r.Path)
|
|
}
|
|
output = append(output, d)
|
|
}
|
|
if len(infos) < want || err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
logrus.Errorln("Readdir() returned err:", err)
|
|
break
|
|
}
|
|
}
|
|
f.Close()
|
|
|
|
out.Dirents = output
|
|
return out
|
|
}
|
|
|
|
func (fs *LocalFs) getAttr(r *kubelwagen.Request) *kubelwagen.Response {
|
|
out := &kubelwagen.Response{
|
|
ID: r.ID,
|
|
Code: fuse.OK,
|
|
}
|
|
fi, err := os.Stat(fs.getPath(r))
|
|
if err != nil {
|
|
logrus.Errorf("LocalFS getAttr (%s): %s\n", r.Path, err)
|
|
return kubelwagen.ErrorResp(r, err)
|
|
}
|
|
out.Stat = fuse.ToAttr(fi)
|
|
return out
|
|
}
|
|
|
|
func (fs *LocalFs) statFs(r *kubelwagen.Request) *kubelwagen.Response {
|
|
out := &kubelwagen.Response{
|
|
ID: r.ID,
|
|
Code: fuse.OK,
|
|
}
|
|
s := syscall.Statfs_t{}
|
|
err := syscall.Statfs(fs.getPath(r), &s)
|
|
if err != nil {
|
|
return kubelwagen.ErrorResp(r, err)
|
|
}
|
|
g := &fuse.StatfsOut{}
|
|
g.FromStatfsT(&s)
|
|
out.Statfs = g
|
|
return out
|
|
}
|
|
|
|
func (fs *LocalFs) serveFromErr(r *kubelwagen.Request, err error) *kubelwagen.Response {
|
|
out := &kubelwagen.Response{
|
|
ID: r.ID,
|
|
Code: fuse.OK,
|
|
}
|
|
if err != nil {
|
|
return kubelwagen.ErrorResp(r, err)
|
|
}
|
|
return out
|
|
|
|
}
|