sftpfs.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package sftpfs
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "io/fs"
  7. "io/ioutil"
  8. "net/url"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "time"
  13. "github.com/pkg/sftp"
  14. "golang.org/x/crypto/ssh"
  15. "imuslab.com/arozos/mod/filesystem/arozfs"
  16. )
  17. /*
  18. SFTP-FS.go
  19. SSH File Transfer Protocol as File System Abstraction
  20. */
  21. type SFTPFileSystemAbstraction struct {
  22. uuid string
  23. hierarchy string
  24. url string
  25. port int
  26. username string
  27. password string
  28. mountFolder string
  29. client *sftp.Client
  30. conn *ssh.Client
  31. }
  32. func NewSFTPFileSystemAbstraction(uuid string, hierarchy string, serverUrl string, port int, mountFolder string, username string, password string) (SFTPFileSystemAbstraction, error) {
  33. parsedUrl, err := url.Parse(serverUrl)
  34. if err != nil {
  35. return SFTPFileSystemAbstraction{}, errors.New("[SFTP] to parse url: " + err.Error())
  36. }
  37. // Get user name and pass
  38. //user := parsedUrl.User.Username()
  39. //, _ := parsedUrl.User.Password()
  40. // Parse Host and Port
  41. host := parsedUrl.Host
  42. fmt.Fprintf(os.Stdout, "Connecting to %s ...\n", host)
  43. var auths []ssh.AuthMethod
  44. // Use password authentication if provided
  45. if password != "" {
  46. auths = append(auths, ssh.Password(password))
  47. }
  48. // Initialize client configuration
  49. config := ssh.ClientConfig{
  50. User: username,
  51. Auth: auths,
  52. // Uncomment to ignore host key check
  53. HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  54. //HostKeyCallback: ssh.FixedHostKey(hostKey),
  55. }
  56. addr := fmt.Sprintf("%s:%d", serverUrl, port)
  57. fmt.Println(addr, username, password)
  58. // Connect to server
  59. conn, err := ssh.Dial("tcp", addr, &config)
  60. if err != nil {
  61. fmt.Fprintf(os.Stderr, "Failed to connect to [%s] %v\n", addr, err)
  62. os.Exit(1)
  63. }
  64. // Create new SFTP client
  65. sc, err := sftp.NewClient(conn)
  66. if err != nil {
  67. fmt.Fprintf(os.Stderr, "Unable to start SFTP subsystem: %v\n", err)
  68. os.Exit(1)
  69. }
  70. fmt.Println("Connection Succeed")
  71. return SFTPFileSystemAbstraction{
  72. uuid: uuid,
  73. hierarchy: hierarchy,
  74. url: serverUrl,
  75. port: port,
  76. username: username,
  77. password: password,
  78. mountFolder: mountFolder,
  79. client: sc,
  80. conn: conn,
  81. }, nil
  82. }
  83. func (s SFTPFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
  84. filename = arozfs.GenericPathFilter(filename)
  85. return s.client.Chmod(filename, mode)
  86. }
  87. func (s SFTPFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
  88. filename = arozfs.GenericPathFilter(filename)
  89. return s.client.Chown(filename, uid, gid)
  90. }
  91. func (s SFTPFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
  92. filename = arozfs.GenericPathFilter(filename)
  93. return s.client.Chtimes(filename, atime, mtime)
  94. }
  95. func (s SFTPFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
  96. filename = arozfs.GenericPathFilter(filename)
  97. //TODO: ADD FILE TYPE CONVERSION
  98. return nil, arozfs.ErrNullOperation
  99. }
  100. func (s SFTPFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
  101. filename = arozfs.GenericPathFilter(filename)
  102. return s.client.Mkdir(filename)
  103. }
  104. func (s SFTPFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
  105. filename = arozfs.GenericPathFilter(filename)
  106. return s.client.MkdirAll(filename)
  107. }
  108. func (s SFTPFileSystemAbstraction) Name() string {
  109. return ""
  110. }
  111. func (s SFTPFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
  112. return nil, arozfs.ErrNullOperation
  113. }
  114. func (s SFTPFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
  115. return nil, arozfs.ErrNullOperation
  116. }
  117. func (s SFTPFileSystemAbstraction) Remove(filename string) error {
  118. filename = arozfs.GenericPathFilter(filename)
  119. return s.client.Remove(filename)
  120. }
  121. func (s SFTPFileSystemAbstraction) RemoveAll(filename string) error {
  122. filename = arozfs.GenericPathFilter(filename)
  123. if s.IsDir(filename) {
  124. return s.client.RemoveDirectory(filename)
  125. }
  126. return s.Remove(filename)
  127. }
  128. func (s SFTPFileSystemAbstraction) Rename(oldname, newname string) error {
  129. oldname = arozfs.GenericPathFilter(oldname)
  130. newname = arozfs.GenericPathFilter(newname)
  131. return s.client.Rename(oldname, newname)
  132. }
  133. func (s SFTPFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
  134. filename = arozfs.GenericPathFilter(filename)
  135. return s.client.Stat(filename)
  136. }
  137. func (s SFTPFileSystemAbstraction) Close() error {
  138. err := s.client.Close()
  139. if err != nil {
  140. return err
  141. }
  142. err = s.conn.Close()
  143. if err != nil {
  144. return err
  145. }
  146. return nil
  147. }
  148. /*
  149. Abstraction Utilities
  150. */
  151. func (s SFTPFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
  152. return arozfs.GenericVirtualPathToRealPathTranslator(s.uuid, s.hierarchy, subpath, username)
  153. }
  154. func (s SFTPFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
  155. return arozfs.GenericRealPathToVirtualPathTranslator(s.uuid, s.hierarchy, fullpath, username)
  156. }
  157. func (s SFTPFileSystemAbstraction) FileExists(realpath string) bool {
  158. _, err := s.Stat(realpath)
  159. if err != nil {
  160. return false
  161. }
  162. return true
  163. }
  164. func (s SFTPFileSystemAbstraction) IsDir(realpath string) bool {
  165. info, err := s.Stat(realpath)
  166. if err != nil {
  167. return false
  168. }
  169. return info.IsDir()
  170. }
  171. func (s SFTPFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
  172. realpathWildcard = arozfs.GenericPathFilter(realpathWildcard)
  173. return s.client.Glob(realpathWildcard)
  174. }
  175. func (s SFTPFileSystemAbstraction) GetFileSize(realpath string) int64 {
  176. info, err := s.Stat(realpath)
  177. if err != nil {
  178. return 0
  179. }
  180. return info.Size()
  181. }
  182. func (s SFTPFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
  183. info, err := s.Stat(realpath)
  184. if err != nil {
  185. return 0, err
  186. }
  187. return info.ModTime().Unix(), nil
  188. }
  189. func (s SFTPFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
  190. filename = arozfs.GenericPathFilter(filename)
  191. f, err := s.client.OpenFile(filename, os.O_CREATE|os.O_WRONLY)
  192. if err != nil {
  193. return err
  194. }
  195. _, err = f.Write(content)
  196. return err
  197. }
  198. func (s SFTPFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
  199. filename = arozfs.GenericPathFilter(filename)
  200. f, err := s.client.OpenFile(filename, os.O_RDONLY)
  201. if err != nil {
  202. return []byte(""), err
  203. }
  204. return ioutil.ReadAll(f)
  205. }
  206. func (s SFTPFileSystemAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
  207. result := []fs.DirEntry{}
  208. infos, err := s.client.ReadDir(filename)
  209. if err != nil {
  210. return result, err
  211. }
  212. for _, finfo := range infos {
  213. de := newDirEntryFromFileInfo(finfo)
  214. result = append(result, de)
  215. }
  216. return result, nil
  217. }
  218. func (s SFTPFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
  219. filename = arozfs.GenericPathFilter(filename)
  220. f, err := s.client.OpenFile(filename, os.O_CREATE|os.O_WRONLY)
  221. if err != nil {
  222. return err
  223. }
  224. _, err = io.Copy(f, stream)
  225. return err
  226. }
  227. func (s SFTPFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
  228. filename = arozfs.GenericPathFilter(filename)
  229. f, err := s.client.OpenFile(filename, os.O_RDONLY)
  230. if err != nil {
  231. return nil, err
  232. }
  233. return f, nil
  234. }
  235. func (s SFTPFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
  236. walker := s.client.Walk(root)
  237. for walker.Step() {
  238. walkFn(walker.Path(), walker.Stat(), walker.Err())
  239. }
  240. return nil
  241. }
  242. func (s SFTPFileSystemAbstraction) Heartbeat() error {
  243. return nil
  244. }
  245. //Utilities
  246. func filterFilepath(rawpath string) string {
  247. rawpath = arozfs.ToSlash(filepath.Clean(strings.TrimSpace(rawpath)))
  248. if strings.HasPrefix(rawpath, "./") {
  249. return rawpath[1:]
  250. } else if rawpath == "." || rawpath == "" {
  251. return "/"
  252. }
  253. return rawpath
  254. }