Browse Source

Added WIP sftp driver

tobychui 2 years ago
parent
commit
35595ba702

+ 6 - 4
go.mod

@@ -60,22 +60,24 @@ require (
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/klauspost/compress v1.15.9 // indirect
 	github.com/klauspost/pgzip v1.2.5 // indirect
+	github.com/kr/fs v0.1.0 // indirect
 	github.com/miekg/dns v1.1.50 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/nwaples/rardecode v1.1.3 // indirect
 	github.com/pierrec/lz4/v4 v4.1.15 // indirect
+	github.com/pkg/sftp v1.13.5 // indirect
 	github.com/sergi/go-diff v1.2.0 // indirect
 	github.com/ulikunitz/xz v0.5.10 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/xanzy/ssh-agent v0.3.2 // indirect
 	github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
 	gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 // indirect
-	golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
+	golang.org/x/crypto v0.3.0 // indirect
 	golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
-	golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
-	golang.org/x/text v0.3.7 // indirect
+	golang.org/x/net v0.2.0 // indirect
+	golang.org/x/sys v0.2.0 // indirect
+	golang.org/x/text v0.4.0 // indirect
 	golang.org/x/tools v0.1.12 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect

+ 13 - 0
go.sum

@@ -342,6 +342,7 @@ github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8=
 github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA=
+github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
@@ -398,6 +399,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
 github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
+github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
+github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@@ -510,11 +513,14 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
+golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -609,6 +615,8 @@ golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
 golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -730,9 +738,12 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
 golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -743,6 +754,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

+ 4 - 0
mod/filesystem/abstractions/emptyfs/emptyfs.go

@@ -118,3 +118,7 @@ func (l EmptyFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser,
 func (l EmptyFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
 	return arozfs.ErrOperationNotSupported
 }
+
+func (l EmptyFileSystemAbstraction) Heartbeat() error {
+	return nil
+}

+ 0 - 138
mod/filesystem/abstractions/nfsfs/nfsfs.go

@@ -1,138 +0,0 @@
-package nfsfs
-
-import (
-	"io"
-	"io/fs"
-	"os"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"imuslab.com/arozos/mod/filesystem/arozfs"
-)
-
-/*
-	NFSFS.go
-
-	Network File System as File System Abstraction
-
-*/
-
-type NFSFileSystemAbstraction struct {
-	uuid      string
-	hierarchy string
-}
-
-func NewNFSFileSystemAbstraction(uuid string, hierarchy string, hostname string, target string, dir string, username string, password string) (NFSFileSystemAbstraction, error) {
-
-	return NFSFileSystemAbstraction{
-		uuid:      uuid,
-		hierarchy: hierarchy,
-	}, nil
-}
-func (l NFSFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
-	return nil, arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Name() string {
-	return ""
-}
-func (l NFSFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
-	return nil, arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
-	return nil, arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Remove(filename string) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) RemoveAll(path string) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Rename(oldname, newname string) error {
-	return arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
-	return nil, arozfs.ErrNullOperation
-}
-func (l NFSFileSystemAbstraction) Close() error {
-	return nil
-}
-
-/*
-	Abstraction Utilities
-*/
-
-func (a NFSFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
-	return arozfs.GenericVirtualPathToRealPathTranslator(a.uuid, a.hierarchy, subpath, username)
-}
-
-func (a NFSFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
-	return arozfs.GenericRealPathToVirtualPathTranslator(a.uuid, a.hierarchy, fullpath, username)
-}
-
-func (a NFSFileSystemAbstraction) FileExists(realpath string) bool {
-	return false
-}
-
-func (a NFSFileSystemAbstraction) IsDir(realpath string) bool {
-	return false
-}
-
-func (a NFSFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
-	return []string{}, arozfs.ErrNullOperation
-}
-
-func (a NFSFileSystemAbstraction) GetFileSize(realpath string) int64 {
-	return 0
-}
-
-func (a NFSFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
-	return 0, arozfs.ErrOperationNotSupported
-}
-
-func (a NFSFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
-	return arozfs.ErrNullOperation
-}
-func (a NFSFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
-	return []byte(""), arozfs.ErrOperationNotSupported
-}
-func (a NFSFileSystemAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
-	return []fs.DirEntry{}, arozfs.ErrOperationNotSupported
-}
-func (a NFSFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
-	return arozfs.ErrNullOperation
-}
-func (a NFSFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
-
-	return nil, arozfs.ErrOperationNotSupported
-}
-
-func (a NFSFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
-	return arozfs.ErrOperationNotSupported
-}
-
-//Utilities
-func filterFilepath(rawpath string) string {
-	rawpath = arozfs.ToSlash(filepath.Clean(strings.TrimSpace(rawpath)))
-	if strings.HasPrefix(rawpath, "./") {
-		return rawpath[1:]
-	} else if rawpath == "." || rawpath == "" {
-		return "/"
-	}
-	return rawpath
-}

+ 269 - 0
mod/filesystem/abstractions/sftpfs/sftpfs.go

@@ -0,0 +1,269 @@
+package stpfs
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"io/fs"
+	"io/ioutil"
+	"net/url"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/pkg/sftp"
+	"golang.org/x/crypto/ssh"
+	"imuslab.com/arozos/mod/filesystem/arozfs"
+)
+
+/*
+	SFTP-FS.go
+
+	SSH File Transfer Protocol as File System Abstraction
+
+*/
+
+type SFTPFileSystemAbstraction struct {
+	uuid      string
+	hierarchy string
+	url       string
+	port      int
+
+	username    string
+	password    string
+	mountFolder string
+
+	client *sftp.Client
+	conn   *ssh.Client
+}
+
+func NewSFTPFileSystemAbstraction(uuid string, hierarchy string, serverUrl string, port int, mountFolder string, username string, password string) (SFTPFileSystemAbstraction, error) {
+	parsedUrl, err := url.Parse(serverUrl)
+	if err != nil {
+		return SFTPFileSystemAbstraction{}, errors.New("[SFTP] to parse url: " + err.Error())
+	}
+
+	// Get user name and pass
+	//user := parsedUrl.User.Username()
+	//, _ := parsedUrl.User.Password()
+
+	// Parse Host and Port
+	host := parsedUrl.Host
+
+	fmt.Fprintf(os.Stdout, "Connecting to %s ...\n", host)
+
+	var auths []ssh.AuthMethod
+
+	// Use password authentication if provided
+	if password != "" {
+		auths = append(auths, ssh.Password(password))
+	}
+
+	// Initialize client configuration
+	config := ssh.ClientConfig{
+		User: username,
+		Auth: auths,
+		// Uncomment to ignore host key check
+		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+		//HostKeyCallback: ssh.FixedHostKey(hostKey),
+	}
+
+	addr := fmt.Sprintf("%s:%d", serverUrl, port)
+	fmt.Println(addr, username, password)
+	// Connect to server
+	conn, err := ssh.Dial("tcp", addr, &config)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to connect to [%s] %v\n", addr, err)
+		os.Exit(1)
+	}
+
+	// Create new SFTP client
+	sc, err := sftp.NewClient(conn)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Unable to start SFTP subsystem: %v\n", err)
+		os.Exit(1)
+	}
+
+	fmt.Println("Connection Succeed")
+	return SFTPFileSystemAbstraction{
+		uuid:        uuid,
+		hierarchy:   hierarchy,
+		url:         serverUrl,
+		port:        port,
+		username:    username,
+		password:    password,
+		mountFolder: mountFolder,
+		client:      sc,
+		conn:        conn,
+	}, nil
+}
+func (s SFTPFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
+	filename = arozfs.GenericPathFilter(filename)
+	return s.client.Chmod(filename, mode)
+}
+func (s SFTPFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
+	filename = arozfs.GenericPathFilter(filename)
+	return s.client.Chown(filename, uid, gid)
+}
+func (s SFTPFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
+	filename = arozfs.GenericPathFilter(filename)
+	return s.client.Chtimes(filename, atime, mtime)
+}
+func (s SFTPFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
+	filename = arozfs.GenericPathFilter(filename)
+	//TODO: ADD FILE TYPE CONVERSION
+	return nil, arozfs.ErrNullOperation
+}
+func (s SFTPFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
+	filename = arozfs.GenericPathFilter(filename)
+	return s.client.Mkdir(filename)
+}
+func (s SFTPFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
+	filename = arozfs.GenericPathFilter(filename)
+	return s.client.MkdirAll(filename)
+}
+func (s SFTPFileSystemAbstraction) Name() string {
+	return ""
+}
+func (s SFTPFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
+	return nil, arozfs.ErrNullOperation
+}
+func (s SFTPFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
+	return nil, arozfs.ErrNullOperation
+}
+func (s SFTPFileSystemAbstraction) Remove(filename string) error {
+	filename = arozfs.GenericPathFilter(filename)
+	return s.client.Remove(filename)
+}
+func (s SFTPFileSystemAbstraction) RemoveAll(filename string) error {
+	filename = arozfs.GenericPathFilter(filename)
+	if s.IsDir(filename) {
+		return s.client.RemoveDirectory(filename)
+	}
+	return s.Remove(filename)
+
+}
+func (s SFTPFileSystemAbstraction) Rename(oldname, newname string) error {
+	oldname = arozfs.GenericPathFilter(oldname)
+	newname = arozfs.GenericPathFilter(newname)
+	return s.client.Rename(oldname, newname)
+}
+func (s SFTPFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
+	filename = arozfs.GenericPathFilter(filename)
+	return s.client.Stat(filename)
+}
+func (s SFTPFileSystemAbstraction) Close() error {
+	err := s.client.Close()
+	if err != nil {
+		return err
+	}
+
+	err = s.conn.Close()
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+/*
+	Abstraction Utilities
+*/
+
+func (s SFTPFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
+	return arozfs.GenericVirtualPathToRealPathTranslator(s.uuid, s.hierarchy, subpath, username)
+}
+
+func (s SFTPFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
+	return arozfs.GenericRealPathToVirtualPathTranslator(s.uuid, s.hierarchy, fullpath, username)
+}
+
+func (s SFTPFileSystemAbstraction) FileExists(realpath string) bool {
+	_, err := s.Stat(realpath)
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+func (s SFTPFileSystemAbstraction) IsDir(realpath string) bool {
+	info, err := s.Stat(realpath)
+	if err != nil {
+		return false
+	}
+
+	return info.IsDir()
+}
+
+func (s SFTPFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
+	realpathWildcard = arozfs.GenericPathFilter(realpathWildcard)
+	return s.client.Glob(realpathWildcard)
+}
+
+func (s SFTPFileSystemAbstraction) GetFileSize(realpath string) int64 {
+	info, err := s.Stat(realpath)
+	if err != nil {
+		return 0
+	}
+
+	return info.Size()
+}
+
+func (s SFTPFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
+	info, err := s.Stat(realpath)
+	if err != nil {
+		return 0, err
+	}
+
+	return info.ModTime().Unix(), nil
+}
+
+func (s SFTPFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
+	filename = arozfs.GenericPathFilter(filename)
+	f, err := s.client.OpenFile(filename, os.O_CREATE|os.O_WRONLY)
+	if err != nil {
+		return err
+	}
+
+	_, err = f.Write(content)
+	return err
+}
+func (s SFTPFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
+	filename = arozfs.GenericPathFilter(filename)
+	f, err := s.client.OpenFile(filename, os.O_RDONLY)
+	if err != nil {
+		return []byte(""), err
+	}
+
+	return ioutil.ReadAll(f)
+}
+func (s SFTPFileSystemAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
+	s.client.ReadDir(filename)
+	return []fs.DirEntry{}, arozfs.ErrOperationNotSupported
+}
+func (s SFTPFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
+	return arozfs.ErrNullOperation
+}
+func (s SFTPFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
+
+	return nil, arozfs.ErrOperationNotSupported
+}
+
+func (s SFTPFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
+	return arozfs.ErrOperationNotSupported
+}
+
+func (s SFTPFileSystemAbstraction) Heartbeat() error {
+	return nil
+}
+
+//Utilities
+func filterFilepath(rawpath string) string {
+	rawpath = arozfs.ToSlash(filepath.Clean(strings.TrimSpace(rawpath)))
+	if strings.HasPrefix(rawpath, "./") {
+		return rawpath[1:]
+	} else if rawpath == "." || rawpath == "" {
+		return "/"
+	}
+	return rawpath
+}

+ 12 - 0
mod/filesystem/arozfs/arozfs.go

@@ -140,6 +140,18 @@ func GenericRealPathToVirtualPathTranslator(uuid string, hierarchy string, rpath
 	return uuid + ":" + rpath, nil
 }
 
+//Generic function for abstraction driver to filter incoming paths
+func GenericPathFilter(filename string) string {
+	filename = ToSlash(filepath.Clean(filename))
+	rawpath := strings.TrimSpace(filename)
+	if strings.HasPrefix(rawpath, "./") {
+		return rawpath[1:]
+	} else if rawpath == "." || rawpath == "" {
+		return "/"
+	}
+	return rawpath
+}
+
 //Filter illegal characters in filename
 func FilterIllegalCharInFilename(filename string, replacement string) string {
 	re, _ := regexp.Compile(`[\\\[\]$?#<>+%!"'|{}:@]`)

+ 53 - 1
mod/filesystem/filesystem.go

@@ -14,17 +14,20 @@ import (
 	"crypto/md5"
 	"encoding/hex"
 	"errors"
+	"fmt"
 	"io"
 	"io/fs"
 	"log"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"time"
 
 	db "imuslab.com/arozos/mod/database"
 	"imuslab.com/arozos/mod/filesystem/abstractions/ftpfs"
 	"imuslab.com/arozos/mod/filesystem/abstractions/localfs"
+	sftpfs "imuslab.com/arozos/mod/filesystem/abstractions/sftpfs"
 	"imuslab.com/arozos/mod/filesystem/abstractions/smbfs"
 	"imuslab.com/arozos/mod/filesystem/abstractions/webdavfs"
 	"imuslab.com/arozos/mod/filesystem/arozfs"
@@ -203,7 +206,7 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 			Closed:                false,
 		}, nil
 	} else if fstype == "smb" {
-		//WebDAV. Create an object and mount it
+		//SMB. Create an object and mount it
 		pathChunks := strings.Split(strings.ReplaceAll(option.Path, "\\", "/"), "/")
 		if len(pathChunks) != 2 {
 			return nil, errors.New("Invalid configured smb filepath: Path format not matching [ip_addr]:[port]/[root_share]")
@@ -240,6 +243,55 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 			Closed:                false,
 		}
 
+		return &thisFsh, nil
+	} else if fstype == "sftp" {
+		//SFTP
+		pathChunks := strings.Split(strings.ReplaceAll(option.Path, "\\", "/"), "/")
+		ipAddr := pathChunks[0]
+		port := 22
+		if strings.Contains(ipAddr, ":") {
+			//Custom port defined
+			ipChunks := strings.Split(ipAddr, ":")
+			ipAddr = ipChunks[0]
+			p, err := strconv.Atoi(ipChunks[1])
+			if err == nil {
+				port = p
+			}
+		}
+		rootShare := pathChunks[1:]
+		user := option.Username
+		password := option.Password
+		fmt.Println(ipAddr, port, "/"+strings.Join(rootShare, "/"), user, password)
+		sftpfs, err := sftpfs.NewSFTPFileSystemAbstraction(
+			option.Uuid,
+			option.Hierarchy,
+			ipAddr,
+			port,
+			"/"+strings.Join(rootShare, "/"),
+			user,
+			password,
+		)
+		if err != nil {
+			fmt.Println(err.Error())
+			return nil, err
+		}
+
+		thisFsh := FileSystemHandler{
+			Name:                  option.Name,
+			UUID:                  option.Uuid,
+			Path:                  option.Path,
+			ReadOnly:              option.Access == arozfs.FsReadOnly,
+			RequireBuffer:         false,
+			Hierarchy:             option.Hierarchy,
+			HierarchyConfig:       nil,
+			InitiationTime:        time.Now().Unix(),
+			FilesystemDatabase:    nil,
+			FileSystemAbstraction: sftpfs,
+			Filesystem:            fstype,
+			StartOptions:          option,
+			Closed:                false,
+		}
+
 		return &thisFsh, nil
 	} else if fstype == "ftp" {