smbfs.go 9.5 KB

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