smbfs.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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/arozfs"
  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, rootShare string, username string, password string) (ServerMessageBlockFileSystemAbstraction, error) {
  29. log.Println("[SMB-FS] Connecting to " + uuid + ":/ (" + ipaddr + ")")
  30. nd := net.Dialer{Timeout: 10 * time.Second}
  31. conn, err := nd.Dial("tcp", ipaddr)
  32. if err != nil {
  33. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  34. return ServerMessageBlockFileSystemAbstraction{}, err
  35. }
  36. d := &smb2.Dialer{
  37. Initiator: &smb2.NTLMInitiator{
  38. User: username,
  39. Password: password,
  40. },
  41. }
  42. s, err := d.Dial(conn)
  43. if err != nil {
  44. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  45. return ServerMessageBlockFileSystemAbstraction{}, err
  46. }
  47. //Mound remote storage
  48. fs, err := s.Mount(rootShare)
  49. if err != nil {
  50. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  51. return ServerMessageBlockFileSystemAbstraction{}, err
  52. }
  53. log.Println("[SMB-FS] Connected to remote: " + ipaddr)
  54. return ServerMessageBlockFileSystemAbstraction{
  55. UUID: uuid,
  56. Hierarchy: hierarchy,
  57. ipaddr: ipaddr,
  58. user: username,
  59. conn: &conn,
  60. session: s,
  61. share: fs,
  62. }, nil
  63. }
  64. func (a ServerMessageBlockFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
  65. filename = filterFilepath(filename)
  66. filename = toWinPath(filename)
  67. return a.share.Chmod(filename, mode)
  68. }
  69. func (a ServerMessageBlockFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
  70. return arozfs.ErrOperationNotSupported
  71. }
  72. func (a ServerMessageBlockFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
  73. filename = filterFilepath(filename)
  74. filename = toWinPath(filename)
  75. return a.share.Chtimes(filename, atime, mtime)
  76. }
  77. func (a ServerMessageBlockFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
  78. filename = filterFilepath(filename)
  79. f, err := a.share.Create(filename)
  80. if err != nil {
  81. return nil, err
  82. }
  83. af := NewSmbFsFile(f)
  84. return af, nil
  85. }
  86. func (a ServerMessageBlockFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
  87. filename = filterFilepath(filename)
  88. filename = toWinPath(filename)
  89. return a.share.Mkdir(filename, mode)
  90. }
  91. func (a ServerMessageBlockFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
  92. filename = filterFilepath(filename)
  93. filename = toWinPath(filename)
  94. return a.share.MkdirAll(filename, mode)
  95. }
  96. func (a ServerMessageBlockFileSystemAbstraction) Name() string {
  97. return ""
  98. }
  99. func (a ServerMessageBlockFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
  100. filename = toWinPath(filterFilepath(filename))
  101. f, err := a.share.Open(filename)
  102. if err != nil {
  103. return nil, err
  104. }
  105. af := NewSmbFsFile(f)
  106. return af, nil
  107. }
  108. func (a ServerMessageBlockFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
  109. filename = toWinPath(filterFilepath(filename))
  110. f, err := a.share.OpenFile(filename, flag, perm)
  111. if err != nil {
  112. return nil, err
  113. }
  114. af := NewSmbFsFile(f)
  115. return af, nil
  116. }
  117. func (a ServerMessageBlockFileSystemAbstraction) Remove(filename string) error {
  118. filename = filterFilepath(filename)
  119. filename = toWinPath(filename)
  120. return a.share.Remove(filename)
  121. }
  122. func (a ServerMessageBlockFileSystemAbstraction) RemoveAll(filename string) error {
  123. filename = filterFilepath(filename)
  124. filename = toWinPath(filename)
  125. return a.share.RemoveAll(filename)
  126. }
  127. func (a ServerMessageBlockFileSystemAbstraction) Rename(oldname, newname string) error {
  128. oldname = toWinPath(filterFilepath(oldname))
  129. newname = toWinPath(filterFilepath(newname))
  130. return a.share.Rename(oldname, newname)
  131. }
  132. func (a ServerMessageBlockFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
  133. filename = toWinPath(filterFilepath(filename))
  134. return a.share.Stat(filename)
  135. }
  136. func (a ServerMessageBlockFileSystemAbstraction) Close() error {
  137. a.share.Umount()
  138. a.session.Logoff()
  139. conn := *(a.conn)
  140. conn.Close()
  141. return nil
  142. }
  143. /*
  144. Abstraction Utilities
  145. */
  146. func (a ServerMessageBlockFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
  147. if strings.HasPrefix(subpath, a.UUID+":") {
  148. //This is full virtual path. Trim the uuid and correct the subpath
  149. subpath = strings.TrimPrefix(subpath, a.UUID+":")
  150. }
  151. subpath = filterFilepath(subpath)
  152. if a.Hierarchy == "user" {
  153. return toWinPath(filepath.ToSlash(filepath.Clean(filepath.Join("users", username, subpath)))), nil
  154. } else if a.Hierarchy == "public" {
  155. return toWinPath(filepath.ToSlash(filepath.Clean(subpath))), nil
  156. }
  157. return "", arozfs.ErrVpathResolveFailed
  158. }
  159. func (a ServerMessageBlockFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
  160. fullpath = filterFilepath(fullpath)
  161. fullpath = strings.TrimPrefix(fullpath, "\\")
  162. vpath := a.UUID + ":/" + filterFilepath(fullpath)
  163. return vpath, nil
  164. }
  165. func (a ServerMessageBlockFileSystemAbstraction) FileExists(realpath string) bool {
  166. realpath = toWinPath(filterFilepath(realpath))
  167. f, err := a.share.Open(realpath)
  168. if err != nil {
  169. return false
  170. }
  171. f.Close()
  172. return true
  173. }
  174. func (a ServerMessageBlockFileSystemAbstraction) IsDir(realpath string) bool {
  175. realpath = filterFilepath(realpath)
  176. realpath = toWinPath(realpath)
  177. stx, err := a.share.Stat(realpath)
  178. if err != nil {
  179. return false
  180. }
  181. return stx.IsDir()
  182. }
  183. func (a ServerMessageBlockFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
  184. realpathWildcard = toWinPath(filterFilepath(realpathWildcard))
  185. fmt.Println("GLOBING", realpathWildcard)
  186. filteredMatches := []string{}
  187. matches, err := a.share.Glob(realpathWildcard)
  188. if err != nil {
  189. return []string{}, err
  190. }
  191. for _, thisMatch := range matches {
  192. filteredMatches = append(filteredMatches, filepath.ToSlash(thisMatch))
  193. fmt.Println(filepath.ToSlash(thisMatch))
  194. }
  195. return filteredMatches, nil
  196. }
  197. func (a ServerMessageBlockFileSystemAbstraction) GetFileSize(realpath string) int64 {
  198. realpath = toWinPath(filterFilepath(realpath))
  199. stat, err := a.share.Stat(realpath)
  200. if err != nil {
  201. return 0
  202. }
  203. return stat.Size()
  204. }
  205. func (a ServerMessageBlockFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
  206. realpath = toWinPath(filterFilepath(realpath))
  207. stat, err := a.share.Stat(realpath)
  208. if err != nil {
  209. return 0, nil
  210. }
  211. return stat.ModTime().Unix(), nil
  212. }
  213. func (a ServerMessageBlockFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
  214. filename = toWinPath(filterFilepath(filename))
  215. return a.share.WriteFile(filename, content, mode)
  216. }
  217. func (a ServerMessageBlockFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
  218. filename = toWinPath(filterFilepath(filename))
  219. return a.share.ReadFile(filename)
  220. }
  221. func (a ServerMessageBlockFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
  222. filename = toWinPath(filterFilepath(filename))
  223. f, err := a.share.OpenFile(filename, os.O_CREATE|os.O_WRONLY, mode)
  224. if err != nil {
  225. return err
  226. }
  227. p := make([]byte, 32768)
  228. for {
  229. _, err := stream.Read(p)
  230. if err != nil {
  231. if err == io.EOF {
  232. break
  233. } else {
  234. return err
  235. }
  236. }
  237. _, err = f.Write(p)
  238. if err != nil {
  239. return err
  240. }
  241. }
  242. return nil
  243. }
  244. func (a ServerMessageBlockFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
  245. filename = toWinPath(filterFilepath(filename))
  246. f, err := a.share.OpenFile(filename, os.O_RDONLY, 0755)
  247. if err != nil {
  248. return nil, err
  249. }
  250. return f, nil
  251. }
  252. func (a ServerMessageBlockFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
  253. root = toWinPath(filterFilepath(root))
  254. err := fs.WalkDir(a.share.DirFS(root), ".", func(path string, d fs.DirEntry, err error) error {
  255. if err != nil {
  256. return err
  257. }
  258. statInfo, err := d.Info()
  259. if err != nil {
  260. return err
  261. }
  262. walkFn(filepath.ToSlash(filepath.Join(root, path)), statInfo, err)
  263. return nil
  264. })
  265. return err
  266. }
  267. /*
  268. Optional Functions
  269. */
  270. func (a *ServerMessageBlockFileSystemAbstraction) CapacityInfo() {
  271. fsinfo, err := a.share.Statfs(".")
  272. if err != nil {
  273. return
  274. }
  275. fmt.Println(fsinfo)
  276. }
  277. /*
  278. Helper Functions
  279. */
  280. func toWinPath(filename string) string {
  281. backslashed := strings.ReplaceAll(filename, "/", "\\")
  282. return strings.TrimPrefix(backslashed, "\\")
  283. }
  284. func filterFilepath(rawpath string) string {
  285. rawpath = filepath.ToSlash(filepath.Clean(rawpath))
  286. rawpath = strings.TrimSpace(rawpath)
  287. if strings.HasPrefix(rawpath, "./") {
  288. return rawpath[1:]
  289. } else if rawpath == "." || rawpath == "" {
  290. return "/"
  291. }
  292. return rawpath
  293. }