Переглянути джерело

Added optimization on storage pool reload logic (wip)

Toby Chui 2 роки тому
батько
коміт
efd1d730cb

+ 1 - 0
mod/filesystem/abstractions/ftpfs/ftpfs.go

@@ -179,6 +179,7 @@ func (l FTPFSAbstraction) Stat(filename string) (os.FileInfo, error) {
 	return nil, arozfs.ErrNullOperation
 }
 func (l FTPFSAbstraction) Close() error {
+	time.Sleep(500 * time.Millisecond)
 	return nil
 }
 

+ 2 - 1
mod/filesystem/abstractions/sftpfs/sftpfs.go

@@ -202,11 +202,12 @@ func (s SFTPFileSystemAbstraction) Close() error {
 	if err != nil {
 		return err
 	}
-
+	time.Sleep(300 * time.Millisecond)
 	err = s.conn.Close()
 	if err != nil {
 		return err
 	}
+	time.Sleep(500 * time.Millisecond)
 	return nil
 }
 

+ 6 - 1
mod/filesystem/abstractions/smbfs/smbfs.go

@@ -38,6 +38,11 @@ type ServerMessageBlockFileSystemAbstraction struct {
 
 func NewServerMessageBlockFileSystemAbstraction(uuid string, hierarchy string, ipaddr string, rootShare string, username string, password string) (ServerMessageBlockFileSystemAbstraction, error) {
 	log.Println("[SMB-FS] Connecting to " + uuid + ":/ (" + ipaddr + ")")
+	//Patch the ip address if port not found
+	if !strings.Contains(ipaddr, ":") {
+		log.Println("[SMB-FS] Port not set. Using default SMB port (:445)")
+		ipaddr = ipaddr + ":445" //Default port for SMB
+	}
 	nd := net.Dialer{Timeout: 10 * time.Second}
 	conn, err := nd.Dial("tcp", ipaddr)
 	if err != nil {
@@ -168,7 +173,7 @@ func (a ServerMessageBlockFileSystemAbstraction) Close() error {
 	time.Sleep(300 * time.Millisecond)
 	conn := *(a.conn)
 	conn.Close()
-
+	time.Sleep(500 * time.Millisecond)
 	return nil
 }
 

+ 367 - 366
mod/filesystem/abstractions/webdavfs/webdavfs.go

@@ -1,366 +1,367 @@
-package webdavfs
-
-import (
-	"errors"
-	"io"
-	"io/fs"
-	"log"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strings"
-	"time"
-
-	"github.com/studio-b12/gowebdav"
-	"imuslab.com/arozos/mod/filesystem/arozfs"
-)
-
-/*
-	WebDAV Client
-
-	This script is design as a wrapper of the studio-b12/gowebdav module
-	that allow access to webdav network drive in ArozOS and allow arozos
-	cross-mounting each others
-
-*/
-
-type WebDAVFileSystem struct {
-	UUID      string
-	Hierarchy string
-	root      string
-	user      string
-	c         *gowebdav.Client
-}
-
-func NewWebDAVMount(UUID string, Hierarchy string, root string, user string, password string) (*WebDAVFileSystem, error) {
-	//Connect to webdav server
-	c := gowebdav.NewClient(root, user, password)
-	err := c.Connect()
-	if err != nil {
-		log.Println("[WebDAV FS] Unable to connect to remote: ", err.Error())
-		return nil, err
-	} else {
-		log.Println("[WebDAV FS] Connected to remote: " + root)
-	}
-	return &WebDAVFileSystem{
-		UUID:      UUID,
-		Hierarchy: Hierarchy,
-		c:         c,
-		root:      root,
-		user:      user,
-	}, nil
-}
-
-func (e WebDAVFileSystem) Chmod(filename string, mode os.FileMode) error {
-	return errors.New("filesystem type not supported")
-}
-func (e WebDAVFileSystem) Chown(filename string, uid int, gid int) error {
-	return errors.New("filesystem type not supported")
-}
-func (e WebDAVFileSystem) Chtimes(filename string, atime time.Time, mtime time.Time) error {
-	return errors.New("filesystem type not supported")
-}
-func (e WebDAVFileSystem) Create(filename string) (arozfs.File, error) {
-	return nil, errors.New("filesystem type not supported")
-}
-func (e WebDAVFileSystem) Mkdir(filename string, mode os.FileMode) error {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.Mkdir(filename, mode)
-}
-func (e WebDAVFileSystem) MkdirAll(filename string, mode os.FileMode) error {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.MkdirAll(filename, mode)
-}
-func (e WebDAVFileSystem) Name() string {
-	return ""
-}
-func (e WebDAVFileSystem) Open(filename string) (arozfs.File, error) {
-	return nil, errors.New("filesystem type not supported")
-}
-func (e WebDAVFileSystem) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.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 {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.Remove(filename)
-}
-func (e WebDAVFileSystem) RemoveAll(filename string) error {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.RemoveAll(filename)
-}
-func (e WebDAVFileSystem) Rename(oldname, newname string) error {
-	oldname = filterFilepath(filepath.ToSlash(filepath.Clean(oldname)))
-	newname = filterFilepath(filepath.ToSlash(filepath.Clean(newname)))
-	err := e.c.Rename(oldname, newname, true)
-	if err != nil {
-		//Unable to rename due to reverse proxy issue. Use Copy and Delete
-		f, err := e.c.ReadStream(oldname)
-		if err != nil {
-			return err
-		}
-
-		err = e.c.WriteStream(newname, f, 0775)
-		if err != nil {
-			return err
-		}
-		f.Close()
-		e.c.RemoveAll(oldname)
-	}
-	return nil
-}
-func (e WebDAVFileSystem) Stat(filename string) (os.FileInfo, error) {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.Stat(filename)
-}
-
-func (e WebDAVFileSystem) VirtualPathToRealPath(subpath string, username string) (string, error) {
-	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 = strings.TrimPrefix(subpath, e.UUID+":")
-	}
-
-	if e.Hierarchy == "user" {
-		return filepath.ToSlash(filepath.Clean(filepath.Join("users", username, subpath))), nil
-	} else if e.Hierarchy == "public" {
-		return filepath.ToSlash(filepath.Clean(subpath)), nil
-	}
-	return "", errors.New("unsupported filesystem hierarchy")
-
-}
-func (e WebDAVFileSystem) RealPathToVirtualPath(rpath string, username string) (string, error) {
-	rpath = filterFilepath(filepath.ToSlash(filepath.Clean(rpath)))
-	if e.Hierarchy == "user" && strings.HasPrefix(rpath, "/users/"+username) {
-		rpath = strings.TrimPrefix(rpath, "/users/"+username)
-	}
-	rpath = filepath.ToSlash(rpath)
-	if !strings.HasPrefix(rpath, "/") {
-		rpath = "/" + rpath
-	}
-	return e.UUID + ":" + rpath, nil
-}
-func (e WebDAVFileSystem) FileExists(filename string) bool {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	_, err := e.c.Stat(filename)
-	if os.IsNotExist(err) || err != nil {
-		return false
-	}
-
-	return true
-}
-func (e WebDAVFileSystem) IsDir(filename string) bool {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	s, err := e.c.Stat(filename)
-	if err != nil {
-		return false
-	}
-	return s.IsDir()
-}
-
-//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
-	}
-	chunks := strings.Split(strings.TrimPrefix(wildcard, "/"), "/")
-	results, err := e.globpath("/", chunks, 0)
-	return results, err
-
-}
-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
-	}
-
-	return s.Size()
-}
-func (e WebDAVFileSystem) GetModTime(filename string) (int64, error) {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	s, err := e.Stat(filename)
-	if err != nil {
-		return 0, err
-	}
-
-	return s.ModTime().Unix(), nil
-}
-func (e WebDAVFileSystem) WriteFile(filename string, content []byte, mode os.FileMode) error {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.Write(filename, content, mode)
-}
-func (e WebDAVFileSystem) ReadFile(filename string) ([]byte, error) {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	bytes, err := e.c.Read(filename)
-	if err != nil {
-		return []byte(""), err
-	}
-	return bytes, nil
-}
-func (e WebDAVFileSystem) ReadDir(filename string) ([]fs.DirEntry, error) {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	fis, err := e.c.ReadDir(filename)
-	if err != nil {
-		return []fs.DirEntry{}, err
-	}
-
-	dirEntires := []fs.DirEntry{}
-	for _, fi := range fis {
-		dirEntires = append(dirEntires, newDirEntryFromFileInfo(fi))
-	}
-	return dirEntires, nil
-}
-func (e WebDAVFileSystem) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.WriteStream(filename, stream, mode)
-
-}
-func (e WebDAVFileSystem) ReadStream(filename string) (io.ReadCloser, error) {
-	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
-	return e.c.ReadStream(filename)
-}
-
-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)
-}
-
-func (e WebDAVFileSystem) Close() error {
-	return nil
-}
-
-func (e WebDAVFileSystem) Heartbeat() error {
-	_, err := e.c.ReadDir("/")
-	return err
-}
-
-/*
-	Helper Functions
-*/
-
-func (e WebDAVFileSystem) walk(thisPath string, walkFun filepath.WalkFunc) error {
-	files, err := e.c.ReadDir(thisPath)
-	if err != nil {
-		return err
-	}
-
-	for _, file := range files {
-		thisFileFullPath := filepath.ToSlash(filepath.Join(thisPath, file.Name()))
-		if file.IsDir() {
-			err = walkFun(thisFileFullPath, file, nil)
-			if err != nil {
-				return err
-			}
-			err = e.walk(thisFileFullPath, walkFun)
-			if err != nil {
-				return err
-			}
-		} else {
-			err = walkFun(thisFileFullPath, file, nil)
-			if err != nil {
-				return err
-			}
-		}
-	}
-	return nil
-}
-
-func (e WebDAVFileSystem) globpath(currentPath string, pathSegments []string, depth int) ([]string, error) {
-	const pathSeparatorsLimit = 1000
-	if depth == pathSeparatorsLimit {
-		return nil, errors.New("bad pattern")
-	}
-
-	// Check pattern is well-formed.
-	if _, err := regexp.MatchString(wildCardToRegexp(strings.Join(pathSegments, "/")), ""); err != nil {
-		return nil, err
-	}
-
-	if len(pathSegments) == 0 {
-		//Reaching the bottom
-		return []string{}, nil
-	}
-
-	thisSegment := pathSegments[0]
-	if strings.Contains(thisSegment, "*") {
-		//Search for matching
-		matchPattern := currentPath + thisSegment
-		files, err := e.c.ReadDir(currentPath)
-		if err != nil {
-			return []string{}, nil
-		}
-
-		//Check which file in the currentPath matches the wildcard
-		matchedSubpaths := []string{}
-		for _, file := range files {
-			thisPath := currentPath + file.Name()
-			match, _ := regexp.MatchString(wildCardToRegexp(matchPattern), thisPath)
-			if match {
-				if file.IsDir() {
-					matchedSubpaths = append(matchedSubpaths, thisPath+"/")
-				} else {
-					matchedSubpaths = append(matchedSubpaths, thisPath)
-				}
-
-			}
-		}
-
-		if len(pathSegments[1:]) == 0 {
-			return matchedSubpaths, nil
-		}
-
-		//For each of the subpaths, do a globpath
-		matchingFilenames := []string{}
-		for _, subpath := range matchedSubpaths {
-			thisMatchedNames, _ := e.globpath(subpath, pathSegments[1:], depth+1)
-			matchingFilenames = append(matchingFilenames, thisMatchedNames...)
-		}
-		return matchingFilenames, nil
-	} else {
-		//Check folder exists
-		if e.FileExists(currentPath+thisSegment) && e.IsDir(currentPath+thisSegment) {
-			return e.globpath(currentPath+thisSegment+"/", pathSegments[1:], depth+1)
-		} else {
-			//Not matching
-			return []string{}, nil
-		}
-	}
-}
-
-func filterFilepath(rawpath string) string {
-	rawpath = strings.TrimSpace(rawpath)
-	if strings.HasPrefix(rawpath, "./") {
-		return rawpath[1:]
-	} 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()
-}
+package webdavfs
+
+import (
+	"errors"
+	"io"
+	"io/fs"
+	"log"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"time"
+
+	"github.com/studio-b12/gowebdav"
+	"imuslab.com/arozos/mod/filesystem/arozfs"
+)
+
+/*
+	WebDAV Client
+
+	This script is design as a wrapper of the studio-b12/gowebdav module
+	that allow access to webdav network drive in ArozOS and allow arozos
+	cross-mounting each others
+
+*/
+
+type WebDAVFileSystem struct {
+	UUID      string
+	Hierarchy string
+	root      string
+	user      string
+	c         *gowebdav.Client
+}
+
+func NewWebDAVMount(UUID string, Hierarchy string, root string, user string, password string) (*WebDAVFileSystem, error) {
+	//Connect to webdav server
+	c := gowebdav.NewClient(root, user, password)
+	err := c.Connect()
+	if err != nil {
+		log.Println("[WebDAV FS] Unable to connect to remote: ", err.Error())
+		return nil, err
+	} else {
+		log.Println("[WebDAV FS] Connected to remote: " + root)
+	}
+	return &WebDAVFileSystem{
+		UUID:      UUID,
+		Hierarchy: Hierarchy,
+		c:         c,
+		root:      root,
+		user:      user,
+	}, nil
+}
+
+func (e WebDAVFileSystem) Chmod(filename string, mode os.FileMode) error {
+	return errors.New("filesystem type not supported")
+}
+func (e WebDAVFileSystem) Chown(filename string, uid int, gid int) error {
+	return errors.New("filesystem type not supported")
+}
+func (e WebDAVFileSystem) Chtimes(filename string, atime time.Time, mtime time.Time) error {
+	return errors.New("filesystem type not supported")
+}
+func (e WebDAVFileSystem) Create(filename string) (arozfs.File, error) {
+	return nil, errors.New("filesystem type not supported")
+}
+func (e WebDAVFileSystem) Mkdir(filename string, mode os.FileMode) error {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.Mkdir(filename, mode)
+}
+func (e WebDAVFileSystem) MkdirAll(filename string, mode os.FileMode) error {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.MkdirAll(filename, mode)
+}
+func (e WebDAVFileSystem) Name() string {
+	return ""
+}
+func (e WebDAVFileSystem) Open(filename string) (arozfs.File, error) {
+	return nil, errors.New("filesystem type not supported")
+}
+func (e WebDAVFileSystem) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.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 {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.Remove(filename)
+}
+func (e WebDAVFileSystem) RemoveAll(filename string) error {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.RemoveAll(filename)
+}
+func (e WebDAVFileSystem) Rename(oldname, newname string) error {
+	oldname = filterFilepath(filepath.ToSlash(filepath.Clean(oldname)))
+	newname = filterFilepath(filepath.ToSlash(filepath.Clean(newname)))
+	err := e.c.Rename(oldname, newname, true)
+	if err != nil {
+		//Unable to rename due to reverse proxy issue. Use Copy and Delete
+		f, err := e.c.ReadStream(oldname)
+		if err != nil {
+			return err
+		}
+
+		err = e.c.WriteStream(newname, f, 0775)
+		if err != nil {
+			return err
+		}
+		f.Close()
+		e.c.RemoveAll(oldname)
+	}
+	return nil
+}
+func (e WebDAVFileSystem) Stat(filename string) (os.FileInfo, error) {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.Stat(filename)
+}
+
+func (e WebDAVFileSystem) VirtualPathToRealPath(subpath string, username string) (string, error) {
+	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 = strings.TrimPrefix(subpath, e.UUID+":")
+	}
+
+	if e.Hierarchy == "user" {
+		return filepath.ToSlash(filepath.Clean(filepath.Join("users", username, subpath))), nil
+	} else if e.Hierarchy == "public" {
+		return filepath.ToSlash(filepath.Clean(subpath)), nil
+	}
+	return "", errors.New("unsupported filesystem hierarchy")
+
+}
+func (e WebDAVFileSystem) RealPathToVirtualPath(rpath string, username string) (string, error) {
+	rpath = filterFilepath(filepath.ToSlash(filepath.Clean(rpath)))
+	if e.Hierarchy == "user" && strings.HasPrefix(rpath, "/users/"+username) {
+		rpath = strings.TrimPrefix(rpath, "/users/"+username)
+	}
+	rpath = filepath.ToSlash(rpath)
+	if !strings.HasPrefix(rpath, "/") {
+		rpath = "/" + rpath
+	}
+	return e.UUID + ":" + rpath, nil
+}
+func (e WebDAVFileSystem) FileExists(filename string) bool {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	_, err := e.c.Stat(filename)
+	if os.IsNotExist(err) || err != nil {
+		return false
+	}
+
+	return true
+}
+func (e WebDAVFileSystem) IsDir(filename string) bool {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	s, err := e.c.Stat(filename)
+	if err != nil {
+		return false
+	}
+	return s.IsDir()
+}
+
+//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
+	}
+	chunks := strings.Split(strings.TrimPrefix(wildcard, "/"), "/")
+	results, err := e.globpath("/", chunks, 0)
+	return results, err
+
+}
+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
+	}
+
+	return s.Size()
+}
+func (e WebDAVFileSystem) GetModTime(filename string) (int64, error) {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	s, err := e.Stat(filename)
+	if err != nil {
+		return 0, err
+	}
+
+	return s.ModTime().Unix(), nil
+}
+func (e WebDAVFileSystem) WriteFile(filename string, content []byte, mode os.FileMode) error {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.Write(filename, content, mode)
+}
+func (e WebDAVFileSystem) ReadFile(filename string) ([]byte, error) {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	bytes, err := e.c.Read(filename)
+	if err != nil {
+		return []byte(""), err
+	}
+	return bytes, nil
+}
+func (e WebDAVFileSystem) ReadDir(filename string) ([]fs.DirEntry, error) {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	fis, err := e.c.ReadDir(filename)
+	if err != nil {
+		return []fs.DirEntry{}, err
+	}
+
+	dirEntires := []fs.DirEntry{}
+	for _, fi := range fis {
+		dirEntires = append(dirEntires, newDirEntryFromFileInfo(fi))
+	}
+	return dirEntires, nil
+}
+func (e WebDAVFileSystem) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.WriteStream(filename, stream, mode)
+
+}
+func (e WebDAVFileSystem) ReadStream(filename string) (io.ReadCloser, error) {
+	filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
+	return e.c.ReadStream(filename)
+}
+
+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)
+}
+
+func (e WebDAVFileSystem) Close() error {
+	time.Sleep(500 * time.Millisecond)
+	return nil
+}
+
+func (e WebDAVFileSystem) Heartbeat() error {
+	_, err := e.c.ReadDir("/")
+	return err
+}
+
+/*
+	Helper Functions
+*/
+
+func (e WebDAVFileSystem) walk(thisPath string, walkFun filepath.WalkFunc) error {
+	files, err := e.c.ReadDir(thisPath)
+	if err != nil {
+		return err
+	}
+
+	for _, file := range files {
+		thisFileFullPath := filepath.ToSlash(filepath.Join(thisPath, file.Name()))
+		if file.IsDir() {
+			err = walkFun(thisFileFullPath, file, nil)
+			if err != nil {
+				return err
+			}
+			err = e.walk(thisFileFullPath, walkFun)
+			if err != nil {
+				return err
+			}
+		} else {
+			err = walkFun(thisFileFullPath, file, nil)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func (e WebDAVFileSystem) globpath(currentPath string, pathSegments []string, depth int) ([]string, error) {
+	const pathSeparatorsLimit = 1000
+	if depth == pathSeparatorsLimit {
+		return nil, errors.New("bad pattern")
+	}
+
+	// Check pattern is well-formed.
+	if _, err := regexp.MatchString(wildCardToRegexp(strings.Join(pathSegments, "/")), ""); err != nil {
+		return nil, err
+	}
+
+	if len(pathSegments) == 0 {
+		//Reaching the bottom
+		return []string{}, nil
+	}
+
+	thisSegment := pathSegments[0]
+	if strings.Contains(thisSegment, "*") {
+		//Search for matching
+		matchPattern := currentPath + thisSegment
+		files, err := e.c.ReadDir(currentPath)
+		if err != nil {
+			return []string{}, nil
+		}
+
+		//Check which file in the currentPath matches the wildcard
+		matchedSubpaths := []string{}
+		for _, file := range files {
+			thisPath := currentPath + file.Name()
+			match, _ := regexp.MatchString(wildCardToRegexp(matchPattern), thisPath)
+			if match {
+				if file.IsDir() {
+					matchedSubpaths = append(matchedSubpaths, thisPath+"/")
+				} else {
+					matchedSubpaths = append(matchedSubpaths, thisPath)
+				}
+
+			}
+		}
+
+		if len(pathSegments[1:]) == 0 {
+			return matchedSubpaths, nil
+		}
+
+		//For each of the subpaths, do a globpath
+		matchingFilenames := []string{}
+		for _, subpath := range matchedSubpaths {
+			thisMatchedNames, _ := e.globpath(subpath, pathSegments[1:], depth+1)
+			matchingFilenames = append(matchingFilenames, thisMatchedNames...)
+		}
+		return matchingFilenames, nil
+	} else {
+		//Check folder exists
+		if e.FileExists(currentPath+thisSegment) && e.IsDir(currentPath+thisSegment) {
+			return e.globpath(currentPath+thisSegment+"/", pathSegments[1:], depth+1)
+		} else {
+			//Not matching
+			return []string{}, nil
+		}
+	}
+}
+
+func filterFilepath(rawpath string) string {
+	rawpath = strings.TrimSpace(rawpath)
+	if strings.HasPrefix(rawpath, "./") {
+		return rawpath[1:]
+	} 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()
+}

+ 3 - 2
mod/storage/storage.go

@@ -130,7 +130,8 @@ func (s *StoragePool) DetachFsHandler(uuid string) {
 func (s *StoragePool) Close() {
 	//For each storage pool, close it
 	for _, fsh := range s.Storages {
-		fsh.Close()
+		if !fsh.Closed {
+			fsh.Close()
+		}
 	}
-
 }

+ 7 - 1
storage.pool.go

@@ -417,7 +417,13 @@ func HandleStoragePoolReload(w http.ResponseWriter, r *http.Request) {
 			//Pool should be exists. Close it
 			pg := permissionHandler.GetPermissionGroupByName(pool)
 
-			pg.StoragePool.Close()
+			for _, fsh := range pg.StoragePool.Storages {
+				//Close the fsh if it is not a bridged one
+				isBridged, _ := bridgeManager.IsBridgedFSH(fsh.UUID, pg.Name)
+				if !isBridged && !fsh.Closed {
+					fsh.Close()
+				}
+			}
 
 			//Create an empty pool for this permission group
 			newEmptyPool := storage.StoragePool{}

+ 0 - 6
web/SystemAO/file_system/file_explorer.html

@@ -1919,12 +1919,6 @@
                     protocol = "ws://";
                 }
 
-                if (path == "share:/"){
-                    //Share root do not contains any actual folder. Fallback to file based rendering
-                    startFallbackThumbnailLoader();
-                    return
-                }
-
                 var port = window.location.port;
                 if (window.location.port == ""){
                     if (location.protocol !== 'https:') {

+ 164 - 0
web/SystemAO/info/gomod-license.csv

@@ -0,0 +1,164 @@
+cloud.google.com/go/compute/metadata,https://github.com/googleapis/google-cloud-go/blob/compute/v1.9.0/compute/LICENSE,Apache-2.0
+github.com/Microsoft/go-winio,https://github.com/Microsoft/go-winio/blob/v0.5.2/LICENSE,MIT
+github.com/ProtonMail/go-crypto,https://github.com/ProtonMail/go-crypto/blob/4b6e5c587895/LICENSE,BSD-3-Clause
+github.com/andybalholm/brotli,https://github.com/andybalholm/brotli/blob/v1.0.4/LICENSE,MIT
+github.com/boltdb/bolt,https://github.com/boltdb/bolt/blob/v1.3.1/LICENSE,MIT
+github.com/cenkalti/backoff,https://github.com/cenkalti/backoff/blob/v2.2.1/LICENSE,MIT
+github.com/cloudflare/circl,https://github.com/cloudflare/circl/blob/v1.2.0/LICENSE,BSD-3-Clause
+github.com/dhowden/tag,https://github.com/dhowden/tag/blob/adf36e896086/LICENSE,BSD-2-Clause
+github.com/disintegration/imaging,https://github.com/disintegration/imaging/blob/v1.6.2/LICENSE,MIT
+github.com/dsnet/compress,https://github.com/dsnet/compress/blob/f66993602bf5/LICENSE.md,BSD-3-Clause
+github.com/emirpasic/gods,https://github.com/emirpasic/gods/blob/v1.18.1/LICENSE,BSD-2-Clause
+github.com/fclairamb/ftpserverlib,https://github.com/fclairamb/ftpserverlib/blob/v0.19.1/license.txt,MIT
+github.com/fclairamb/go-log,https://github.com/fclairamb/go-log/blob/v0.4.1/license.txt,MIT
+github.com/fogleman/fauxgl,https://github.com/fogleman/fauxgl/blob/27cddc103802/LICENSE.md,MIT
+github.com/fogleman/simplify,https://github.com/fogleman/simplify/blob/d32f302d5046/LICENSE.md,MIT
+github.com/gabriel-vasile/mimetype,https://github.com/gabriel-vasile/mimetype/blob/v1.4.1/LICENSE,MIT
+github.com/geoffgarside/ber,https://github.com/geoffgarside/ber/blob/v1.1.0/LICENSE,BSD-3-Clause
+github.com/go-git/gcfg,https://github.com/go-git/gcfg/blob/v1.5.0/LICENSE,BSD-3-Clause
+github.com/go-git/go-billy/v5,https://github.com/go-git/go-billy/blob/v5.3.1/LICENSE,Apache-2.0
+github.com/go-git/go-git/v5,https://github.com/go-git/go-git/blob/v5.4.2/LICENSE,Apache-2.0
+github.com/go-ldap/ldap,https://github.com/go-ldap/ldap/blob/v3.0.3/LICENSE,MIT
+github.com/golang/freetype,Unknown,Unknown
+github.com/golang/freetype/raster,Unknown,Unknown
+github.com/golang/freetype/truetype,Unknown,Unknown
+github.com/golang/snappy,https://github.com/golang/snappy/blob/v0.0.4/LICENSE,BSD-3-Clause
+github.com/google/uuid,https://github.com/google/uuid/blob/v1.3.0/LICENSE,BSD-3-Clause
+github.com/gorilla/securecookie,https://github.com/gorilla/securecookie/blob/v1.1.1/LICENSE,BSD-3-Clause
+github.com/gorilla/sessions,https://github.com/gorilla/sessions/blob/v1.2.1/LICENSE,BSD-3-Clause
+github.com/gorilla/websocket,https://github.com/gorilla/websocket/blob/v1.5.0/LICENSE,BSD-2-Clause
+github.com/grandcat/zeroconf,https://github.com/grandcat/zeroconf/blob/v1.0.0/LICENSE,MIT
+github.com/hashicorp/errwrap,https://github.com/hashicorp/errwrap/blob/v1.1.0/LICENSE,MPL-2.0
+github.com/hashicorp/go-multierror,https://github.com/hashicorp/go-multierror/blob/v1.1.1/LICENSE,MPL-2.0
+github.com/hirochachacha/go-smb2,https://github.com/hirochachacha/go-smb2/blob/v1.1.0/LICENSE,BSD-2-Clause
+github.com/imdario/mergo,https://github.com/imdario/mergo/blob/v0.3.13/LICENSE,BSD-3-Clause
+github.com/jbenet/go-context/io,https://github.com/jbenet/go-context/blob/d14ea06fba99/LICENSE,MIT
+github.com/jlaffaye/ftp,https://github.com/jlaffaye/ftp/blob/v0.1.0/LICENSE,ISC
+github.com/kevinburke/ssh_config,https://github.com/kevinburke/ssh_config/blob/v1.2.0/LICENSE,MIT
+github.com/klauspost/compress,https://github.com/klauspost/compress/blob/v1.15.9/LICENSE,Apache-2.0
+github.com/klauspost/compress/internal/snapref,https://github.com/klauspost/compress/blob/v1.15.9/internal\snapref\LICENSE,BSD-3-Clause
+github.com/klauspost/compress/zstd/internal/xxhash,https://github.com/klauspost/compress/blob/v1.15.9/zstd\internal\xxhash\LICENSE.txt,MIT
+github.com/klauspost/pgzip,https://github.com/klauspost/pgzip/blob/v1.2.5/LICENSE,MIT
+github.com/koron/go-ssdp,https://github.com/koron/go-ssdp/blob/v0.0.3/LICENSE,MIT
+github.com/kr/fs,https://github.com/kr/fs/blob/v0.1.0/LICENSE,BSD-3-Clause
+github.com/mholt/archiver/v3,https://github.com/mholt/archiver/blob/v3.5.1/LICENSE,MIT
+github.com/miekg/dns,https://github.com/miekg/dns/blob/v1.1.50/LICENSE,BSD-3-Clause
+github.com/mitchellh/go-homedir,https://github.com/mitchellh/go-homedir/blob/v1.1.0/LICENSE,MIT
+github.com/nfnt/resize,https://github.com/nfnt/resize/blob/83c6a9932646/LICENSE,ISC
+github.com/nwaples/rardecode,https://github.com/nwaples/rardecode/blob/v1.1.3/LICENSE,BSD-2-Clause
+github.com/oliamb/cutter,https://github.com/oliamb/cutter/blob/v0.2.2/LICENSE,MIT
+github.com/oov/psd,https://github.com/oov/psd/blob/5db5eafcecbb/LICENSE,MIT
+github.com/pierrec/lz4/v4,https://github.com/pierrec/lz4/blob/v4.1.15/LICENSE,BSD-3-Clause
+github.com/pkg/sftp,https://github.com/pkg/sftp/blob/v1.13.5/LICENSE,BSD-2-Clause
+github.com/robertkrimen/otto,https://github.com/robertkrimen/otto/blob/b87d35c0b86f/LICENSE,MIT
+github.com/satori/go.uuid,https://github.com/satori/go.uuid/blob/v1.2.0/LICENSE,MIT
+github.com/sergi/go-diff/diffmatchpatch,https://github.com/sergi/go-diff/blob/v1.2.0/LICENSE,MIT
+github.com/spf13/afero,https://github.com/spf13/afero/blob/v1.9.2/LICENSE.txt,Apache-2.0
+github.com/studio-b12/gowebdav,https://github.com/studio-b12/gowebdav/blob/c7b1ff8a5e62/LICENSE,BSD-3-Clause
+github.com/tidwall/pretty,https://github.com/tidwall/pretty/blob/v1.2.0/LICENSE,MIT
+github.com/ulikunitz/xz,https://github.com/ulikunitz/xz/blob/v0.5.10/LICENSE,BSD-3-Clause
+github.com/valyala/bytebufferpool,https://github.com/valyala/bytebufferpool/blob/v1.0.0/LICENSE,MIT
+github.com/valyala/fasttemplate,https://github.com/valyala/fasttemplate/blob/v1.2.1/LICENSE,MIT
+github.com/xanzy/ssh-agent,https://github.com/xanzy/ssh-agent/blob/v0.3.2/LICENSE,Apache-2.0
+github.com/xi2/xz,Unknown,Unknown
+gitlab.com/NebulousLabs/fastrand,https://gitlab.com/NebulousLabs/fastrand/blob/603482d69e40/LICENSE,MIT
+gitlab.com/NebulousLabs/go-upnp,https://gitlab.com/NebulousLabs/go-upnp/blob/11da932010b6/LICENSE,MIT
+gitlab.com/NebulousLabs/go-upnp/goupnp,https://gitlab.com/NebulousLabs/go-upnp/blob/11da932010b6/goupnp\LICENSE,BSD-2-Clause
+golang.org/x/crypto,https://cs.opensource.google/go/x/crypto/+/v0.3.0:LICENSE,BSD-3-Clause
+golang.org/x/image,https://cs.opensource.google/go/x/image/+/e7cb9697:LICENSE,BSD-3-Clause
+golang.org/x/net,https://cs.opensource.google/go/x/net/+/v0.2.0:LICENSE,BSD-3-Clause
+golang.org/x/oauth2,https://cs.opensource.google/go/x/oauth2/+/0ebed06d:LICENSE,BSD-3-Clause
+golang.org/x/sync/syncmap,https://cs.opensource.google/go/x/sync/+/7fc1605a:LICENSE,BSD-3-Clause
+golang.org/x/sys,https://cs.opensource.google/go/x/sys/+/v0.2.0:LICENSE,BSD-3-Clause
+golang.org/x/text,https://cs.opensource.google/go/x/text/+/v0.4.0:LICENSE,BSD-3-Clause
+gopkg.in/asn1-ber.v1,https://github.com/go-asn1-ber/asn1-ber/blob/f715ec2f112d/LICENSE,MIT
+gopkg.in/sourcemap.v1,https://github.com/go-sourcemap/sourcemap/blob/v1.0.5/LICENSE,BSD-2-Clause
+gopkg.in/warnings.v0,https://github.com/go-warnings/warnings/blob/v0.1.2/LICENSE,BSD-2-Clause
+imuslab.com/arozos,Unknown,Unknown
+imuslab.com/arozos/mod/agi,Unknown,Unknown
+imuslab.com/arozos/mod/apt,Unknown,Unknown
+imuslab.com/arozos/mod/auth,Unknown,Unknown
+imuslab.com/arozos/mod/auth/accesscontrol,Unknown,Unknown
+imuslab.com/arozos/mod/auth/accesscontrol/blacklist,Unknown,Unknown
+imuslab.com/arozos/mod/auth/accesscontrol/whitelist,Unknown,Unknown
+imuslab.com/arozos/mod/auth/authlogger,Unknown,Unknown
+imuslab.com/arozos/mod/auth/autologin,Unknown,Unknown
+imuslab.com/arozos/mod/auth/explogin,Unknown,Unknown
+imuslab.com/arozos/mod/auth/ldap,Unknown,Unknown
+imuslab.com/arozos/mod/auth/ldap/ldapreader,Unknown,Unknown
+imuslab.com/arozos/mod/auth/oauth2,Unknown,Unknown
+imuslab.com/arozos/mod/auth/oauth2/syncdb,Unknown,Unknown
+imuslab.com/arozos/mod/auth/register,Unknown,Unknown
+imuslab.com/arozos/mod/cluster/wakeonlan,Unknown,Unknown
+imuslab.com/arozos/mod/compatibility,Unknown,Unknown
+imuslab.com/arozos/mod/console,Unknown,Unknown
+imuslab.com/arozos/mod/database,Unknown,Unknown
+imuslab.com/arozos/mod/disk/diskcapacity,Unknown,Unknown
+imuslab.com/arozos/mod/disk/diskcapacity/dftool,Unknown,Unknown
+imuslab.com/arozos/mod/disk/diskmg,Unknown,Unknown
+imuslab.com/arozos/mod/disk/diskspace,Unknown,Unknown
+imuslab.com/arozos/mod/disk/smart,Unknown,Unknown
+imuslab.com/arozos/mod/disk/sortfile,Unknown,Unknown
+imuslab.com/arozos/mod/fileservers,Unknown,Unknown
+imuslab.com/arozos/mod/fileservers/servers/dirserv,Unknown,Unknown
+imuslab.com/arozos/mod/fileservers/servers/ftpserv,Unknown,Unknown
+imuslab.com/arozos/mod/fileservers/servers/sftpserv,Unknown,Unknown
+imuslab.com/arozos/mod/fileservers/servers/webdavserv,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/abstractions/ftpfs,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/abstractions/localfs,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/abstractions/sftpfs,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/abstractions/smbfs,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/abstractions/webdavfs,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/arozfs,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/fspermission,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/fssort,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/fuzzy,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/hidden,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/localversion,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/metadata,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/renderer,Unknown,Unknown
+imuslab.com/arozos/mod/filesystem/shortcut,Unknown,Unknown
+imuslab.com/arozos/mod/info/hardwareinfo,Unknown,Unknown
+imuslab.com/arozos/mod/info/logger,Unknown,Unknown
+imuslab.com/arozos/mod/info/logviewer,Unknown,Unknown
+imuslab.com/arozos/mod/info/usageinfo,Unknown,Unknown
+imuslab.com/arozos/mod/iot,Unknown,Unknown
+imuslab.com/arozos/mod/iot/hds,Unknown,Unknown
+imuslab.com/arozos/mod/iot/hdsv2,Unknown,Unknown
+imuslab.com/arozos/mod/iot/sonoff_s2x,Unknown,Unknown
+imuslab.com/arozos/mod/modules,Unknown,Unknown
+imuslab.com/arozos/mod/network,Unknown,Unknown
+imuslab.com/arozos/mod/network/gzipmiddleware,Unknown,Unknown
+imuslab.com/arozos/mod/network/mdns,Unknown,Unknown
+imuslab.com/arozos/mod/network/neighbour,Unknown,Unknown
+imuslab.com/arozos/mod/network/netstat,Unknown,Unknown
+imuslab.com/arozos/mod/network/reverseproxy,Unknown,MIT
+imuslab.com/arozos/mod/network/ssdp,Unknown,Unknown
+imuslab.com/arozos/mod/network/upnp,Unknown,Unknown
+imuslab.com/arozos/mod/network/webdav,Unknown,BSD-3-Clause
+imuslab.com/arozos/mod/network/websocket,Unknown,Unknown
+imuslab.com/arozos/mod/network/websocketproxy,Unknown,MIT
+imuslab.com/arozos/mod/network/wifi,Unknown,Unknown
+imuslab.com/arozos/mod/neuralnet,Unknown,Unknown
+imuslab.com/arozos/mod/notification,Unknown,Unknown
+imuslab.com/arozos/mod/notification/agents/smtpn,Unknown,Unknown
+imuslab.com/arozos/mod/permission,Unknown,Unknown
+imuslab.com/arozos/mod/prouter,Unknown,Unknown
+imuslab.com/arozos/mod/quota,Unknown,Unknown
+imuslab.com/arozos/mod/security/csrf,Unknown,Unknown
+imuslab.com/arozos/mod/share,Unknown,Unknown
+imuslab.com/arozos/mod/share/shareEntry,Unknown,Unknown
+imuslab.com/arozos/mod/storage,Unknown,Unknown
+imuslab.com/arozos/mod/storage/bridge,Unknown,Unknown
+imuslab.com/arozos/mod/storage/du,Unknown,Unknown
+imuslab.com/arozos/mod/storage/ftp,Unknown,Unknown
+imuslab.com/arozos/mod/storage/sftpserver,Unknown,Unknown
+imuslab.com/arozos/mod/storage/webdav,Unknown,Unknown
+imuslab.com/arozos/mod/subservice,Unknown,Unknown
+imuslab.com/arozos/mod/time/nightly,Unknown,Unknown
+imuslab.com/arozos/mod/time/scheduler,Unknown,Unknown
+imuslab.com/arozos/mod/updates,Unknown,Unknown
+imuslab.com/arozos/mod/user,Unknown,Unknown
+imuslab.com/arozos/mod/utils,Unknown,Unknown
+imuslab.com/arozos/mod/www,Unknown,Unknown

+ 42 - 15
web/SystemAO/storage/fshedit.html

@@ -101,7 +101,7 @@
             </div>
             <div class="field">
                 <label>Path</label>
-                <input type="text" name="path" placeholder="e.g. /media/mydrive">
+                <input type="text" name="path" placeholder="e.g. /media/mydrive" onchange="checkPathProtocol(this);">
             </div>
             <div class="field">
                 <label>Access Permission</label>
@@ -132,20 +132,20 @@
             <div class="field">
                 <label>Filesystem Type</label>
                 <div id="fstype" class="ui selection dropdown">
-                <input type="hidden" name="filesystem" value="ntfs" onchange="handleFileSystemTypeChange(this.value);">
-                <i class="dropdown icon"></i>
-                <div class="default text">Filesystem Type</div>
-                <div class="menu">
-                    <div class="item" data-value="ext4">EXT4</div>
-                    <!-- <div class="item" data-value="ext3">EXT3</div> -->
-                    <div class="item" data-value="ntfs">NTFS</div>
-                    <div class="item" data-value="vfat">VFAT</div>
-                    <div class="item" data-value="fat">FAT</div>
-                    <div class="item" data-value="webdav">WebDAV</div>
-                    <div class="item" data-value="smb">SMB</div>
-                    <div class="item" data-value="ftp">FTP</div>
-                    <div class="item" data-value="sftp">SFTP</div>
-                </div>
+                    <input type="hidden" name="filesystem" value="ntfs" onchange="handleFileSystemTypeChange(this.value);">
+                    <i class="dropdown icon"></i>
+                    <div class="default text">Filesystem Type</div>
+                    <div class="menu">
+                        <div class="item" data-value="ext4">EXT4</div>
+                        <!-- <div class="item" data-value="ext3">EXT3</div> -->
+                        <div class="item" data-value="ntfs">NTFS</div>
+                        <div class="item" data-value="vfat">VFAT</div>
+                        <div class="item" data-value="fat">FAT</div>
+                        <div class="item" data-value="webdav">WebDAV</div>
+                        <div class="item" data-value="smb">SMB</div>
+                        <div class="item" data-value="ftp">FTP</div>
+                        <div class="item" data-value="sftp">SFTP</div>
+                    </div>
                 </div>
             </div>
             <div class="localfs">
@@ -268,6 +268,33 @@
             }
         }
 
+        function checkPathProtocol(object){
+            var newPath = $(object).val();
+            if (newPath.startsWith("http://") || newPath.startsWith("https://")){
+                //WebDAV
+                $("#fstype").dropdown("set selected", "webdav");
+                newPath = newPath.replace("http://", "");
+                newPath = newPath.replace("https://", "");
+                $(object).val(newPath);
+            }else if (newPath.startsWith("ftp://")){
+                //FTP
+                $("#fstype").dropdown("set selected", "ftp");
+                newPath = newPath.replace("ftp://", "");
+                $(object).val(newPath);
+            }else if (newPath.startsWith("sftp://")){
+                //SFTP
+                $("#fstype").dropdown("set selected", "sftp");
+                newPath = newPath.replace("sftp://", "");
+                $(object).val(newPath);
+            }else if (newPath.startsWith("\\\\")){
+                //SMB, Copy directly from Windows
+                $("#fstype").dropdown("set selected", "smb");
+                newPath = newPath.replace("\\\\", "");
+                newPath = newPath.split("\\").join("/");
+                $(object).val(newPath);
+            }
+        }
+
         function handleHierarchyChange(object){
             var newHierarcy = $(object).find("input").val();
             //Force access mode to readonly if backup mode