ftpfs.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. package ftpfs
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/fs"
  7. "io/ioutil"
  8. "log"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "time"
  13. "github.com/jlaffaye/ftp"
  14. "imuslab.com/arozos/mod/filesystem/arozfs"
  15. )
  16. /*
  17. FTPFS.go
  18. FTP Server as File System Abstraction
  19. */
  20. type FTPFSAbstraction struct {
  21. uuid string
  22. hierarchy string
  23. conn *ftp.ServerConn
  24. closer chan bool
  25. }
  26. func NewFTPFSAbstraction(uuid string, hierarchy string, hostname string, username string, password string) (FTPFSAbstraction, error) {
  27. c, err := ftp.Dial(hostname, ftp.DialWithTimeout(5*time.Second))
  28. if err != nil {
  29. log.Println("[FTPFS] Unable to dial TCP: " + err.Error())
  30. return FTPFSAbstraction{}, err
  31. }
  32. if username == "" && password == "" {
  33. username = "anonymouss"
  34. password = "anonymous"
  35. }
  36. //Login to the FTP account
  37. err = c.Login(username, password)
  38. if err != nil {
  39. return FTPFSAbstraction{}, err
  40. }
  41. //Create a ticker to prevent connection close
  42. ticker := time.NewTicker(180 * time.Second)
  43. done := make(chan bool)
  44. go func() {
  45. for {
  46. select {
  47. case <-done:
  48. return
  49. case <-ticker.C:
  50. c.NoOp()
  51. }
  52. }
  53. }()
  54. return FTPFSAbstraction{
  55. uuid: uuid,
  56. hierarchy: hierarchy,
  57. conn: c,
  58. closer: done,
  59. }, nil
  60. }
  61. func (l FTPFSAbstraction) Chmod(filename string, mode os.FileMode) error {
  62. return arozfs.ErrOperationNotSupported
  63. }
  64. func (l FTPFSAbstraction) Chown(filename string, uid int, gid int) error {
  65. return arozfs.ErrOperationNotSupported
  66. }
  67. func (l FTPFSAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
  68. return arozfs.ErrOperationNotSupported
  69. }
  70. func (l FTPFSAbstraction) Create(filename string) (arozfs.File, error) {
  71. return nil, arozfs.ErrOperationNotSupported
  72. }
  73. func (l FTPFSAbstraction) Mkdir(filename string, mode os.FileMode) error {
  74. return l.conn.MakeDir(filename)
  75. }
  76. func (l FTPFSAbstraction) MkdirAll(filename string, mode os.FileMode) error {
  77. return l.Mkdir(filename, mode)
  78. }
  79. func (l FTPFSAbstraction) Name() string {
  80. return ""
  81. }
  82. func (l FTPFSAbstraction) Open(filename string) (arozfs.File, error) {
  83. return nil, arozfs.ErrOperationNotSupported
  84. }
  85. func (l FTPFSAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
  86. return nil, arozfs.ErrOperationNotSupported
  87. }
  88. func (l FTPFSAbstraction) Remove(filename string) error {
  89. filename = filterFilepath(filename)
  90. return l.conn.Delete(filename)
  91. }
  92. func (l FTPFSAbstraction) RemoveAll(path string) error {
  93. path = filterFilepath(path)
  94. return l.conn.Delete(path)
  95. }
  96. func (l FTPFSAbstraction) Rename(oldname, newname string) error {
  97. oldname = filterFilepath(oldname)
  98. newname = filterFilepath(newname)
  99. return l.conn.Rename(oldname, newname)
  100. }
  101. func (l FTPFSAbstraction) Stat(filename string) (os.FileInfo, error) {
  102. return nil, arozfs.ErrNullOperation
  103. }
  104. func (l FTPFSAbstraction) Close() error {
  105. return l.conn.Quit()
  106. }
  107. /*
  108. Abstraction Utilities
  109. */
  110. func (l FTPFSAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
  111. return arozfs.GenericVirtualPathToRealPathTranslator(l.uuid, l.hierarchy, subpath, username)
  112. }
  113. func (l FTPFSAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
  114. return arozfs.GenericRealPathToVirtualPathTranslator(l.uuid, l.hierarchy, fullpath, username)
  115. }
  116. func (l FTPFSAbstraction) FileExists(realpath string) bool {
  117. realpath = filterFilepath(realpath)
  118. _, err := l.conn.GetEntry(realpath)
  119. return err == nil
  120. }
  121. func (l FTPFSAbstraction) IsDir(realpath string) bool {
  122. realpath = filterFilepath(realpath)
  123. entry, err := l.conn.GetEntry(realpath)
  124. if err != nil {
  125. return false
  126. }
  127. return entry.Type == ftp.EntryTypeFolder
  128. }
  129. func (l FTPFSAbstraction) Glob(realpathWildcard string) ([]string, error) {
  130. return []string{}, arozfs.ErrNullOperation
  131. }
  132. func (l FTPFSAbstraction) GetFileSize(realpath string) int64 {
  133. realpath = filterFilepath(realpath)
  134. entry, err := l.conn.GetEntry(realpath)
  135. if err != nil {
  136. return 0
  137. }
  138. return int64(entry.Size)
  139. }
  140. func (l FTPFSAbstraction) GetModTime(realpath string) (int64, error) {
  141. realpath = filterFilepath(realpath)
  142. entry, err := l.conn.GetEntry(realpath)
  143. if err != nil {
  144. return 0, err
  145. }
  146. return entry.Time.Unix(), nil
  147. }
  148. func (l FTPFSAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
  149. filename = filterFilepath(filename)
  150. reader := bytes.NewReader(content)
  151. return l.conn.Stor(filename, reader)
  152. }
  153. func (l FTPFSAbstraction) ReadFile(filename string) ([]byte, error) {
  154. filename = filterFilepath(filename)
  155. r, err := l.conn.Retr(filename)
  156. if err != nil {
  157. panic(err)
  158. }
  159. defer r.Close()
  160. return ioutil.ReadAll(r)
  161. }
  162. func (l FTPFSAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
  163. results := []fs.DirEntry{}
  164. filename = filterFilepath(filename)
  165. entries, err := l.conn.List(filename)
  166. if err != nil {
  167. return results, err
  168. }
  169. for _, entry := range entries {
  170. entryFilename := arozfs.ToSlash(filepath.Join(filename, entry.Name))
  171. fmt.Println(entryFilename)
  172. thisDirEntry := newDirEntryFromFTPEntry(entry, l.conn, entryFilename)
  173. results = append(results, thisDirEntry)
  174. }
  175. return results, nil
  176. }
  177. func (l FTPFSAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
  178. filename = filterFilepath(filename)
  179. return l.conn.Stor(filename, stream)
  180. }
  181. func (l FTPFSAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
  182. filename = filterFilepath(filename)
  183. return l.conn.Retr(filename)
  184. }
  185. func (l FTPFSAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
  186. return arozfs.ErrOperationNotSupported
  187. }
  188. func (l FTPFSAbstraction) Heartbeat() error {
  189. return nil
  190. }
  191. //Utilities
  192. func filterFilepath(rawpath string) string {
  193. rawpath = arozfs.ToSlash(filepath.Clean(strings.TrimSpace(rawpath)))
  194. if strings.HasPrefix(rawpath, "./") {
  195. return rawpath[1:]
  196. } else if rawpath == "." || rawpath == "" {
  197. return "/"
  198. }
  199. return rawpath
  200. }