|
@@ -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
|
|
|
+}
|