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), int64(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 }