浏览代码

Tidy up the abstraction structure

Toby Chui 3 年之前
父节点
当前提交
2f37148fa0

+ 1 - 0
agi.go

@@ -27,6 +27,7 @@ func AGIInit() {
 		FileSystemRender:     thumbRenderHandler,
 		ShareManager:         shareManager,
 		NightlyManager:       nightlyManager,
+		TempFolderPath:       *tmp_directory,
 	})
 	if err != nil {
 		log.Println("AGI Gateway Initialization Failed")

+ 6 - 6
file_system.go

@@ -298,7 +298,7 @@ func system_fs_handleFileSearch(w http.ResponseWriter, r *http.Request) {
 			}
 
 			thisVpath, _ := targetFSH.FileSystemAbstraction.RealPathToVirtualPath(matchedFile, userinfo.Username)
-			results = append(results, fs.GetFileDataFromPath(thisVpath, matchedFile, 2))
+			results = append(results, fs.GetFileDataFromPath(targetFSH, thisVpath, matchedFile, 2))
 
 		}
 
@@ -322,7 +322,7 @@ func system_fs_handleFileSearch(w http.ResponseWriter, r *http.Request) {
 			if casesensitve != "true" {
 				keyword = strings.ToLower(keyword)
 			}
-			err = shareEntryTable.Walk(subpath, userinfo.Username, userinfo.GetUserPermissionGroupNames(), func(fileData fs.FileData) error {
+			err = shareManager.Walk(subpath, userinfo, func(fileData fs.FileData) error {
 				filename := filepath.Base(fileData.Filename)
 				if casesensitve != "true" {
 					filename = strings.ToLower(filename)
@@ -348,8 +348,7 @@ func system_fs_handleFileSearch(w http.ResponseWriter, r *http.Request) {
 					if matcher.Match(thisFilename) {
 						//This is a matching file
 						thisVpath, _ := fshAbs.RealPathToVirtualPath(path, userinfo.Username)
-						results = append(results, fs.GetFileDataFromPath(thisVpath, path, 2))
-
+						results = append(results, fs.GetFileDataFromPath(targetFSH, thisVpath, path, 2))
 					}
 				}
 
@@ -2493,7 +2492,7 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 	//Handle some special virtual file systems / mount points
 	if fsh.UUID == "share" && subpath == "" {
 		userpgs := userinfo.GetUserPermissionGroupNames()
-		files := shareEntryTable.ListRootForUser(userinfo.Username, userpgs)
+		files := shareManager.ListRootForUser(userinfo, userpgs)
 		parsedFilelist = files
 	} else {
 		//Normal file systems
@@ -2528,6 +2527,7 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 		files, err := fshAbs.Glob(realpath + "/*")
 		if err != nil {
 			log.Println("[File System] Unable to list dir: " + err.Error())
+			return
 		}
 		var shortCutInfo *shortcut.ShortcutData = nil
 		for _, v := range files {
@@ -2933,7 +2933,7 @@ func system_fs_handleCacheRender(w http.ResponseWriter, r *http.Request) {
 		common.SendErrorResponse(w, "Unable to resolve target directory")
 		return
 	}
-	rpath, _ := fsh.FileSystemAbstraction.RealPathToVirtualPath(subpath, userinfo.Username)
+	rpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(subpath, userinfo.Username)
 
 	//Get folder sort mode
 	sortMode := "default"

+ 188 - 0
legacy/bufffs/bufffs.go

@@ -0,0 +1,188 @@
+package bufffs
+
+/*
+	bufffs.go
+
+	This module provide a mockup template for buffered type file system to create
+	a os.File compatible type of file for other purposes
+*/
+
+import (
+	"errors"
+	"io"
+	"os"
+	"syscall"
+	"time"
+)
+
+type file struct {
+	name string
+	buf  []byte
+	ptr  int64
+
+	IsDir        bool
+	CloseHandler func(interface{}) error
+	ClosePayload interface{}
+	FshRealPath  string
+}
+
+// New creates new mock file, which can be used as os.File.
+//func New(name string, isDir bool, content []byte, fshRealpath string, closeHandler func(interface{}) error, closePayload interface{}) *file {
+func New(name string) *file {
+	return &file{
+		name: name,
+		buf:  []byte{},
+		ptr:  0,
+
+		IsDir:        false,
+		CloseHandler: nil,
+		ClosePayload: nil,
+		FshRealPath:  "",
+	}
+}
+
+func (f *file) SetContent(content []byte) {
+	f.buf = content
+}
+
+func (f *file) Chdir() error {
+	return errors.New("operation not supported")
+}
+func (f *file) Chmod(mode os.FileMode) error {
+	return errors.New("operation not supported")
+}
+func (f *file) Chown(uid, gid int) error {
+	return errors.New("operation not supported")
+}
+
+func (f *file) Fd() uintptr {
+	return 0
+}
+
+func (f *file) Name() string {
+	return ""
+}
+
+func (f *file) ReadDir(n int) ([]os.DirEntry, error) {
+	return []os.DirEntry{}, errors.New("operation not supported")
+}
+func (f *file) ReadFrom(r io.Reader) (n int64, err error) {
+	return 0, errors.New("operation not supported")
+}
+func (f *file) Readdir(n int) ([]os.FileInfo, error) {
+	return []os.FileInfo{}, errors.New("operation not supported")
+}
+func (f *file) Readdirnames(n int) (names []string, err error) {
+	return []string{}, errors.New("operation not supported")
+}
+
+func (f *file) SetDeadline(t time.Time) error {
+	return errors.New("operation not supported")
+}
+func (f *file) SetReadDeadline(t time.Time) error {
+	return errors.New("operation not supported")
+}
+func (f *file) SetWriteDeadline(t time.Time) error {
+	return errors.New("operation not supported")
+}
+
+func (f *file) Sync() error {
+	return errors.New("operation not supported")
+}
+func (f *file) SyscallConn() (syscall.RawConn, error) {
+	return nil, errors.New("operation not supported")
+}
+
+func (f *file) WriteAt(b []byte, off int64) (n int, err error) {
+	return 0, errors.New("operation not supported")
+}
+func (f *file) WriteString(s string) (n int, err error) {
+	return 0, errors.New("operation not supported")
+}
+
+func (m *file) Close() error {
+	if m.CloseHandler != nil {
+		return m.CloseHandler(m.ClosePayload)
+	}
+	return nil
+}
+
+func (m *file) Read(p []byte) (n int, err error) {
+	n, err = m.ReadAt(p, m.ptr)
+	m.ptr += int64(n)
+
+	return n, err
+}
+
+func (m *file) Seek(offset int64, whence int) (int64, error) {
+	switch whence {
+	case io.SeekStart:
+		m.ptr = offset
+	case io.SeekEnd:
+		m.ptr = int64(len(m.buf)) + offset
+	case io.SeekCurrent:
+		m.ptr = m.ptr + offset
+	}
+
+	return m.ptr, nil
+}
+
+func (m *file) Stat() (os.FileInfo, error) {
+	return fileInfo{
+		size: int64(len(m.buf)),
+		dir:  m.IsDir,
+	}, nil
+}
+
+func (m *file) ReadAt(p []byte, off int64) (n int, err error) {
+	if n = copy(p, m.buf[off:]); n == 0 {
+		return n, io.EOF
+	} else {
+		return n, nil
+	}
+}
+
+func (m *file) Write(p []byte) (n int, err error) {
+	m.buf = append(m.buf, p...)
+
+	return len(p), nil
+}
+
+func (m *file) Truncate(size int64) error {
+	if size > int64(len(m.buf)) {
+		size = int64(len(m.buf))
+	}
+
+	m.buf = m.buf[:size-1]
+
+	return nil
+}
+
+type fileInfo struct {
+	size int64
+	dir  bool
+}
+
+func (m fileInfo) Name() string {
+	return ""
+}
+
+func (m fileInfo) Size() int64 {
+	return m.size
+}
+
+func (m fileInfo) Mode() os.FileMode {
+	return os.FileMode(0)
+}
+
+func (m fileInfo) ModTime() time.Time {
+	return time.Time{}
+}
+
+func (m fileInfo) IsDir() bool {
+	return m.dir
+}
+
+func (m fileInfo) Sys() interface{} {
+	return nil
+}

+ 3 - 23
mod/agi/agi.file.go

@@ -232,7 +232,7 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 			return reply
 		}
 		results := []string{}
-		filepath.Walk(rpath, func(path string, info os.FileInfo, err error) error {
+		fsh.FileSystemAbstraction.Walk(rpath, func(path string, info os.FileInfo, err error) error {
 			if err != nil {
 				//Ignore this error file and continue
 				return nil
@@ -284,7 +284,7 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 			rootDirs := []string{}
 			fileHandlers := u.GetAllFileSystemHandler()
 			for _, fsh := range fileHandlers {
-				if fsh.Hierarchy == "backup" || fsh.IsVirtual() {
+				if fsh.Hierarchy == "backup" || fsh.UUID == "share" {
 
 				} else {
 					rootDirs = append(rootDirs, fsh.UUID+":/")
@@ -415,10 +415,9 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 				//Hidden file. Skip this
 				continue
 			}
-			thisVpath, _ := u.RealPathToVirtualPath(filename)
+			thisVpath, _ := realpathToVirtualpath(fsh, filename, u)
 			results = append(results, thisVpath)
 		}
-
 		reply, _ := vm.ToValue(results)
 		return reply
 	})
@@ -633,25 +632,6 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 		}
 	})
 
-	/*
-		vm.Set("_filelib_decodeURI", func(call otto.FunctionCall) otto.Value {
-			originalURI, err := call.Argument(0).ToString()
-			if err != nil {
-				g.raiseError(err)
-				reply, _ := vm.ToValue(false)
-				return reply
-			}
-			decodedURI := specialURIDecode(originalURI)
-			result, err := otto.ToValue(decodedURI)
-			if err != nil {
-				g.raiseError(err)
-				reply, _ := vm.ToValue(false)
-				return reply
-			}
-			return result
-		})
-	*/
-
 	//Other file operations, wip
 
 	//Wrap all the native code function into an imagelib class

+ 44 - 2
mod/agi/agi.go

@@ -3,16 +3,20 @@ package agi
 import (
 	"encoding/json"
 	"errors"
+	"io"
 	"io/ioutil"
 	"log"
 	"net/http"
+	"os"
 	"path/filepath"
 	"strings"
 	"time"
 
 	"github.com/robertkrimen/otto"
+	uuid "github.com/satori/go.uuid"
 
 	apt "imuslab.com/arozos/mod/apt"
+	"imuslab.com/arozos/mod/filesystem"
 	metadata "imuslab.com/arozos/mod/filesystem/metadata"
 	"imuslab.com/arozos/mod/iot"
 	"imuslab.com/arozos/mod/share"
@@ -58,8 +62,9 @@ type AgiSysInfo struct {
 	NightlyManager       *nightly.TaskManager
 
 	//Scanning Roots
-	StartupRoot   string
-	ActivateScope []string
+	StartupRoot    string
+	ActivateScope  []string
+	TempFolderPath string
 }
 
 type Gateway struct {
@@ -390,3 +395,40 @@ func (g *Gateway) ExecuteAGIScriptAsUser(scriptFile string, targetUser *user.Use
 	valueString, err := value.ToString()
 	return valueString, nil
 }
+
+/*
+
+	Get user specific tmp filepath for buffering remote file. Return filepath and closer
+	tempFilepath, closerFunction := g.getUserSpecificTempFilePath(u, "myfile.txt")
+	//Do something with it, after done
+	closerFunction();
+*/
+func (g *Gateway) getUserSpecificTempFilePath(u *user.User, filename string) (string, func()) {
+	uuid := uuid.NewV4().String()
+	tmpFileLocation := filepath.Join(g.Option.TempFolderPath, "agiBuff", u.Username, uuid, filepath.Base(filename))
+	os.MkdirAll(filepath.Dir(tmpFileLocation), 0775)
+	return tmpFileLocation, func() {
+		os.RemoveAll(filepath.Dir(tmpFileLocation))
+	}
+}
+
+/*
+	Buffer remote reosurces to local by fsh and rpath. Return buffer filepath on local device and its closer function
+*/
+func (g *Gateway) bufferRemoteResourcesToLocal(fsh *filesystem.FileSystemHandler, u *user.User, rpath string) (string, func(), error) {
+	buffFile, closerFunc := g.getUserSpecificTempFilePath(u, rpath)
+	f, err := fsh.FileSystemAbstraction.ReadStream(rpath)
+	if err != nil {
+		return "", nil, err
+	}
+	dest, err := os.OpenFile(buffFile, os.O_CREATE|os.O_RDWR, 0775)
+	if err != nil {
+		return "", nil, err
+	}
+	io.Copy(dest, f)
+	f.Close()
+	dest.Close()
+	return buffFile, func() {
+		closerFunc()
+	}, nil
+}

+ 84 - 16
mod/agi/agi.image.go

@@ -37,7 +37,6 @@ func (g *Gateway) ImageLibRegister() {
 }
 
 func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
-
 	//Get image dimension, requires filepath (virtual)
 	vm.Set("_imagelib_getImageDimension", func(call otto.FunctionCall) otto.Value {
 		imageFileVpath, err := call.Argument(0).ToString()
@@ -57,9 +56,22 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 			return otto.FalseValue()
 		}
 
-		panic("fix point 1")
+		openingPath := imagePath
+		var closerFunc func()
+		if fsh.RequireBuffer {
+			bufferPath, cf := g.getUserSpecificTempFilePath(u, imagePath)
+			closerFunc = cf
+			defer closerFunc()
+			c, err := fsh.FileSystemAbstraction.ReadFile(imagePath)
+			if err != nil {
+				g.raiseError(errors.New("Read from file system failed: " + err.Error()))
+				return otto.FalseValue()
+			}
+			os.WriteFile(bufferPath, c, 0775)
+			openingPath = bufferPath
+		}
 
-		file, err := os.Open(imagePath)
+		file, err := os.Open(openingPath)
 		if err != nil {
 			g.raiseError(err)
 			return otto.FalseValue()
@@ -103,7 +115,7 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 		}
 
 		//Convert the virtual paths to real paths
-		_, rsrc, err := virtualPathToRealPath(vsrc, u)
+		srcfsh, rsrc, err := virtualPathToRealPath(vsrc, u)
 		if err != nil {
 			g.raiseError(err)
 			return otto.FalseValue()
@@ -128,19 +140,47 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 			}
 		}
 
+		resizeOpeningFile := rsrc
+		resizeWritingFile := rdest
+		var srcCloser func()
+		var destCloser func()
+		if srcfsh.RequireBuffer {
+			resizeOpeningFile, srcCloser, err = g.bufferRemoteResourcesToLocal(srcfsh, u, rsrc)
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+			defer srcCloser()
+		}
+
+		if destfsh.RequireBuffer {
+			resizeWritingFile, destCloser, err = g.bufferRemoteResourcesToLocal(destfsh, u, rdest)
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+			defer destCloser()
+		}
+
 		//Resize the image
-		src, err := imaging.Open(rsrc)
+		src, err := imaging.Open(resizeOpeningFile)
 		if err != nil {
 			//Opening failed
 			g.raiseError(err)
 			return otto.FalseValue()
 		}
 		src = imaging.Resize(src, int(width), int(height), imaging.Lanczos)
-		err = imaging.Save(src, rdest)
+		err = imaging.Save(src, resizeWritingFile)
 		if err != nil {
 			g.raiseError(err)
 			return otto.FalseValue()
 		}
+
+		if destfsh.RequireBuffer {
+			c, _ := os.ReadFile(resizeWritingFile)
+			destfsh.FileSystemAbstraction.WriteFile(rdest, c, 0775)
+		}
+
 		return otto.TrueValue()
 	})
 
@@ -181,6 +221,7 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 		}
 
 		//Convert the virtual paths to realpaths
+
 		srcFsh, rsrc, err := virtualPathToRealPath(vsrc, u)
 		if err != nil {
 			g.raiseError(err)
@@ -192,12 +233,21 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 			g.raiseError(err)
 			return otto.FalseValue()
 		}
-		destFshAbs := destFsh.FileSystemAbstraction
-		fmt.Println("WIP", destFshAbs)
+		destWritePath := rdest
+		var destCloserFunction func()
+		if destFsh.RequireBuffer {
+			destWritePath, destCloserFunction = g.getUserSpecificTempFilePath(u, rdest)
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+			defer destCloserFunction()
+		}
 
 		//Try to read the source image
 		imageBytes, err := srcFshAbs.ReadFile(rsrc)
 		if err != nil {
+			fmt.Println(err)
 			g.raiseError(err)
 			return otto.FalseValue()
 		}
@@ -209,7 +259,7 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 		}
 
 		//Crop the image
-		croppedImg, err := cutter.Crop(img, cutter.Config{
+		croppedImg, _ := cutter.Crop(img, cutter.Config{
 			Width:  int(width),
 			Height: int(height),
 			Anchor: image.Point{int(posx), int(posy)},
@@ -217,23 +267,30 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 		})
 
 		//Create the new image
-		out, err := os.Create(rdest)
+		out, err := os.Create(destWritePath)
 		if err != nil {
 			g.raiseError(err)
 			return otto.FalseValue()
 		}
 
-		if strings.ToLower(filepath.Ext(rdest)) == ".png" {
+		if strings.ToLower(filepath.Ext(destWritePath)) == ".png" {
 			png.Encode(out, croppedImg)
-		} else if strings.ToLower(filepath.Ext(rdest)) == ".jpg" {
+		} else if strings.ToLower(filepath.Ext(destWritePath)) == ".jpg" {
 			jpeg.Encode(out, croppedImg, nil)
 		} else {
 			g.raiseError(errors.New("Not supported format: Only support jpg or png"))
 			return otto.FalseValue()
 		}
-
 		out.Close()
 
+		if destFsh.RequireBuffer {
+			c, _ := os.ReadFile(destWritePath)
+			err := destFsh.FileSystemAbstraction.WriteFile(rdest, c, 0775)
+			if err != nil {
+				fmt.Println(">", err.Error())
+			}
+		}
+
 		return otto.TrueValue()
 	})
 
@@ -279,15 +336,26 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 		}
 
 		//Convert the vsrc to real path
-		_, rsrc, err := virtualPathToRealPath(vsrc, u)
+		fsh, rsrc, err := virtualPathToRealPath(vsrc, u)
 		if err != nil {
 			g.raiseError(err)
 			return otto.FalseValue()
 		}
 
+		analysisSrc := rsrc
+		var closerFunc func()
+		if fsh.RequireBuffer {
+			analysisSrc, closerFunc, err = g.bufferRemoteResourcesToLocal(fsh, u, rsrc)
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+			defer closerFunc()
+		}
+
 		if classifier == "default" || classifier == "darknet19" {
 			//Use darknet19 for classification
-			r, err := neuralnet.AnalysisPhotoDarknet19(rsrc)
+			r, err := neuralnet.AnalysisPhotoDarknet19(analysisSrc)
 			if err != nil {
 				g.raiseError(err)
 				return otto.FalseValue()
@@ -303,7 +371,7 @@ func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
 
 		} else if classifier == "yolo3" {
 			//Use yolo3 for classification, return positions of object as well
-			r, err := neuralnet.AnalysisPhotoYOLO3(rsrc)
+			r, err := neuralnet.AnalysisPhotoYOLO3(analysisSrc)
 			if err != nil {
 				g.raiseError(err)
 				return otto.FalseValue()

+ 1 - 1
mod/filesystem/emptyFilesystemAbstraction.go → mod/filesystem/abstractions/emptyfs/emptyfs.go

@@ -1,4 +1,4 @@
-package filesystem
+package emptyfs
 
 import (
 	"errors"

+ 55 - 42
mod/filesystem/localFilesystemAbstraction.go → mod/filesystem/abstractions/localfs/localfs.go

@@ -1,4 +1,4 @@
-package filesystem
+package localfs
 
 import (
 	"errors"
@@ -80,9 +80,10 @@ func (l LocalFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
 */
 
 func (l LocalFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
-	if strings.HasPrefix(subpath, l.UUID+":/") {
+	subpath = filepath.ToSlash(subpath)
+	if strings.HasPrefix(subpath, l.UUID+":") {
 		//This is full virtual path. Trim the uuid and correct the subpath
-		subpath = subpath[len(l.UUID+":/"):]
+		subpath = subpath[len(l.UUID+":"):]
 	}
 
 	if l.Hierarchy == "user" {
@@ -94,57 +95,69 @@ func (l LocalFileSystemAbstraction) VirtualPathToRealPath(subpath string, userna
 }
 
 func (l LocalFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
-	thisStorageRootAbs, err := filepath.Abs(l.Rootpath)
-	if err != nil {
-		//Fail to abs this path. Maybe this is a emulated file system?
-		thisStorageRootAbs = l.Rootpath
-	}
-	thisStorageRootAbs = filepath.ToSlash(filepath.Clean(thisStorageRootAbs))
-
-	subPath := ""
-	if len(fullpath) > len(l.Rootpath) && filepath.ToSlash(fullpath[:len(l.Rootpath)]) == filepath.ToSlash(l.Rootpath) {
-		//This realpath is in contained inside this storage root
-		subtractionPath := l.Rootpath
-		if l.Hierarchy == "user" {
-			//Check if this file is belongs to this user
-			startOffset := len(filepath.Clean(l.Rootpath) + "/users/")
-			if len(fullpath) < startOffset+len(username) {
-				//This file is not owned by this user
-				return "", errors.New("File not owned by this user")
-			} else {
-				userNameMatch := fullpath[startOffset : startOffset+len(username)]
-				if userNameMatch != username {
+	/*
+		thisStorageRootAbs, err := filepath.Abs(l.Rootpath)
+		if err != nil {
+			//Fail to abs this path. Maybe this is a emulated file system?
+			thisStorageRootAbs = l.Rootpath
+		}
+		thisStorageRootAbs = filepath.ToSlash(filepath.Clean(thisStorageRootAbs))
+
+		subPath := ""
+		if len(fullpath) > len(l.Rootpath) && filepath.ToSlash(fullpath[:len(l.Rootpath)]) == filepath.ToSlash(l.Rootpath) {
+			//This realpath is in contained inside this storage root
+			subtractionPath := l.Rootpath
+			if l.Hierarchy == "user" {
+				//Check if this file is belongs to this user
+				startOffset := len(filepath.Clean(l.Rootpath) + "/users/")
+				if len(fullpath) < startOffset+len(username) {
 					//This file is not owned by this user
 					return "", errors.New("File not owned by this user")
+				} else {
+					userNameMatch := fullpath[startOffset : startOffset+len(username)]
+					if userNameMatch != username {
+						//This file is not owned by this user
+						return "", errors.New("File not owned by this user")
+					}
 				}
+
+				//Generate subtraction path
+				subtractionPath = filepath.ToSlash(filepath.Clean(filepath.Join(l.Rootpath, "users", username)))
 			}
 
-			//Generate subtraction path
-			subtractionPath = filepath.ToSlash(filepath.Clean(filepath.Join(l.Rootpath, "users", username)))
-		}
+			if len(subtractionPath) < len(fullpath) {
+				subPath = fullpath[len(subtractionPath):]
+			}
 
-		if len(subtractionPath) < len(fullpath) {
-			subPath = fullpath[len(subtractionPath):]
-		}
+		} else if len(fullpath) > len(thisStorageRootAbs) && filepath.ToSlash(fullpath[:len(thisStorageRootAbs)]) == filepath.ToSlash(thisStorageRootAbs) {
+			//The realpath contains the absolute path of this storage root
+			subtractionPath := thisStorageRootAbs
+			if l.Hierarchy == "user" {
+				subtractionPath = thisStorageRootAbs + "/users/" + username + "/"
+			}
 
-	} else if len(fullpath) > len(thisStorageRootAbs) && filepath.ToSlash(fullpath[:len(thisStorageRootAbs)]) == filepath.ToSlash(thisStorageRootAbs) {
-		//The realpath contains the absolute path of this storage root
-		subtractionPath := thisStorageRootAbs
-		if l.Hierarchy == "user" {
-			subtractionPath = thisStorageRootAbs + "/users/" + username + "/"
+			if len(subtractionPath) < len(fullpath) {
+				subPath = fullpath[len(subtractionPath):]
+			}
+		} else if filepath.ToSlash(fullpath) == filepath.ToSlash(l.Rootpath) {
+			//Storage Root's root
+			subPath = ""
 		}
 
-		if len(subtractionPath) < len(fullpath) {
-			subPath = fullpath[len(subtractionPath):]
+		if len(subPath) > 1 && subPath[:1] == "/" {
+			subPath = subPath[1:]
 		}
-	} else if filepath.ToSlash(fullpath) == filepath.ToSlash(l.Rootpath) {
-		//Storage Root's root
-		subPath = ""
+	*/
+	fullpath = filepath.ToSlash(fullpath)
+	if strings.HasPrefix(fullpath, l.UUID+":") && !common.FileExists(fullpath) {
+		return "", errors.New("invalid usage: Given path is vpath")
 	}
-
-	if len(subPath) > 1 && subPath[:1] == "/" {
-		subPath = subPath[1:]
+	prefix := filepath.ToSlash(filepath.Join(l.Rootpath, "users", username))
+	if l.Hierarchy == "public" {
+		prefix = filepath.ToSlash(l.Rootpath)
 	}
+	fullpath = filepath.ToSlash(filepath.Clean(fullpath))
+	subPath := strings.TrimPrefix(fullpath, prefix)
 
 	return l.UUID + ":" + filepath.ToSlash(subPath), nil
 }

+ 188 - 0
mod/filesystem/abstractions/webdavfs/bufffs/bufffs.go

@@ -0,0 +1,188 @@
+package bufffs
+
+/*
+	bufffs.go
+
+	This module provide a mockup template for buffered type file system to create
+	a os.File compatible type of file for other purposes
+*/
+
+import (
+	"errors"
+	"io"
+	"os"
+	"syscall"
+	"time"
+)
+
+type file struct {
+	name string
+	buf  []byte
+	ptr  int64
+
+	IsDir        bool
+	CloseHandler func(interface{}) error
+	ClosePayload interface{}
+	FshRealPath  string
+}
+
+// New creates new mock file, which can be used as os.File.
+//func New(name string, isDir bool, content []byte, fshRealpath string, closeHandler func(interface{}) error, closePayload interface{}) *file {
+func New(name string) *file {
+	return &file{
+		name: name,
+		buf:  []byte{},
+		ptr:  0,
+
+		IsDir:        false,
+		CloseHandler: nil,
+		ClosePayload: nil,
+		FshRealPath:  "",
+	}
+}
+
+func (f *file) SetContent(content []byte) {
+	f.buf = content
+}
+
+func (f *file) Chdir() error {
+	return errors.New("operation not supported")
+}
+func (f *file) Chmod(mode os.FileMode) error {
+	return errors.New("operation not supported")
+}
+func (f *file) Chown(uid, gid int) error {
+	return errors.New("operation not supported")
+}
+
+func (f *file) Fd() uintptr {
+	return 0
+}
+
+func (f *file) Name() string {
+	return ""
+}
+
+func (f *file) ReadDir(n int) ([]os.DirEntry, error) {
+	return []os.DirEntry{}, errors.New("operation not supported")
+}
+func (f *file) ReadFrom(r io.Reader) (n int64, err error) {
+	return 0, errors.New("operation not supported")
+}
+func (f *file) Readdir(n int) ([]os.FileInfo, error) {
+	return []os.FileInfo{}, errors.New("operation not supported")
+}
+func (f *file) Readdirnames(n int) (names []string, err error) {
+	return []string{}, errors.New("operation not supported")
+}
+
+func (f *file) SetDeadline(t time.Time) error {
+	return errors.New("operation not supported")
+}
+func (f *file) SetReadDeadline(t time.Time) error {
+	return errors.New("operation not supported")
+}
+func (f *file) SetWriteDeadline(t time.Time) error {
+	return errors.New("operation not supported")
+}
+
+func (f *file) Sync() error {
+	return errors.New("operation not supported")
+}
+func (f *file) SyscallConn() (syscall.RawConn, error) {
+	return nil, errors.New("operation not supported")
+}
+
+func (f *file) WriteAt(b []byte, off int64) (n int, err error) {
+	return 0, errors.New("operation not supported")
+}
+func (f *file) WriteString(s string) (n int, err error) {
+	return 0, errors.New("operation not supported")
+}
+
+func (m *file) Close() error {
+	if m.CloseHandler != nil {
+		return m.CloseHandler(m.ClosePayload)
+	}
+	return nil
+}
+
+func (m *file) Read(p []byte) (n int, err error) {
+	n, err = m.ReadAt(p, m.ptr)
+	m.ptr += int64(n)
+
+	return n, err
+}
+
+func (m *file) Seek(offset int64, whence int) (int64, error) {
+	switch whence {
+	case io.SeekStart:
+		m.ptr = offset
+	case io.SeekEnd:
+		m.ptr = int64(len(m.buf)) + offset
+	case io.SeekCurrent:
+		m.ptr = m.ptr + offset
+	}
+
+	return m.ptr, nil
+}
+
+func (m *file) Stat() (os.FileInfo, error) {
+	return fileInfo{
+		size: int64(len(m.buf)),
+		dir:  m.IsDir,
+	}, nil
+}
+
+func (m *file) ReadAt(p []byte, off int64) (n int, err error) {
+	if n = copy(p, m.buf[off:]); n == 0 {
+		return n, io.EOF
+	} else {
+		return n, nil
+	}
+}
+
+func (m *file) Write(p []byte) (n int, err error) {
+	m.buf = append(m.buf, p...)
+
+	return len(p), nil
+}
+
+func (m *file) Truncate(size int64) error {
+	if size > int64(len(m.buf)) {
+		size = int64(len(m.buf))
+	}
+
+	m.buf = m.buf[:size-1]
+
+	return nil
+}
+
+type fileInfo struct {
+	size int64
+	dir  bool
+}
+
+func (m fileInfo) Name() string {
+	return ""
+}
+
+func (m fileInfo) Size() int64 {
+	return m.size
+}
+
+func (m fileInfo) Mode() os.FileMode {
+	return os.FileMode(0)
+}
+
+func (m fileInfo) ModTime() time.Time {
+	return time.Time{}
+}
+
+func (m fileInfo) IsDir() bool {
+	return m.dir
+}
+
+func (m fileInfo) Sys() interface{} {
+	return nil
+}

+ 45 - 5
mod/filesystem/webdavClient/webdavclient.go → mod/filesystem/abstractions/webdavfs/webdavfs.go

@@ -1,4 +1,4 @@
-package webdavclient
+package webdavfs
 
 import (
 	"errors"
@@ -31,6 +31,11 @@ type WebDAVFileSystem struct {
 	c         *gowebdav.Client
 }
 
+type myFileType struct {
+	io.Reader
+	io.Closer
+}
+
 func NewWebDAVMount(UUID string, Hierarchy string, root string, user string, password string, tmpBuff string) (*WebDAVFileSystem, error) {
 	//Connect to webdav server
 	c := gowebdav.NewClient(root, user, password)
@@ -82,6 +87,10 @@ func (e WebDAVFileSystem) Open(filename string) (*os.File, error) {
 	return nil, errors.New("filesystem type not supported")
 }
 func (e WebDAVFileSystem) OpenFile(filename string, flag int, perm os.FileMode) (*os.File, error) {
+	//Buffer the target file to memory
+	//To be implement: Wait for Golang's fs.File.Write function to be released
+	//f := bufffs.New(filename)
+	//return f, nil
 	return nil, errors.New("filesystem type not supported")
 }
 func (e WebDAVFileSystem) Remove(filename string) error {
@@ -103,9 +112,10 @@ func (e WebDAVFileSystem) Stat(filename string) (os.FileInfo, error) {
 }
 
 func (e WebDAVFileSystem) VirtualPathToRealPath(subpath string, username string) (string, error) {
-	if strings.HasPrefix(subpath, e.UUID+":/") {
+	subpath = filterFilepath(filepath.ToSlash(filepath.Clean(subpath)))
+	if strings.HasPrefix(subpath, e.UUID+":") {
 		//This is full virtual path. Trim the uuid and correct the subpath
-		subpath = subpath[len(e.UUID+":/"):]
+		subpath = strings.TrimPrefix(subpath, e.UUID+":")
 	}
 
 	if e.Hierarchy == "user" {
@@ -139,25 +149,34 @@ func (e WebDAVFileSystem) IsDir(filename string) bool {
 
 //Notes: This is not actual Glob function. This just emulate Glob using ReadDir with max depth 1 layer
 func (e WebDAVFileSystem) Glob(wildcard string) ([]string, error) {
+	wildcard = filepath.ToSlash(filepath.Clean(wildcard))
+	if !strings.HasPrefix(wildcard, "/") {
+		//Handle case for listing root, "*"
+		wildcard = "/" + wildcard
+	}
+
 	fileInfos, err := e.c.ReadDir(filterFilepath(filepath.ToSlash(filepath.Clean(filepath.Dir(wildcard)))))
 	if err != nil {
 		return []string{}, err
 	}
 
 	validFiles := []string{}
+	matchingRule := wildCardToRegexp(wildcard)
 	for _, fileInfo := range fileInfos {
 		thisFullPath := filepath.ToSlash(filepath.Join(filepath.Dir(wildcard), fileInfo.Name()))
-		match, _ := regexp.MatchString(wildcard, thisFullPath)
+		match, _ := regexp.MatchString(matchingRule, thisFullPath)
 		if match {
 			validFiles = append(validFiles, thisFullPath)
 		}
 	}
+
 	return validFiles, nil
 }
 func (e WebDAVFileSystem) GetFileSize(filename string) int64 {
 	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
 	s, err := e.Stat(filename)
 	if err != nil {
+		log.Println(err)
 		return 0
 	}
 
@@ -196,6 +215,11 @@ func (e WebDAVFileSystem) ReadStream(filename string) (io.ReadCloser, error) {
 
 func (e WebDAVFileSystem) Walk(rootpath string, walkFn filepath.WalkFunc) error {
 	rootpath = filepath.ToSlash(filepath.Clean(rootpath))
+	rootStat, err := e.Stat(rootpath)
+	err = walkFn(rootpath, rootStat, err)
+	if err != nil {
+		return err
+	}
 	return e.walk(rootpath, walkFn)
 }
 
@@ -227,10 +251,26 @@ func (e WebDAVFileSystem) walk(thisPath string, walkFun filepath.WalkFunc) error
 }
 
 func filterFilepath(rawpath string) string {
+	rawpath = strings.TrimSpace(rawpath)
 	if strings.HasPrefix(rawpath, "./") {
 		return rawpath[1:]
-	} else if rawpath == "." {
+	} else if rawpath == "." || rawpath == "" {
 		return "/"
 	}
 	return rawpath
 }
+
+func wildCardToRegexp(pattern string) string {
+	var result strings.Builder
+	for i, literal := range strings.Split(pattern, "*") {
+		// Replace * with .*
+		if i > 0 {
+			result.WriteString(".*")
+		}
+
+		// Quote any regular expression meta characters in the
+		// literal text.
+		result.WriteString(regexp.QuoteMeta(literal))
+	}
+	return result.String()
+}

+ 7 - 5
mod/filesystem/filesystem.go

@@ -21,7 +21,9 @@ import (
 
 	db "imuslab.com/arozos/mod/database"
 	"imuslab.com/arozos/mod/disk/hybridBackup"
-	webdavclient "imuslab.com/arozos/mod/filesystem/webdavClient"
+	"imuslab.com/arozos/mod/filesystem/abstractions/emptyfs"
+	"imuslab.com/arozos/mod/filesystem/abstractions/localfs"
+	"imuslab.com/arozos/mod/filesystem/abstractions/webdavfs"
 )
 
 //Options for creating new file system handler
@@ -174,7 +176,7 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 				HierarchyConfig:       hsConfig,
 				InitiationTime:        time.Now().Unix(),
 				FilesystemDatabase:    fsdb,
-				FileSystemAbstraction: NewLocalFileSystemAbstraction(option.Uuid, rootpath, option.Hierarchy, option.Access == "readonly"),
+				FileSystemAbstraction: localfs.NewLocalFileSystemAbstraction(option.Uuid, rootpath, option.Hierarchy, option.Access == "readonly"),
 				Filesystem:            fstype,
 				Closed:                false,
 			}, nil
@@ -190,7 +192,7 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 				HierarchyConfig:       DefaultEmptyHierarchySpecificConfig,
 				InitiationTime:        time.Now().Unix(),
 				FilesystemDatabase:    fsdb,
-				FileSystemAbstraction: NewLocalFileSystemAbstraction(option.Uuid, rootpath, option.Hierarchy, option.Access == "readonly"),
+				FileSystemAbstraction: localfs.NewLocalFileSystemAbstraction(option.Uuid, rootpath, option.Hierarchy, option.Access == "readonly"),
 				Filesystem:            fstype,
 				Closed:                false,
 			}, nil
@@ -202,7 +204,7 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 		user := option.Username
 		password := option.Password
 
-		webdavfs, err := webdavclient.NewWebDAVMount(option.Uuid, option.Hierarchy, root, user, password, "./tmp/webdavBuff")
+		webdavfs, err := webdavfs.NewWebDAVMount(option.Uuid, option.Hierarchy, root, user, password, "./tmp/webdavBuff")
 		if err != nil {
 			return nil, err
 		}
@@ -236,7 +238,7 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 				HierarchyConfig:       nil,
 				InitiationTime:        time.Now().Unix(),
 				FilesystemDatabase:    nil,
-				FileSystemAbstraction: NewEmptyFileSystemAbstraction(),
+				FileSystemAbstraction: emptyfs.NewEmptyFileSystemAbstraction(),
 				Filesystem:            fstype,
 				Closed:                false,
 			}, nil

+ 5 - 5
mod/filesystem/metadata/audio.go

@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"image"
 	"image/jpeg"
-	"os"
 	"path/filepath"
 
 	"github.com/dhowden/tag"
@@ -17,12 +16,13 @@ func generateThumbnailForAudio(fsh *filesystem.FileSystemHandler, cacheFolder st
 	if fsh.RequireBuffer {
 		return "", nil
 	}
+	fshAbs := fsh.FileSystemAbstraction
 	//This extension is supported by id4. Call to library
-	f, err := os.Open(file)
-	defer f.Close()
+	f, err := fshAbs.Open(file)
 	if err != nil {
 		return "", err
 	}
+	defer f.Close()
 	m, err := tag.ReadFrom(f)
 	if err != nil {
 		return "", err
@@ -37,7 +37,7 @@ func generateThumbnailForAudio(fsh *filesystem.FileSystemHandler, cacheFolder st
 		}
 
 		//Create an empty file
-		out, _ := os.Create(cacheFolder + filepath.Base(file) + ".jpg")
+		out, _ := fshAbs.Create(cacheFolder + filepath.Base(file) + ".jpg")
 		defer out.Close()
 
 		b := img.Bounds()
@@ -53,7 +53,7 @@ func generateThumbnailForAudio(fsh *filesystem.FileSystemHandler, cacheFolder st
 		}
 
 		//Crop out the center
-		croppedImg, err := cutter.Crop(m, cutter.Config{
+		croppedImg, _ := cutter.Crop(m, cutter.Config{
 			Width:  480,
 			Height: 480,
 			Mode:   cutter.Centered,

+ 9 - 39
mod/filesystem/metadata/folder.go

@@ -7,9 +7,7 @@ import (
 	"image/jpeg"
 	"image/png"
 	"log"
-	"os"
 	"path/filepath"
-	"strings"
 
 	"github.com/nfnt/resize"
 	"imuslab.com/arozos/mod/filesystem"
@@ -24,19 +22,20 @@ func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder s
 	if fsh.RequireBuffer {
 		return "", nil
 	}
+	fshAbs := fsh.FileSystemAbstraction
 	//Check if this folder has cache image folder
 	cacheFolderInsideThisFolder := filepath.Join(file, "/.metadata/.cache")
-	if !fileExists(cacheFolderInsideThisFolder) {
+	if !fshAbs.FileExists(cacheFolderInsideThisFolder) {
 		//This folder do not have a cache folder
 		return "", errors.New("No previewable files")
 	}
 
 	//Load the base template
-	if !fileExists("web/img/system/folder-preview.png") {
+	if !fshAbs.FileExists("web/img/system/folder-preview.png") {
 		//Missing system files. Skip rendering
 		return "", errors.New("Missing system template image file")
 	}
-	image1, err := os.Open("web/img/system/folder-preview.png")
+	image1, err := fshAbs.Open("web/img/system/folder-preview.png")
 	if err != nil {
 		return "", err
 	}
@@ -53,12 +52,12 @@ func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder s
 	draw.Draw(resultThumbnail, b, baseTemplate, image.ZP, draw.Over)
 
 	//Get cached file inside this folder, only include jpg (non folder)
-	contentCache, _ := wGlob(filepath.Join(cacheFolderInsideThisFolder, "/*.jpg"))
+	contentCache, _ := fshAbs.Glob(filepath.Join(cacheFolderInsideThisFolder, "/*.jpg"))
 
 	//Check if there are more than 1 file inside this folder that is cached
 	if len(contentCache) > 1 {
 		//More than 1 files. Render the image at the back
-		image2, err := os.Open(contentCache[1])
+		image2, err := fshAbs.Open(contentCache[1])
 		if err != nil {
 			return "", err
 		}
@@ -76,7 +75,7 @@ func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder s
 	}
 
 	//Render the top image
-	image3, err := os.Open(contentCache[0])
+	image3, err := fshAbs.Open(contentCache[0])
 	if err != nil {
 		return "", errors.New("failed to open: " + err.Error())
 	}
@@ -85,7 +84,7 @@ func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder s
 	if err != nil {
 		//Fail to decode the image. Try to remove the damaged iamge file
 		image3.Close()
-		os.Remove(contentCache[0])
+		fshAbs.Remove(contentCache[0])
 		log.Println("Failed to decode cahce image for: " + contentCache[0] + ". Removing thumbnail cache")
 		return "", errors.New("failed to decode: " + err.Error())
 	}
@@ -95,7 +94,7 @@ func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder s
 	resizedTopImage := resize.Resize(260, 260, topImage, resize.Lanczos3)
 	draw.Draw(resultThumbnail, resizedTopImage.Bounds().Add(topImageOffset), resizedTopImage, image.ZP, draw.Over)
 
-	outfile, err := os.Create(filepath.Join(cacheFolder, filepath.Base(file)+".png"))
+	outfile, err := fshAbs.Create(filepath.Join(cacheFolder, filepath.Base(file)+".png"))
 	if err != nil {
 		log.Fatalf("failed to create: %s", err)
 	}
@@ -105,32 +104,3 @@ func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder s
 	ctx, err := getImageAsBase64(fsh, cacheFolder+filepath.Base(file)+".png")
 	return ctx, err
 }
-
-//Special glob function to handle file name containing wildcard characters
-func wGlob(path string) ([]string, error) {
-	files, err := filepath.Glob(path)
-	if err != nil {
-		return []string{}, err
-	}
-
-	if strings.Contains(path, "[") == true || strings.Contains(path, "]") == true {
-		if len(files) == 0 {
-			//Handle reverse check. Replace all [ and ] with ?
-			newSearchPath := strings.ReplaceAll(path, "[", "?")
-			newSearchPath = strings.ReplaceAll(newSearchPath, "]", "?")
-			//Scan with all the similar structure except [ and ]
-			tmpFilelist, _ := filepath.Glob(newSearchPath)
-			for _, file := range tmpFilelist {
-				file = filepath.ToSlash(file)
-				if strings.Contains(file, filepath.ToSlash(filepath.Dir(path))) {
-					files = append(files, file)
-				}
-			}
-		}
-	}
-	//Convert all filepaths to slash
-	for i := 0; i < len(files); i++ {
-		files[i] = filepath.ToSlash(files[i])
-	}
-	return files, nil
-}

+ 4 - 2
mod/filesystem/metadata/image.go

@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"image"
 	"image/jpeg"
-	"os"
 	"path/filepath"
 
 	"github.com/nfnt/resize"
@@ -13,6 +12,9 @@ import (
 )
 
 func generateThumbnailForImage(fsh *filesystem.FileSystemHandler, cacheFolder string, file string, generateOnly bool) (string, error) {
+	if fsh.RequireBuffer {
+		return "", nil
+	}
 	fshAbs := fsh.FileSystemAbstraction
 	imageBytes, err := fshAbs.ReadFile(file)
 	if err != nil {
@@ -44,7 +46,7 @@ func generateThumbnailForImage(fsh *filesystem.FileSystemHandler, cacheFolder st
 	})
 
 	//Create the thumbnail
-	out, err := os.Create(cacheFolder + filepath.Base(file) + ".jpg")
+	out, err := fshAbs.Create(cacheFolder + filepath.Base(file) + ".jpg")
 	if err != nil {
 		return "", err
 	}

+ 5 - 2
mod/filesystem/metadata/metadata.go

@@ -41,7 +41,7 @@ func NewRenderHandler() *RenderHandler {
 //Build cache for all files (non recursive) for the given filepath
 func (rh *RenderHandler) BuildCacheForFolder(fsh *filesystem.FileSystemHandler, vpath string, username string) error {
 	fshAbs := fsh.FileSystemAbstraction
-	rpath, _ := fshAbs.RealPathToVirtualPath(vpath, username)
+	rpath, _ := fshAbs.VirtualPathToRealPath(vpath, username)
 
 	//Get a list of all files inside this path
 	files, err := fshAbs.Glob(filepath.ToSlash(filepath.Clean(rpath)) + "/*")
@@ -120,6 +120,9 @@ func (rh *RenderHandler) LoadCache(fsh *filesystem.FileSystemHandler, rpath stri
 			return ctx, err
 		}
 
+	} else if fsh.ReadOnly {
+		//Not exists, but this Fsh is read only. Return nothing
+		return "", errors.New("Cannot generate thumbnail on readonly file system")
 	} else {
 		//This file not exists yet. Check if it is being hold by another process already
 		if rh.fileIsBusy(rpath) {
@@ -220,7 +223,7 @@ func (rh *RenderHandler) HandleLoadCache(w http.ResponseWriter, r *http.Request,
 		oldc.(*websocket.Conn).Close()
 	}
 
-	files, err := specialGlob(fsh, targetPath+"/*")
+	files, err := fsh.FileSystemAbstraction.Glob(targetPath + "/*")
 	if err != nil {
 		w.WriteHeader(http.StatusInternalServerError)
 		w.Write([]byte("500 - Internal Server Error"))

+ 8 - 7
mod/filesystem/metadata/model.go

@@ -3,7 +3,6 @@ package metadata
 import (
 	"errors"
 	"image/jpeg"
-	"os"
 	"path/filepath"
 
 	"imuslab.com/arozos/mod/filesystem"
@@ -14,7 +13,9 @@ func generateThumbnailForModel(fsh *filesystem.FileSystemHandler, cacheFolder st
 	if fsh.RequireBuffer {
 		return "", nil
 	}
-	if !fileExists(file) {
+	fshAbs := fsh.FileSystemAbstraction
+
+	if !fshAbs.FileExists(file) {
 		//The user removed this file before the thumbnail is finished
 		return "", errors.New("Source not exists")
 	}
@@ -28,24 +29,24 @@ func generateThumbnailForModel(fsh *filesystem.FileSystemHandler, cacheFolder st
 		Height:          480,
 	})
 
-	img, err := r.RenderModel(file)
+	img, _ := r.RenderModel(file)
 	opt := jpeg.Options{
 		Quality: 90,
 	}
 
-	f, err := os.Create(outputFile)
+	f, err := fshAbs.Create(outputFile)
 	if err != nil {
 		return "", err
 	}
 
-	err = jpeg.Encode(f, img, &opt)
+	jpeg.Encode(f, img, &opt)
 	f.Close()
 
-	if !generateOnly && fileExists(outputFile) {
+	if !generateOnly && fshAbs.FileExists(outputFile) {
 		//return the image as well
 		ctx, err := getImageAsBase64(fsh, outputFile)
 		return ctx, err
-	} else if !fileExists(outputFile) {
+	} else if !fshAbs.FileExists(outputFile) {
 		return "", errors.New("Image generation failed")
 	}
 	return "", nil

+ 5 - 5
mod/filesystem/metadata/psd.go

@@ -4,7 +4,6 @@ import (
 	"errors"
 	"image"
 	"image/jpeg"
-	"os"
 	"path/filepath"
 
 	"github.com/nfnt/resize"
@@ -17,14 +16,15 @@ func generateThumbnailForPSD(fsh *filesystem.FileSystemHandler, cacheFolder stri
 	if fsh.RequireBuffer {
 		return "", nil
 	}
-	if !fileExists(file) {
+	fshAbs := fsh.FileSystemAbstraction
+	if !fshAbs.FileExists(file) {
 		//The user removed this file before the thumbnail is finished
 		return "", errors.New("Source not exists")
 	}
 
 	outputFile := cacheFolder + filepath.Base(file) + ".jpg"
 
-	f, err := os.Open(file)
+	f, err := fshAbs.Open(file)
 	if err != nil {
 		return "", err
 	}
@@ -56,7 +56,7 @@ func generateThumbnailForPSD(fsh *filesystem.FileSystemHandler, cacheFolder stri
 		Mode:   cutter.Centered,
 	})
 
-	outf, err := os.Create(outputFile)
+	outf, err := fshAbs.Create(outputFile)
 	if err != nil {
 		return "", err
 	}
@@ -69,7 +69,7 @@ func generateThumbnailForPSD(fsh *filesystem.FileSystemHandler, cacheFolder stri
 	}
 	outf.Close()
 
-	if !generateOnly && fileExists(outputFile) {
+	if !generateOnly && fshAbs.FileExists(outputFile) {
 		//return the image as well
 		ctx, err := getImageAsBase64(fsh, outputFile)
 		return ctx, err

+ 5 - 7
mod/filesystem/metadata/video.go

@@ -5,8 +5,6 @@ import (
 	"errors"
 	"image"
 	"image/jpeg"
-	"io/ioutil"
-	"os"
 	"os/exec"
 	"path/filepath"
 
@@ -38,9 +36,9 @@ func generateThumbnailForVideo(fsh *filesystem.FileSystemHandler, cacheFolder st
 		}
 
 		//Resize and crop the output image
-		if fileExists(outputFile) {
-			imageBytes, _ := ioutil.ReadFile(outputFile)
-			os.Remove(outputFile)
+		if fshAbs.FileExists(outputFile) {
+			imageBytes, _ := fshAbs.ReadFile(outputFile)
+			fshAbs.Remove(outputFile)
 			img, _, err := image.Decode(bytes.NewReader(imageBytes))
 			if err != nil {
 				//log.Println(err.Error())
@@ -54,7 +52,7 @@ func generateThumbnailForVideo(fsh *filesystem.FileSystemHandler, cacheFolder st
 
 				if err == nil {
 					//Write it back to the original file
-					out, _ := os.Create(outputFile)
+					out, _ := fshAbs.Create(outputFile)
 					jpeg.Encode(out, croppedImg, nil)
 					out.Close()
 
@@ -66,7 +64,7 @@ func generateThumbnailForVideo(fsh *filesystem.FileSystemHandler, cacheFolder st
 		}
 
 		//Finished
-		if !generateOnly && fileExists(outputFile) {
+		if !generateOnly && fshAbs.FileExists(outputFile) {
 			//return the image as well
 			ctx, err := getImageAsBase64(fsh, outputFile)
 			return ctx, err

+ 4 - 4
mod/filesystem/static.go

@@ -104,10 +104,10 @@ func GetIDFromVirtualPath(vpath string) (string, string, error) {
 	return vdID, path, nil
 }
 
-func GetFileDataFromPath(vpath string, realpath string, sizeRounding int) FileData {
-	fileSize := GetFileSize(realpath)
+func GetFileDataFromPath(fsh *FileSystemHandler, vpath string, realpath string, sizeRounding int) FileData {
+	fileSize := fsh.FileSystemAbstraction.GetFileSize(realpath)
 	displaySize := GetFileDisplaySize(fileSize, sizeRounding)
-	modtime, _ := GetModTime(realpath)
+	modtime, _ := fsh.FileSystemAbstraction.GetModTime(realpath)
 
 	var shortcutInfo *shortcut.ShortcutData = nil
 	if filepath.Ext(realpath) == ".shortcut" {
@@ -121,7 +121,7 @@ func GetFileDataFromPath(vpath string, realpath string, sizeRounding int) FileDa
 		Filename:    filepath.Base(realpath),
 		Filepath:    vpath,
 		Realpath:    filepath.ToSlash(realpath),
-		IsDir:       IsDir(realpath),
+		IsDir:       fsh.FileSystemAbstraction.IsDir(realpath),
 		Filesize:    fileSize,
 		Displaysize: displaySize,
 		ModTime:     modtime,

+ 5 - 0
mod/network/webdav/webdav.go

@@ -27,6 +27,8 @@ type Handler struct {
 	// Logger is an optional error logger. If non-nil, it will be called
 	// for all HTTP requests.
 	Logger func(*http.Request, error)
+	//External Event Listener for the webdev request
+	RequestEventListener func(path string)
 }
 
 func (h *Handler) stripPrefix(p string) (string, int, error) {
@@ -572,6 +574,9 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
 		return mw.write(makePropstatResponse(href, pstats))
 	}
 
+	if h.RequestEventListener != nil {
+		h.RequestEventListener(reqPath)
+	}
 	walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn)
 	closeErr := mw.close()
 	if walkErr != nil {

+ 137 - 9
mod/share/share.go

@@ -1052,25 +1052,153 @@ func validateShareModes(mode string) (bool, string, []string) {
 	return false, "", []string{}
 }
 
+func (s *Manager) Walk(subpath string, userinfo *user.User, fastWalkFunction func(filesystem.FileData) error) error {
+	//Resolve the subpath
+	usergroup := userinfo.GetUserPermissionGroupNames()
+	if subpath == "" {
+		//List root as a collections of shares
+		rootFileList := s.ListRootForUser(userinfo, usergroup)
+		for _, fileInRoot := range rootFileList {
+			fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(fileInRoot.Filepath)
+			if err != nil {
+				return err
+			}
+			runtimeRealpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(fileInRoot.Filepath, userinfo.Username)
+			if fsh.FileSystemAbstraction.IsDir(runtimeRealpath) {
+				//Walk it
+				err := fsh.FileSystemAbstraction.Walk(runtimeRealpath, func(path string, info os.FileInfo, err error) error {
+					relPath, err := filepath.Rel(runtimeRealpath, path)
+					if err != nil {
+						return err
+					}
+
+					thisVpath := filepath.ToSlash(filepath.Join(fileInRoot.Filepath, relPath))
+					thisFd := filesystem.GetFileDataFromPath(fsh, thisVpath, path, 2)
+					err = fastWalkFunction(thisFd)
+					if err != nil {
+						return err
+					}
+
+					return nil
+				})
+
+				return err
+			} else {
+				//Normal files
+				err := fastWalkFunction(fileInRoot)
+				if err != nil {
+					return err
+				}
+			}
+
+		}
+	} else {
+		//List realpath of the system
+
+		fmt.Println("RESOLVE", subpath)
+		/*
+			rpath, err := s.ResolveShareVrootPath(subpath, username, usergroup)
+			if err != nil {
+				return err
+			}
+
+			vpath := "share:/" + subpath
+			err = filepath.Walk(rpath, func(path string, info os.FileInfo, err error) error {
+
+				relPath, err := filepath.Rel(rpath, path)
+				if err != nil {
+					return err
+				}
+				thisVpath := filepath.ToSlash(filepath.Join(vpath, relPath))
+				thisFd := fs.GetFileDataFromPath(thisVpath, rpath, 2)
+				err = fastWalkFunction(thisFd)
+				if err != nil {
+					return err
+				}
+
+				return nil
+			})
+
+
+			return err
+		*/
+	}
+
+	return nil
+}
+
+func (s *Manager) ListRootForUser(userinfo *user.User, usergroup []string) []filesystem.FileData {
+	//Iterate through all shares in the system to see which of the share is user accessible
+	userAccessiableShare := []*shareEntry.ShareOption{}
+	s.options.ShareEntryTable.FileToUrlMap.Range(func(fp, so interface{}) bool {
+		thisShareOption := so.(*shareEntry.ShareOption)
+		if s.ShareIsValid(thisShareOption) && thisShareOption.IsAccessibleBy(userinfo.Username, usergroup) {
+			userAccessiableShare = append(userAccessiableShare, thisShareOption)
+		}
+		return true
+	})
+
+	results := []filesystem.FileData{}
+	for _, thisShareObject := range userAccessiableShare {
+		fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(thisShareObject.FileVirtualPath)
+		if err != nil {
+			continue
+		}
+		thisFile := filesystem.GetFileDataFromPath(fsh, "share:/"+thisShareObject.UUID+"/"+filepath.Base(thisShareObject.FileVirtualPath), thisShareObject.FileRealPath, 2)
+		if thisShareObject.Owner == userinfo.Username {
+			thisFile.IsShared = true
+		}
+
+		results = append(results, thisFile)
+	}
+
+	return results
+}
+
+func (s *Manager) ShareIsValid(thisShareOption *shareEntry.ShareOption) bool {
+	vpath := thisShareOption.FileVirtualPath
+	userinfo, _ := s.options.UserHandler.GetUserInfoFromUsername(thisShareOption.Owner)
+	fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
+	if err != nil {
+		return false
+	}
+
+	fshAbs := fsh.FileSystemAbstraction
+	rpath, _ := fshAbs.VirtualPathToRealPath(vpath, userinfo.Username)
+
+	if !fshAbs.FileExists(rpath) {
+		return false
+	}
+
+	return true
+}
+
+func (s *Manager) GetPathHashFromShare(thisShareOption *shareEntry.ShareOption) (string, error) {
+	vpath := thisShareOption.FileVirtualPath
+	userinfo, _ := s.options.UserHandler.GetUserInfoFromUsername(thisShareOption.Owner)
+	fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
+	if err != nil {
+		return "", err
+	}
+	pathHash := shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
+	return pathHash, nil
+}
+
 //Check and clear shares that its pointinf files no longe exists
 func (s *Manager) ValidateAndClearShares() {
 	//Iterate through all shares within the system
 	s.options.ShareEntryTable.FileToUrlMap.Range(func(k, v interface{}) bool {
 		thisShareOption := v.(*shareEntry.ShareOption)
-		vpath := thisShareOption.FileVirtualPath
-		userinfo, _ := s.options.UserHandler.GetUserInfoFromUsername(thisShareOption.Owner)
-		fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
+		pathHash, err := s.GetPathHashFromShare(thisShareOption)
 		if err != nil {
-			//The file system handler that provide this share file is gone. Skip this
+			//Unable to resolve path hash. Filesystem handler is gone?
+			s.options.ShareEntryTable.RemoveShareByUUID(thisShareOption.UUID)
 			return true
 		}
-		fshAbs := fsh.FileSystemAbstraction
-		thisVpath, _ := fshAbs.VirtualPathToRealPath(vpath, userinfo.Username)
-		pathHash := shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
-		if !fshAbs.FileExists(thisVpath) {
+		if !s.ShareIsValid(thisShareOption) {
 			//This share source file don't exists anymore. Remove it
 			s.options.ShareEntryTable.RemoveShareByPathHash(pathHash)
-			log.Println("*Share* Removing share to file: " + vpath + " as it no longer exists")
+			log.Println("*Share* Removing share to file: " + thisShareOption.FileRealPath + " as it no longer exists")
 		}
 		return true
 	})

+ 2 - 89
mod/share/shareEntry/shareEntry.go

@@ -5,7 +5,6 @@ import (
 	"encoding/hex"
 	"encoding/json"
 	"errors"
-	"os"
 	"path/filepath"
 	"strings"
 	"sync"
@@ -13,7 +12,6 @@ import (
 	uuid "github.com/satori/go.uuid"
 	"imuslab.com/arozos/mod/database"
 	"imuslab.com/arozos/mod/filesystem"
-	fs "imuslab.com/arozos/mod/filesystem"
 )
 
 /*
@@ -229,6 +227,7 @@ func (s *ShareEntryTable) ResolveShareOptionFromShareSubpath(subpath string) (*S
 	}
 }
 
+/*
 func (s *ShareEntryTable) ResolveShareVrootPath(subpath string, username string, usergroup []string) (string, error) {
 	//Get a list of accessible files from this user
 	subpathElements := strings.Split(filepath.ToSlash(filepath.Clean(subpath))[1:], "/")
@@ -260,93 +259,7 @@ func (s *ShareEntryTable) ResolveShareVrootPath(subpath string, username string,
 	return "", errors.New("Not implemented")
 }
 
-func (s *ShareEntryTable) Walk(subpath string, username string, usergroup []string, fastWalkFunction func(fs.FileData) error) error {
-	//Resolve the subpath
-	if subpath == "" {
-		//List root as a collections of shares
-		rootFileList := s.ListRootForUser(username, usergroup)
-		for _, fileInRoot := range rootFileList {
-			if fs.IsDir(fileInRoot.Realpath) {
-				//Walk it
-				err := filepath.Walk(fileInRoot.Realpath, func(path string, info os.FileInfo, err error) error {
-					relPath, err := filepath.Rel(fileInRoot.Realpath, path)
-					if err != nil {
-						return err
-					}
-
-					thisVpath := filepath.ToSlash(filepath.Join(fileInRoot.Filepath, relPath))
-					thisFd := fs.GetFileDataFromPath(thisVpath, path, 2)
-					err = fastWalkFunction(thisFd)
-					if err != nil {
-						return err
-					}
-					return nil
-				})
-
-				return err
-			} else {
-				//Normal files
-				err := fastWalkFunction(fileInRoot)
-				if err != nil {
-					return err
-				}
-			}
-
-		}
-	} else {
-		//List realpath of the system
-		rpath, err := s.ResolveShareVrootPath(subpath, username, usergroup)
-		if err != nil {
-			return err
-		}
-
-		vpath := "share:/" + subpath
-		err = filepath.Walk(rpath, func(path string, info os.FileInfo, err error) error {
-			relPath, err := filepath.Rel(rpath, path)
-			if err != nil {
-				return err
-			}
-			thisVpath := filepath.ToSlash(filepath.Join(vpath, relPath))
-			thisFd := fs.GetFileDataFromPath(thisVpath, rpath, 2)
-			err = fastWalkFunction(thisFd)
-			if err != nil {
-				return err
-			}
-			return nil
-		})
-
-		return err
-	}
-	return nil
-}
-
-func (s *ShareEntryTable) ListRootForUser(username string, usergroup []string) []fs.FileData {
-	//Iterate through all shares in the system to see which of the share is user accessible
-	userAccessiableShare := []*ShareOption{}
-	s.FileToUrlMap.Range(func(fp, so interface{}) bool {
-		fileRealpath := fp.(string)
-		thisShareOption := so.(*ShareOption)
-		if fs.FileExists(fileRealpath) {
-			if thisShareOption.IsAccessibleBy(username, usergroup) {
-				userAccessiableShare = append(userAccessiableShare, thisShareOption)
-			}
-		}
-		return true
-	})
-
-	results := []fs.FileData{}
-	for _, thisShareObject := range userAccessiableShare {
-		rpath := thisShareObject.FileRealPath
-		thisFile := fs.GetFileDataFromPath("share:/"+thisShareObject.UUID+"/"+filepath.Base(rpath), rpath, 2)
-		if thisShareObject.Owner == username {
-			thisFile.IsShared = true
-		}
-
-		results = append(results, thisFile)
-	}
-
-	return results
-}
+*/
 
 func GetPathHash(fsh *filesystem.FileSystemHandler, vpath string, username string) string {
 	fshAbs := fsh.FileSystemAbstraction

+ 88 - 0
mod/storage/webdav/fshAdapter.go

@@ -0,0 +1,88 @@
+package webdav
+
+import (
+	"context"
+	"errors"
+	"log"
+	"os"
+	"path/filepath"
+
+	"imuslab.com/arozos/mod/filesystem"
+	"imuslab.com/arozos/mod/network/webdav"
+)
+
+type FshWebDAVAdapter struct {
+	fsh      *filesystem.FileSystemHandler
+	username string
+	buffDir  string //The folder to put buffer files
+}
+
+func NewFshWebDAVAdapter(fsh *filesystem.FileSystemHandler, username string, buffDir string) *FshWebDAVAdapter {
+	return &FshWebDAVAdapter{
+		fsh,
+		username,
+		buffDir,
+	}
+}
+
+func (a *FshWebDAVAdapter) requestPathToRealPath(name string) (string, error) {
+	fullVpath := a.fsh.UUID + ":" + name
+	realRequestPath, err := a.fsh.FileSystemAbstraction.VirtualPathToRealPath(fullVpath, a.username)
+	if err != nil {
+		return "", err
+	}
+	return filepath.ToSlash(realRequestPath), nil
+}
+
+func (a *FshWebDAVAdapter) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
+	realRequestPath, err := a.requestPathToRealPath(name)
+	if err != nil {
+		return err
+	}
+	return a.fsh.FileSystemAbstraction.Mkdir(realRequestPath, perm)
+}
+func (a *FshWebDAVAdapter) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
+	//The name come in as the relative path of the request vpath (e.g. user:/Video/test.mp4 will get requested as /Video/test.mp4)
+	//Merge it into a proper vpath and perform abstraction path translation
+	realRequestPath, err := a.requestPathToRealPath(name)
+	if err != nil {
+		log.Println(err)
+		return nil, err
+	}
+
+	if a.fsh.RequireBuffer {
+		//Buffer the remote content to local for access
+		//WIP
+
+		return nil, errors.New("work in progress")
+	} else {
+		return a.fsh.FileSystemAbstraction.OpenFile(realRequestPath, flag, perm)
+	}
+}
+func (a *FshWebDAVAdapter) RemoveAll(ctx context.Context, name string) error {
+	realRequestPath, err := a.requestPathToRealPath(name)
+	if err != nil {
+		return err
+	}
+	return a.fsh.FileSystemAbstraction.RemoveAll(realRequestPath)
+}
+func (a *FshWebDAVAdapter) Rename(ctx context.Context, oldName, newName string) error {
+	realOldname, err := a.requestPathToRealPath(oldName)
+	if err != nil {
+		return err
+	}
+
+	realNewname, err := a.requestPathToRealPath(newName)
+	if err != nil {
+		return err
+	}
+
+	return a.fsh.FileSystemAbstraction.Rename(realOldname, realNewname)
+}
+func (a *FshWebDAVAdapter) Stat(ctx context.Context, name string) (os.FileInfo, error) {
+	realRequestPath, err := a.requestPathToRealPath(name)
+	if err != nil {
+		return nil, err
+	}
+	return a.fsh.FileSystemAbstraction.Stat(realRequestPath)
+}

+ 54 - 18
mod/storage/webdav/webdav.go

@@ -19,6 +19,9 @@ import (
 	"sync"
 	"time"
 
+	"imuslab.com/arozos/mod/filesystem"
+	"imuslab.com/arozos/mod/filesystem/hidden"
+	"imuslab.com/arozos/mod/filesystem/metadata"
 	"imuslab.com/arozos/mod/network/webdav"
 	"imuslab.com/arozos/mod/user"
 )
@@ -253,16 +256,25 @@ func (s *Server) HandleRequest(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	//Try to resolve the realpath of the vroot
-	realRoot, err := userinfo.VirtualPathToRealPath(reqRoot + ":/")
+	fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(reqRoot + ":/")
 	if err != nil {
-		log.Println(err.Error())
+		log.Println("[WebDAV] Failed to load File System Handler from request root: ", reqRoot+":/", err.Error())
 		http.Error(w, "Invalid ", http.StatusUnauthorized)
 		return
 	}
 
+	//Try to resolve the realpath of the vroot
+	/*
+		realRoot, err := userinfo.VirtualPathToRealPath(reqRoot + ":/")
+		if err != nil {
+			log.Println(err.Error())
+			http.Error(w, "Invalid ", http.StatusUnauthorized)
+			return
+		}
+	*/
+
 	//Ok. Check if the file server of this root already exists
-	fs := s.getFsFromRealRoot(realRoot, filepath.ToSlash(filepath.Join(s.prefix, reqRoot)))
+	fs := s.getFsFromRealRoot(fsh, userinfo.Username, filepath.ToSlash(filepath.Join(s.prefix, reqRoot)))
 
 	//Serve the content
 	fs.ServeHTTP(w, r)
@@ -287,21 +299,45 @@ func (s *Server) serveReadOnlyWebDav(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
-func (s *Server) getFsFromRealRoot(realRoot string, prefix string) *webdav.Handler {
-	tfs, ok := s.filesystems.Load(realRoot)
-	if !ok {
-		//This file system handle hasn't been created. Create it now
-		fs := &webdav.Handler{
-			Prefix:     prefix,
-			FileSystem: webdav.Dir(realRoot),
-			LockSystem: webdav.NewMemLS(),
-		}
+func (s *Server) getFsFromRealRoot(fsh *filesystem.FileSystemHandler, username string, prefix string) *webdav.Handler {
+	//Create a webdav adapter from the fsh
+	fshadapter := NewFshWebDAVAdapter(fsh, username, "./tmp/webdavBuff")
+	fs := &webdav.Handler{
+		Prefix:     prefix,
+		FileSystem: fshadapter,
+		LockSystem: webdav.NewMemLS(),
+	}
 
-		//Store the file system handler
-		s.filesystems.Store(realRoot, fs)
+	//Create event listener for the path request
+	fs.RequestEventListener = func(path string) {
+		//Generate thumbnail in the background if listed
+		vpath, _ := fsh.FileSystemAbstraction.RealPathToVirtualPath(path, username)
+		go func() {
+			isHidden, _ := hidden.IsHidden(vpath, false)
+			if !isHidden {
+				metadata.NewRenderHandler().BuildCacheForFolder(fsh, vpath, username)
+			}
 
-		return fs
-	} else {
-		return tfs.(*webdav.Handler)
+		}()
 	}
+
+	return fs
+	/*
+		tfs, ok := s.filesystems.Load(realRoot)
+		if !ok {
+			//This file system handle hasn't been created. Create it now
+			fs := &webdav.Handler{
+				Prefix:     prefix,
+				FileSystem: webdav.Dir(realRoot),
+				LockSystem: webdav.NewMemLS(),
+			}
+
+			//Store the file system handler
+			s.filesystems.Store(realRoot, fs)
+
+			return fs
+		} else {
+			return tfs.(*webdav.Handler)
+		}
+	*/
 }

+ 3 - 3
mod/storage/webdav/webdavWindowHandler.go

@@ -121,15 +121,15 @@ func (s *Server) HandleWindowClientAccess(w http.ResponseWriter, r *http.Request
 				return
 			}
 
-			realRoot, err := userinfo.VirtualPathToRealPath(vroot + ":/")
+			fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vroot + ":/")
 			if err != nil {
-				log.Println(err.Error())
+				log.Println("[WebDAV] Failed to load File System Handler from request root: ", err.Error())
 				http.Error(w, "Invalid ", http.StatusUnauthorized)
 				return
 			}
 
 			//Get and serve the file content
-			fs := s.getFsFromRealRoot(realRoot, filepath.ToSlash(filepath.Join(s.prefix, vroot)))
+			fs := s.getFsFromRealRoot(fsh, userinfo.Username, filepath.ToSlash(filepath.Join(s.prefix, vroot)))
 			fs.ServeHTTP(w, r)
 		}
 	}

+ 1 - 1
mod/user/directoryHandler.go

@@ -129,7 +129,7 @@ func (u *User) VirtualPathToRealPath(vpath string) (string, error) {
 			} else if storage.Hierarchy == "public" {
 				return filepath.ToSlash(filepath.Join(filepath.Clean(storage.Path), subpath)), nil
 			} else if storage.Hierarchy == "share" {
-				return (*u.parent.shareEntryTable).ResolveShareVrootPath(subpath, u.Username, u.GetUserPermissionGroupNames())
+				//return (*u.parent.shareEntryTable).ResolveShareVrootPath(subpath, u.Username, u.GetUserPermissionGroupNames())
 			} else {
 				return "", errors.New("Unknown Filesystem Handler Hierarchy")
 			}

+ 1 - 1
storage.go

@@ -104,7 +104,7 @@ func LoadBaseStoragePool() error {
 	webdh, err := fs.NewFileSystemHandler(fs.FileSystemOption{
 		Name:       "Loopback",
 		Uuid:       "loopback",
-		Path:       "http://192.168.1.214:8081/webdav/tmp",
+		Path:       "http://192.168.1.214:8081/webdav/user",
 		Access:     "readwrite",
 		Hierarchy:  "public",
 		Automount:  false,

+ 2 - 3
web/Music/functions/listSong.js

@@ -257,8 +257,8 @@ function handleUserRequest(){
                 sendJSONResp(JSON.stringify(rootInfo));
             }else{
                 //List information about other folders
-                var targetpath = decodeURIComponent(listdir)
-                var filelist = filelib.aglob(targetpath + "*")
+                var targetpath = decodeURIComponent(listdir);
+                var filelist = filelib.aglob(targetpath + "*");
                 var files = [];
                 var folders = [];
                 for (var j = 0; j < filelist.length; j++){
@@ -269,7 +269,6 @@ function handleUserRequest(){
                         if (IsSupportExt(ext)  && !IsMetaFile(filelist[j])){
                             files.push(filelist[j]);
                         }
-                        
                     }
                 }
 

+ 7 - 7
web/Video/backend/buildPlaylist.js

@@ -31,7 +31,7 @@ function scanPathForVideo(thisDir, thisStorageName){
     if (filelib.fileExists(thisDir + "Video/")){
         var walkPath = thisDir + "Video/";
         var folderList = filelib.walk(walkPath, "folder")
-
+        
         //Build the folder list base on the discovered video files
         var foldersThatContainsVideoFile = [];
         for (var i = 0; i < folderList.length; i++){
@@ -45,6 +45,7 @@ function scanPathForVideo(thisDir, thisStorageName){
             if (validFilesInThisPath > 0){
                 //This folder contain video file
                 foldersThatContainsVideoFile.push(thisFolderPath);
+                
             }
         }
 
@@ -114,12 +115,11 @@ function main(){
     filelib.mkdir("user:/Video");
 
     //Scan each of the storage devices for video files
-    for (var i =0; i < storages.length; i++){
-        if (storages[i].Filesystem == "virtual" || storages[i].Hierarchy == "backup"){
-            continue;
-        }
-        var thisDir = storages[i].UUID + ":/";
-        var thisStorageName = storages[i].Name;
+    var vroots = filelib.glob("/");
+    for (var i =0; i < vroots.length; i++){
+        var thisRoot = vroots[i];
+        var thisDir = thisRoot;
+        var thisStorageName = filelib.rootName(thisRoot);
         scanPathForVideo(thisDir, thisStorageName)
     }