|
|
@@ -0,0 +1,257 @@
|
|
|
+package tftp
|
|
|
+
|
|
|
+// arozos virtual path translation handler for TFTP
|
|
|
+// Similar to FTP aofs implementation but adapted for TFTP
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/spf13/afero"
|
|
|
+ "imuslab.com/arozos/mod/filesystem"
|
|
|
+ "imuslab.com/arozos/mod/user"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ aofsCanRead = 1
|
|
|
+ aofsCanWrite = 2
|
|
|
+)
|
|
|
+
|
|
|
+type aofs struct {
|
|
|
+ userinfo *user.User
|
|
|
+ tmpFolder string
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Create(name string) (afero.File, error) {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return nil, errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.Create(rewritePath)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Chown(name string, uid, gid int) error {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.Chown(rewritePath, uid, gid)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Mkdir(name string, perm os.FileMode) error {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.Mkdir(rewritePath, perm)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) MkdirAll(path string, perm os.FileMode) error {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(path)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.MkdirAll(rewritePath, perm)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Open(name string) (afero.File, error) {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanRead) {
|
|
|
+ return nil, errors.New("Permission denied")
|
|
|
+ }
|
|
|
+
|
|
|
+ return fsh.FileSystemAbstraction.Open(rewritePath)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Stat(name string) (os.FileInfo, error) {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanRead) {
|
|
|
+ return nil, errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.Stat(rewritePath)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return nil, errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.OpenFile(rewritePath, flag, perm)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) AllocateSpace(size int) error {
|
|
|
+ if a.userinfo.StorageQuota.HaveSpace(int64(size)) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return errors.New("Storage Quota Full")
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Remove(name string) error {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+
|
|
|
+ return fsh.FileSystemAbstraction.Remove(rewritePath)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) RemoveAll(path string) error {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(path)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.RemoveAll(rewritePath)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Rename(oldname, newname string) error {
|
|
|
+ fshsrc, rewritePathsrc, err := a.pathRewrite(oldname)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ fshdest, rewritePathdest, err := a.pathRewrite(newname)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fshsrc, rewritePathsrc, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fshdest, rewritePathdest, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+
|
|
|
+ if !fshdest.FileSystemAbstraction.FileExists(filepath.Dir(rewritePathdest)) {
|
|
|
+ fshdest.FileSystemAbstraction.MkdirAll(filepath.Dir(rewritePathdest), 0775)
|
|
|
+ }
|
|
|
+
|
|
|
+ if fshsrc.UUID == fshdest.UUID {
|
|
|
+ //Renaming in same fsh
|
|
|
+ return fshsrc.FileSystemAbstraction.Rename(rewritePathsrc, rewritePathdest)
|
|
|
+ } else {
|
|
|
+ //Cross fsh read write.
|
|
|
+ f, err := fshsrc.FileSystemAbstraction.ReadStream(rewritePathsrc)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer f.Close()
|
|
|
+
|
|
|
+ err = fshdest.FileSystemAbstraction.WriteStream(rewritePathdest, f, 0775)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ err = fshsrc.FileSystemAbstraction.RemoveAll(rewritePathsrc)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Name() string {
|
|
|
+ return "arozos virtualFS (TFTP)"
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Chmod(name string, mode os.FileMode) error {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.Chmod(rewritePath, mode)
|
|
|
+}
|
|
|
+
|
|
|
+func (a aofs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
|
+ fsh, rewritePath, err := a.pathRewrite(name)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if !a.checkAllowAccess(fsh, rewritePath, aofsCanWrite) {
|
|
|
+ return errors.New("Permission denied")
|
|
|
+ }
|
|
|
+ return fsh.FileSystemAbstraction.Chtimes(rewritePath, atime, mtime)
|
|
|
+}
|
|
|
+
|
|
|
+// arozos adaptive functions
|
|
|
+// This function rewrite the path from tftp representation to real filepath on disk
|
|
|
+func (a aofs) pathRewrite(path string) (*filesystem.FileSystemHandler, string, error) {
|
|
|
+ path = filepath.ToSlash(filepath.Clean(path))
|
|
|
+
|
|
|
+ if path == "/" {
|
|
|
+ //Roots. Return TFTP do not list directory error
|
|
|
+ return nil, "", errors.New("TFTP does not support directory listing. Use use a valid file path like user:/Desktop/foo.txt")
|
|
|
+ } else if len(path) > 0 {
|
|
|
+ //Rewrite the path for any alternative filepath
|
|
|
+ //Get the uuid of the filepath
|
|
|
+ path = strings.TrimPrefix(path, "/")
|
|
|
+ path = strings.TrimPrefix(path, "./") // Prevent path traversal
|
|
|
+ // Support both /user/Desktop/test.txt or user:/Desktop/test.txt
|
|
|
+ path = strings.Replace(path, ":/", "/", 1)
|
|
|
+ fmt.Println(path)
|
|
|
+ subpaths := strings.Split(path, "/")
|
|
|
+ fsHandlerUUID := subpaths[0]
|
|
|
+ remainingPaths := subpaths[1:]
|
|
|
+ fsh, err := a.userinfo.GetFileSystemHandlerFromVirtualPath(fsHandlerUUID + ":/")
|
|
|
+ if err != nil {
|
|
|
+ return nil, "", errors.New("File System Abstraction not found")
|
|
|
+ }
|
|
|
+
|
|
|
+ rpath, err := fsh.FileSystemAbstraction.VirtualPathToRealPath(fsh.UUID+":/"+strings.Join(remainingPaths, "/"), a.userinfo.Username)
|
|
|
+ if err != nil {
|
|
|
+ return nil, "", errors.New("File System Handler Hierarchy not supported by TFTP driver")
|
|
|
+ }
|
|
|
+ return fsh, rpath, nil
|
|
|
+ } else {
|
|
|
+ //fsh not found.
|
|
|
+ return nil, "", errors.New("Invalid path")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Check if user has access to the given path, mode can be {read / write}
|
|
|
+func (a aofs) checkAllowAccess(fsh *filesystem.FileSystemHandler, path string, mode int) bool {
|
|
|
+ vpath, err := fsh.FileSystemAbstraction.RealPathToVirtualPath(path, a.userinfo.Username)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ if mode == aofsCanRead {
|
|
|
+ return a.userinfo.CanRead(vpath)
|
|
|
+ } else if mode == aofsCanWrite {
|
|
|
+ return a.userinfo.CanWrite(vpath)
|
|
|
+ } else {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|