sftpfs.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. package sftpfs
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "io/fs"
  7. "io/ioutil"
  8. "log"
  9. "net/url"
  10. "os"
  11. "path/filepath"
  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. _, 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. log.Println("[SFTP FS] Establishing connection with " + serverUrl + "...")
  42. var auths []ssh.AuthMethod
  43. // Use password authentication if provided
  44. if password != "" {
  45. auths = append(auths, ssh.Password(password))
  46. }
  47. // Initialize client configuration
  48. config := ssh.ClientConfig{
  49. User: username,
  50. Auth: auths,
  51. // Uncomment to ignore host key check
  52. HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  53. //HostKeyCallback: ssh.FixedHostKey(hostKey),
  54. }
  55. addr := fmt.Sprintf("%s:%d", serverUrl, port)
  56. // Connect to server
  57. conn, err := ssh.Dial("tcp", addr, &config)
  58. if err != nil {
  59. log.Printf("[SFTP FS] Failed to connect to [%s] %v\n", addr, err)
  60. return SFTPFileSystemAbstraction{}, err
  61. }
  62. // Create new SFTP client
  63. sc, err := sftp.NewClient(conn)
  64. if err != nil {
  65. log.Printf("[SFTP FS] Unable to start SFTP subsystem: %v\n", err)
  66. return SFTPFileSystemAbstraction{}, err
  67. }
  68. log.Println("[SFTP FS] Connected to remote: " + addr)
  69. return SFTPFileSystemAbstraction{
  70. uuid: uuid,
  71. hierarchy: hierarchy,
  72. url: serverUrl,
  73. port: port,
  74. username: username,
  75. password: password,
  76. mountFolder: mountFolder,
  77. client: sc,
  78. conn: conn,
  79. }, nil
  80. }
  81. func (s SFTPFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
  82. filename = arozfs.GenericPathFilter(filename)
  83. return s.client.Chmod(filename, mode)
  84. }
  85. func (s SFTPFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
  86. filename = arozfs.GenericPathFilter(filename)
  87. return s.client.Chown(filename, uid, gid)
  88. }
  89. func (s SFTPFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
  90. filename = arozfs.GenericPathFilter(filename)
  91. return s.client.Chtimes(filename, atime, mtime)
  92. }
  93. func (s SFTPFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
  94. filename = arozfs.GenericPathFilter(filename)
  95. //TODO: ADD FILE TYPE CONVERSION
  96. return nil, arozfs.ErrNullOperation
  97. }
  98. func (s SFTPFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
  99. filename = arozfs.GenericPathFilter(filename)
  100. return s.client.Mkdir(filename)
  101. }
  102. func (s SFTPFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
  103. filename = arozfs.GenericPathFilter(filename)
  104. return s.client.MkdirAll(filename)
  105. }
  106. func (s SFTPFileSystemAbstraction) Name() string {
  107. return ""
  108. }
  109. func (s SFTPFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
  110. filename = arozfs.GenericPathFilter(filename)
  111. f, err := s.client.Open(filename)
  112. if err != nil {
  113. return nil, err
  114. }
  115. stats, err := f.Stat()
  116. if err != nil {
  117. return nil, err
  118. }
  119. isDir := stats.IsDir()
  120. de := []fs.DirEntry{}
  121. if isDir {
  122. dirEntries, err := s.ReadDir(filename)
  123. if err == nil {
  124. de = dirEntries
  125. }
  126. }
  127. //Wrap the file and return
  128. wf := newSftpFsFile(f, isDir, de)
  129. return wf, nil
  130. }
  131. func (s SFTPFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
  132. filename = arozfs.GenericPathFilter(filename)
  133. f, err := s.client.OpenFile(filename, flag)
  134. if err != nil {
  135. return nil, err
  136. }
  137. stats, err := f.Stat()
  138. if err != nil {
  139. return nil, err
  140. }
  141. isDir := stats.IsDir()
  142. de := []fs.DirEntry{}
  143. if isDir {
  144. dirEntries, err := s.ReadDir(filename)
  145. if err == nil {
  146. de = dirEntries
  147. }
  148. }
  149. //Wrap the file and return
  150. wf := newSftpFsFile(f, isDir, de)
  151. return wf, nil
  152. }
  153. func (s SFTPFileSystemAbstraction) Remove(filename string) error {
  154. filename = arozfs.GenericPathFilter(filename)
  155. return s.client.Remove(filename)
  156. }
  157. func (s SFTPFileSystemAbstraction) RemoveAll(filename string) error {
  158. filename = arozfs.GenericPathFilter(filename)
  159. if s.IsDir(filename) {
  160. return s.client.RemoveDirectory(filename)
  161. }
  162. return s.Remove(filename)
  163. }
  164. func (s SFTPFileSystemAbstraction) Rename(oldname, newname string) error {
  165. oldname = arozfs.GenericPathFilter(oldname)
  166. newname = arozfs.GenericPathFilter(newname)
  167. return s.client.Rename(oldname, newname)
  168. }
  169. func (s SFTPFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
  170. filename = arozfs.GenericPathFilter(filename)
  171. return s.client.Stat(filename)
  172. }
  173. func (s SFTPFileSystemAbstraction) Close() error {
  174. err := s.client.Close()
  175. if err != nil {
  176. return err
  177. }
  178. err = s.conn.Close()
  179. if err != nil {
  180. return err
  181. }
  182. return nil
  183. }
  184. /*
  185. Abstraction Utilities
  186. */
  187. func (s SFTPFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
  188. rpath, err := arozfs.GenericVirtualPathToRealPathTranslator(s.uuid, s.hierarchy, subpath, username)
  189. if err != nil {
  190. return "", err
  191. }
  192. if !(len(rpath) >= len(s.mountFolder) && rpath[:len(s.mountFolder)] == s.mountFolder) {
  193. //Prepend the mount folder (aka root folder) to the translated output from generic path translator
  194. rpath = arozfs.ToSlash(filepath.Join(s.mountFolder, rpath))
  195. }
  196. return rpath, nil
  197. }
  198. func (s SFTPFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
  199. if len(fullpath) >= len(s.mountFolder) && fullpath[:len(s.mountFolder)] == s.mountFolder {
  200. //Trim out the mount folder path from the full path before passing into the generic path translator
  201. fullpath = fullpath[len(s.mountFolder):]
  202. }
  203. return arozfs.GenericRealPathToVirtualPathTranslator(s.uuid, s.hierarchy, fullpath, username)
  204. }
  205. func (s SFTPFileSystemAbstraction) FileExists(realpath string) bool {
  206. _, err := s.Stat(realpath)
  207. fmt.Println(realpath, err)
  208. return err == nil
  209. }
  210. func (s SFTPFileSystemAbstraction) IsDir(realpath string) bool {
  211. info, err := s.Stat(realpath)
  212. if err != nil {
  213. return false
  214. }
  215. return info.IsDir()
  216. }
  217. func (s SFTPFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
  218. realpathWildcard = arozfs.GenericPathFilter(realpathWildcard)
  219. return s.client.Glob(realpathWildcard)
  220. }
  221. func (s SFTPFileSystemAbstraction) GetFileSize(realpath string) int64 {
  222. info, err := s.Stat(realpath)
  223. if err != nil {
  224. return 0
  225. }
  226. return info.Size()
  227. }
  228. func (s SFTPFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
  229. info, err := s.Stat(realpath)
  230. if err != nil {
  231. return 0, err
  232. }
  233. return info.ModTime().Unix(), nil
  234. }
  235. func (s SFTPFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
  236. filename = arozfs.GenericPathFilter(filename)
  237. f, err := s.client.OpenFile(filename, os.O_CREATE|os.O_WRONLY)
  238. if err != nil {
  239. return err
  240. }
  241. _, err = f.Write(content)
  242. return err
  243. }
  244. func (s SFTPFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
  245. filename = arozfs.GenericPathFilter(filename)
  246. f, err := s.client.OpenFile(filename, os.O_RDONLY)
  247. if err != nil {
  248. return []byte(""), err
  249. }
  250. return ioutil.ReadAll(f)
  251. }
  252. func (s SFTPFileSystemAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
  253. filename = arozfs.GenericPathFilter(filename)
  254. result := []fs.DirEntry{}
  255. infos, err := s.client.ReadDir(filename)
  256. if err != nil {
  257. return result, err
  258. }
  259. for _, finfo := range infos {
  260. de := newDirEntryFromFileInfo(finfo)
  261. result = append(result, de)
  262. }
  263. return result, nil
  264. }
  265. func (s SFTPFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
  266. filename = arozfs.GenericPathFilter(filename)
  267. f, err := s.client.OpenFile(filename, os.O_CREATE|os.O_WRONLY)
  268. if err != nil {
  269. return err
  270. }
  271. _, err = io.Copy(f, stream)
  272. return err
  273. }
  274. func (s SFTPFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
  275. filename = arozfs.GenericPathFilter(filename)
  276. f, err := s.client.OpenFile(filename, os.O_RDONLY)
  277. if err != nil {
  278. return nil, err
  279. }
  280. return f, nil
  281. }
  282. func (s SFTPFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
  283. root = arozfs.GenericPathFilter(root)
  284. walker := s.client.Walk(root)
  285. for walker.Step() {
  286. walkFn(walker.Path(), walker.Stat(), walker.Err())
  287. }
  288. return nil
  289. }
  290. func (s SFTPFileSystemAbstraction) Heartbeat() error {
  291. return nil
  292. }