smbfs.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. package smbfs
  2. import (
  3. "fmt"
  4. "io"
  5. "io/fs"
  6. "log"
  7. "net"
  8. "os"
  9. "path/filepath"
  10. "regexp"
  11. "strings"
  12. "time"
  13. "github.com/hirochachacha/go-smb2"
  14. "imuslab.com/arozos/mod/filesystem/arozfs"
  15. )
  16. /*
  17. Server Message Block.go
  18. This is a file abstraction that mount SMB folders onto ArozOS as virtual drive
  19. */
  20. type ServerMessageBlockFileSystemAbstraction struct {
  21. UUID string
  22. Hierarchy string
  23. root string
  24. ipaddr string
  25. user string
  26. pass string
  27. conn *net.Conn
  28. session *smb2.Session
  29. share *smb2.Share
  30. }
  31. func NewServerMessageBlockFileSystemAbstraction(uuid string, hierarchy string, ipaddr string, rootShare string, username string, password string) (ServerMessageBlockFileSystemAbstraction, error) {
  32. log.Println("[SMB-FS] Connecting to " + uuid + ":/ (" + ipaddr + ")")
  33. nd := net.Dialer{Timeout: 10 * time.Second}
  34. conn, err := nd.Dial("tcp", ipaddr)
  35. if err != nil {
  36. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  37. return ServerMessageBlockFileSystemAbstraction{}, err
  38. }
  39. d := &smb2.Dialer{
  40. Initiator: &smb2.NTLMInitiator{
  41. User: username,
  42. Password: password,
  43. },
  44. }
  45. s, err := d.Dial(conn)
  46. if err != nil {
  47. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  48. return ServerMessageBlockFileSystemAbstraction{}, err
  49. }
  50. //Mound remote storage
  51. fs, err := s.Mount(rootShare)
  52. if err != nil {
  53. log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
  54. return ServerMessageBlockFileSystemAbstraction{}, err
  55. }
  56. fsAbstraction := ServerMessageBlockFileSystemAbstraction{
  57. UUID: uuid,
  58. Hierarchy: hierarchy,
  59. root: rootShare,
  60. ipaddr: ipaddr,
  61. user: username,
  62. pass: password,
  63. conn: &conn,
  64. session: s,
  65. share: fs,
  66. }
  67. //Create a ticker to check for renewing conncetion once an hour
  68. ticker := time.NewTicker(1 * time.Hour)
  69. done := make(chan bool)
  70. go func(s *ServerMessageBlockFileSystemAbstraction) {
  71. for {
  72. select {
  73. case <-done:
  74. return
  75. case <-ticker.C:
  76. //Unable to list share due to session timeout. Close the handler and try connecting again.
  77. s.share.Umount()
  78. time.Sleep(300 * time.Millisecond)
  79. fs, err := s.session.Mount(s.root)
  80. if err != nil {
  81. fmt.Println("[SMBFS] Unable to remount " + s.root)
  82. return
  83. }
  84. fmt.Println("[SMBFS] Session Renewed for " + s.root)
  85. s.share = fs
  86. }
  87. }
  88. }(&fsAbstraction)
  89. log.Println("[SMB-FS] Connected to remote: " + ipaddr)
  90. return fsAbstraction, nil
  91. }
  92. func (a ServerMessageBlockFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
  93. filename = filterFilepath(filename)
  94. filename = toWinPath(filename)
  95. return a.share.Chmod(filename, mode)
  96. }
  97. func (a ServerMessageBlockFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
  98. return arozfs.ErrOperationNotSupported
  99. }
  100. func (a ServerMessageBlockFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
  101. filename = filterFilepath(filename)
  102. filename = toWinPath(filename)
  103. return a.share.Chtimes(filename, atime, mtime)
  104. }
  105. func (a ServerMessageBlockFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
  106. filename = filterFilepath(filename)
  107. f, err := a.share.Create(filename)
  108. if err != nil {
  109. return nil, err
  110. }
  111. af := NewSmbFsFile(f)
  112. return af, nil
  113. }
  114. func (a ServerMessageBlockFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
  115. filename = filterFilepath(filename)
  116. filename = toWinPath(filename)
  117. return a.share.Mkdir(filename, mode)
  118. }
  119. func (a ServerMessageBlockFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
  120. filename = filterFilepath(filename)
  121. filename = toWinPath(filename)
  122. return a.share.MkdirAll(filename, mode)
  123. }
  124. func (a ServerMessageBlockFileSystemAbstraction) Name() string {
  125. return ""
  126. }
  127. func (a ServerMessageBlockFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
  128. filename = toWinPath(filterFilepath(filename))
  129. f, err := a.share.Open(filename)
  130. if err != nil {
  131. return nil, err
  132. }
  133. af := NewSmbFsFile(f)
  134. return af, nil
  135. }
  136. func (a ServerMessageBlockFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
  137. filename = toWinPath(filterFilepath(filename))
  138. f, err := a.share.OpenFile(filename, flag, perm)
  139. if err != nil {
  140. return nil, err
  141. }
  142. af := NewSmbFsFile(f)
  143. return af, nil
  144. }
  145. func (a ServerMessageBlockFileSystemAbstraction) Remove(filename string) error {
  146. filename = filterFilepath(filename)
  147. filename = toWinPath(filename)
  148. return a.share.Remove(filename)
  149. }
  150. func (a ServerMessageBlockFileSystemAbstraction) RemoveAll(filename string) error {
  151. filename = filterFilepath(filename)
  152. filename = toWinPath(filename)
  153. return a.share.RemoveAll(filename)
  154. }
  155. func (a ServerMessageBlockFileSystemAbstraction) Rename(oldname, newname string) error {
  156. oldname = toWinPath(filterFilepath(oldname))
  157. newname = toWinPath(filterFilepath(newname))
  158. return a.share.Rename(oldname, newname)
  159. }
  160. func (a ServerMessageBlockFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
  161. filename = toWinPath(filterFilepath(filename))
  162. return a.share.Stat(filename)
  163. }
  164. func (a ServerMessageBlockFileSystemAbstraction) Close() error {
  165. a.share.Umount()
  166. a.session.Logoff()
  167. conn := *(a.conn)
  168. conn.Close()
  169. return nil
  170. }
  171. /*
  172. Abstraction Utilities
  173. */
  174. func (a ServerMessageBlockFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
  175. if strings.HasPrefix(subpath, a.UUID+":") {
  176. //This is full virtual path. Trim the uuid and correct the subpath
  177. subpath = strings.TrimPrefix(subpath, a.UUID+":")
  178. }
  179. subpath = filterFilepath(subpath)
  180. if a.Hierarchy == "user" {
  181. return toWinPath(filepath.ToSlash(filepath.Clean(filepath.Join("users", username, subpath)))), nil
  182. } else if a.Hierarchy == "public" {
  183. return toWinPath(filepath.ToSlash(filepath.Clean(subpath))), nil
  184. }
  185. return "", arozfs.ErrVpathResolveFailed
  186. }
  187. func (a ServerMessageBlockFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
  188. fullpath = filterFilepath(fullpath)
  189. fullpath = strings.TrimPrefix(fullpath, "\\")
  190. vpath := a.UUID + ":/" + strings.ReplaceAll(fullpath, "\\", "/")
  191. return vpath, nil
  192. }
  193. func (a ServerMessageBlockFileSystemAbstraction) FileExists(realpath string) bool {
  194. realpath = toWinPath(filterFilepath(realpath))
  195. f, err := a.share.Open(realpath)
  196. if err != nil {
  197. return false
  198. }
  199. f.Close()
  200. return true
  201. }
  202. func (a ServerMessageBlockFileSystemAbstraction) IsDir(realpath string) bool {
  203. realpath = filterFilepath(realpath)
  204. realpath = toWinPath(realpath)
  205. stx, err := a.share.Stat(realpath)
  206. if err != nil {
  207. return false
  208. }
  209. return stx.IsDir()
  210. }
  211. func (a ServerMessageBlockFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
  212. realpathWildcard = strings.ReplaceAll(realpathWildcard, "[", "?")
  213. realpathWildcard = strings.ReplaceAll(realpathWildcard, "]", "?")
  214. matches, err := a.share.Glob(realpathWildcard)
  215. if err != nil {
  216. return []string{}, err
  217. }
  218. return matches, nil
  219. }
  220. func (a ServerMessageBlockFileSystemAbstraction) GetFileSize(realpath string) int64 {
  221. realpath = toWinPath(filterFilepath(realpath))
  222. stat, err := a.share.Stat(realpath)
  223. if err != nil {
  224. return 0
  225. }
  226. return stat.Size()
  227. }
  228. func (a ServerMessageBlockFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
  229. realpath = toWinPath(filterFilepath(realpath))
  230. stat, err := a.share.Stat(realpath)
  231. if err != nil {
  232. return 0, nil
  233. }
  234. return stat.ModTime().Unix(), nil
  235. }
  236. func (a ServerMessageBlockFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
  237. filename = toWinPath(filterFilepath(filename))
  238. return a.share.WriteFile(filename, content, mode)
  239. }
  240. func (a ServerMessageBlockFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
  241. filename = toWinPath(filterFilepath(filename))
  242. return a.share.ReadFile(filename)
  243. }
  244. func (a ServerMessageBlockFileSystemAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
  245. filename = toWinPath(filterFilepath(filename))
  246. fis, err := a.share.ReadDir(filename)
  247. if err != nil {
  248. return []fs.DirEntry{}, err
  249. }
  250. dirEntires := []fs.DirEntry{}
  251. for _, fi := range fis {
  252. if fi.Name() == "System Volume Information" || fi.Name() == "$RECYCLE.BIN" || fi.Name() == "$MFT" {
  253. //System folders. Hide it
  254. continue
  255. }
  256. dirEntires = append(dirEntires, newDirEntryFromFileInfo(fi))
  257. }
  258. return dirEntires, nil
  259. }
  260. func (a ServerMessageBlockFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
  261. filename = toWinPath(filterFilepath(filename))
  262. f, err := a.share.OpenFile(filename, os.O_CREATE|os.O_WRONLY, mode)
  263. if err != nil {
  264. return err
  265. }
  266. p := make([]byte, 32768)
  267. for {
  268. _, err := stream.Read(p)
  269. if err != nil {
  270. if err == io.EOF {
  271. break
  272. } else {
  273. return err
  274. }
  275. }
  276. _, err = f.Write(p)
  277. if err != nil {
  278. return err
  279. }
  280. }
  281. return nil
  282. }
  283. func (a ServerMessageBlockFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
  284. filename = toWinPath(filterFilepath(filename))
  285. f, err := a.share.OpenFile(filename, os.O_RDONLY, 0755)
  286. if err != nil {
  287. return nil, err
  288. }
  289. return f, nil
  290. }
  291. func (a ServerMessageBlockFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
  292. root = toWinPath(filterFilepath(root))
  293. err := fs.WalkDir(a.share.DirFS(root), ".", func(path string, d fs.DirEntry, err error) error {
  294. if err != nil {
  295. return err
  296. }
  297. statInfo, err := d.Info()
  298. if err != nil {
  299. return err
  300. }
  301. walkFn(filepath.ToSlash(filepath.Join(root, path)), statInfo, err)
  302. return nil
  303. })
  304. return err
  305. }
  306. /*
  307. Optional Functions
  308. */
  309. func (a *ServerMessageBlockFileSystemAbstraction) CapacityInfo() {
  310. fsinfo, err := a.share.Statfs(".")
  311. if err != nil {
  312. return
  313. }
  314. fmt.Println(fsinfo)
  315. }
  316. /*
  317. Helper Functions
  318. */
  319. func toWinPath(filename string) string {
  320. backslashed := strings.ReplaceAll(filename, "/", "\\")
  321. return strings.TrimPrefix(backslashed, "\\")
  322. }
  323. func filterFilepath(rawpath string) string {
  324. rawpath = filepath.ToSlash(filepath.Clean(rawpath))
  325. rawpath = strings.TrimSpace(rawpath)
  326. if strings.HasPrefix(rawpath, "./") {
  327. return rawpath[1:]
  328. } else if rawpath == "." || rawpath == "" {
  329. return "/"
  330. }
  331. return rawpath
  332. }
  333. func wildCardToRegexp(pattern string) string {
  334. var result strings.Builder
  335. for i, literal := range strings.Split(pattern, "*") {
  336. // Replace * with .*
  337. if i > 0 {
  338. result.WriteString(".*")
  339. }
  340. // Quote any regular expression meta characters in the
  341. // literal text.
  342. result.WriteString(regexp.QuoteMeta(literal))
  343. }
  344. return result.String()
  345. }