smbfs.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package smbfs
  2. import (
  3. "fmt"
  4. "io"
  5. "io/fs"
  6. "log"
  7. "net"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "time"
  12. "github.com/hirochachacha/go-smb2"
  13. "imuslab.com/arozos/mod/filesystem/fsdef"
  14. )
  15. /*
  16. Server Message Block.go
  17. This is a file abstraction that mount SMB folders onto ArozOS as virtual drive
  18. */
  19. type ServerMessageBlockFileSystemAbstraction struct {
  20. UUID string
  21. Hierarchy string
  22. ipaddr string
  23. user string
  24. conn *net.Conn
  25. session *smb2.Session
  26. share *smb2.Share
  27. }
  28. func NewServerMessageBlockFileSystemAbstraction(uuid string, hierarchy string, ipaddr string, username string, password string) (ServerMessageBlockFileSystemAbstraction, error) {
  29. conn, err := net.Dial("tcp", ipaddr+":445")
  30. if err != nil {
  31. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  32. return ServerMessageBlockFileSystemAbstraction{}, err
  33. }
  34. d := &smb2.Dialer{
  35. Initiator: &smb2.NTLMInitiator{
  36. User: username,
  37. Password: password,
  38. },
  39. }
  40. s, err := d.Dial(conn)
  41. if err != nil {
  42. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  43. return ServerMessageBlockFileSystemAbstraction{}, err
  44. }
  45. //Mound remote storage
  46. //SSDBuffer
  47. fs, err := s.Mount("SSDBuffer")
  48. if err != nil {
  49. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  50. return ServerMessageBlockFileSystemAbstraction{}, err
  51. }
  52. log.Println("[SMB-FS] Connected to remote: " + ipaddr)
  53. return ServerMessageBlockFileSystemAbstraction{
  54. UUID: uuid,
  55. Hierarchy: hierarchy,
  56. ipaddr: ipaddr,
  57. user: username,
  58. conn: &conn,
  59. session: s,
  60. share: fs,
  61. }, nil
  62. }
  63. func (a ServerMessageBlockFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
  64. filename = filterFilepath(filename)
  65. filename = toWinPath(filename)
  66. return a.share.Chmod(filename, mode)
  67. }
  68. func (a ServerMessageBlockFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
  69. return fsdef.ErrOperationNotSupported
  70. }
  71. func (a ServerMessageBlockFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
  72. filename = filterFilepath(filename)
  73. filename = toWinPath(filename)
  74. return a.share.Chtimes(filename, atime, mtime)
  75. }
  76. func (a ServerMessageBlockFileSystemAbstraction) Create(filename string) (*os.File, error) {
  77. return nil, fsdef.ErrOperationNotSupported
  78. }
  79. func (a ServerMessageBlockFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
  80. filename = filterFilepath(filename)
  81. filename = toWinPath(filename)
  82. return a.share.Mkdir(filename, mode)
  83. }
  84. func (a ServerMessageBlockFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
  85. filename = filterFilepath(filename)
  86. filename = toWinPath(filename)
  87. return a.share.MkdirAll(filename, mode)
  88. }
  89. func (a ServerMessageBlockFileSystemAbstraction) Name() string {
  90. return ""
  91. }
  92. func (a ServerMessageBlockFileSystemAbstraction) Open(filename string) (*os.File, error) {
  93. return nil, fsdef.ErrOperationNotSupported
  94. }
  95. func (a ServerMessageBlockFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (*os.File, error) {
  96. return nil, fsdef.ErrOperationNotSupported
  97. }
  98. func (a ServerMessageBlockFileSystemAbstraction) Remove(filename string) error {
  99. filename = filterFilepath(filename)
  100. filename = toWinPath(filename)
  101. return a.share.Remove(filename)
  102. }
  103. func (a ServerMessageBlockFileSystemAbstraction) RemoveAll(filename string) error {
  104. filename = filterFilepath(filename)
  105. filename = toWinPath(filename)
  106. return a.share.RemoveAll(filename)
  107. }
  108. func (a ServerMessageBlockFileSystemAbstraction) Rename(oldname, newname string) error {
  109. oldname = toWinPath(filterFilepath(oldname))
  110. newname = toWinPath(filterFilepath(newname))
  111. return a.share.Rename(oldname, newname)
  112. }
  113. func (a ServerMessageBlockFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
  114. filename = toWinPath(filterFilepath(filename))
  115. return a.share.Stat(filename)
  116. }
  117. func (a ServerMessageBlockFileSystemAbstraction) Close() error {
  118. a.share.Umount()
  119. a.session.Logoff()
  120. conn := *(a.conn)
  121. conn.Close()
  122. return nil
  123. }
  124. /*
  125. Abstraction Utilities
  126. */
  127. func (a ServerMessageBlockFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
  128. if strings.HasPrefix(subpath, a.UUID+":") {
  129. //This is full virtual path. Trim the uuid and correct the subpath
  130. subpath = strings.TrimPrefix(subpath, a.UUID+":")
  131. }
  132. subpath = filterFilepath(subpath)
  133. if a.Hierarchy == "user" {
  134. return toWinPath(filepath.ToSlash(filepath.Clean(filepath.Join("users", username, subpath)))), nil
  135. } else if a.Hierarchy == "public" {
  136. return toWinPath(filepath.ToSlash(filepath.Clean(subpath))), nil
  137. }
  138. return "", fsdef.ErrVpathResolveFailed
  139. }
  140. func (a ServerMessageBlockFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
  141. fullpath = strings.TrimPrefix(fullpath, "\\")
  142. vpath := a.UUID + ":/" + filterFilepath(fullpath)
  143. return vpath, nil
  144. }
  145. func (a ServerMessageBlockFileSystemAbstraction) FileExists(realpath string) bool {
  146. realpath = toWinPath(filterFilepath(realpath))
  147. _, err := a.share.Open(realpath)
  148. return err == nil
  149. }
  150. func (a ServerMessageBlockFileSystemAbstraction) IsDir(realpath string) bool {
  151. realpath = filterFilepath(realpath)
  152. realpath = toWinPath(realpath)
  153. stx, err := a.share.Stat(realpath)
  154. if err != nil {
  155. return false
  156. }
  157. return stx.IsDir()
  158. }
  159. func (a ServerMessageBlockFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
  160. realpathWildcard = toWinPath(filterFilepath(realpathWildcard))
  161. return a.share.Glob(realpathWildcard)
  162. }
  163. func (a ServerMessageBlockFileSystemAbstraction) GetFileSize(realpath string) int64 {
  164. realpath = toWinPath(filterFilepath(realpath))
  165. stat, err := a.share.Stat(realpath)
  166. if err != nil {
  167. return 0
  168. }
  169. return stat.Size()
  170. }
  171. func (a ServerMessageBlockFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
  172. realpath = toWinPath(filterFilepath(realpath))
  173. stat, err := a.share.Stat(realpath)
  174. if err != nil {
  175. return 0, nil
  176. }
  177. return stat.ModTime().Unix(), nil
  178. }
  179. func (a ServerMessageBlockFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
  180. filename = toWinPath(filterFilepath(filename))
  181. return a.share.WriteFile(filename, content, mode)
  182. }
  183. func (a ServerMessageBlockFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
  184. filename = toWinPath(filterFilepath(filename))
  185. return a.share.ReadFile(filename)
  186. }
  187. func (a ServerMessageBlockFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
  188. filename = toWinPath(filterFilepath(filename))
  189. f, err := a.share.OpenFile(filename, os.O_CREATE|os.O_WRONLY, mode)
  190. if err != nil {
  191. return err
  192. }
  193. p := make([]byte, 1024)
  194. for {
  195. _, err := stream.Read(p)
  196. if err != nil {
  197. if err == io.EOF {
  198. break
  199. } else {
  200. return err
  201. }
  202. }
  203. _, err = f.Write(p)
  204. if err != nil {
  205. return err
  206. }
  207. }
  208. return nil
  209. }
  210. func (a ServerMessageBlockFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
  211. filename = toWinPath(filterFilepath(filename))
  212. f, err := a.share.OpenFile(filename, os.O_RDONLY, 0755)
  213. if err != nil {
  214. return nil, err
  215. }
  216. return f, nil
  217. }
  218. func (a ServerMessageBlockFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
  219. root = toWinPath(filterFilepath(root))
  220. err := fs.WalkDir(a.share.DirFS(root), ".", func(path string, d fs.DirEntry, err error) error {
  221. statInfo, _ := d.Info()
  222. walkFn(path, statInfo, err)
  223. return nil
  224. })
  225. return err
  226. }
  227. /*
  228. Optional Functions
  229. */
  230. func (a *ServerMessageBlockFileSystemAbstraction) CapacityInfo() {
  231. fsinfo, err := a.share.Statfs(".")
  232. if err != nil {
  233. return
  234. }
  235. fmt.Println(fsinfo)
  236. }
  237. /*
  238. Helper Functions
  239. */
  240. func toWinPath(filename string) string {
  241. backslashed := strings.ReplaceAll(filename, "/", "\\")
  242. return strings.TrimPrefix(backslashed, "\\")
  243. }
  244. func filterFilepath(rawpath string) string {
  245. rawpath = filepath.ToSlash(filepath.Clean(rawpath))
  246. rawpath = strings.TrimSpace(rawpath)
  247. if strings.HasPrefix(rawpath, "./") {
  248. return rawpath[1:]
  249. } else if rawpath == "." || rawpath == "" {
  250. return "/"
  251. }
  252. return rawpath
  253. }