static.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. package filesystem
  2. import (
  3. "crypto/md5"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "log"
  10. "mime"
  11. "os"
  12. "os/exec"
  13. "path/filepath"
  14. "runtime"
  15. "strconv"
  16. "strings"
  17. "net/url"
  18. mimetype "github.com/gabriel-vasile/mimetype"
  19. "imuslab.com/arozos/mod/filesystem/shortcut"
  20. )
  21. //Structure definations
  22. type FileData struct {
  23. Filename string
  24. Filepath string
  25. Realpath string
  26. IsDir bool
  27. Filesize int64
  28. Displaysize string
  29. ModTime int64
  30. IsShared bool
  31. Shortcut *shortcut.ShortcutData //This will return nil or undefined if it is not a shortcut file
  32. }
  33. type TrashedFile struct {
  34. Filename string
  35. Filepath string
  36. FileExt string
  37. IsDir bool
  38. Filesize int64
  39. RemoveTimestamp int64
  40. RemoveDate string
  41. OriginalPath string
  42. OriginalFilename string
  43. }
  44. type FileProperties struct {
  45. VirtualPath string
  46. StoragePath string
  47. Basename string
  48. VirtualDirname string
  49. StorageDirname string
  50. Ext string
  51. MimeType string
  52. Filesize int64
  53. Permission string
  54. LastModTime string
  55. LastModUnix int64
  56. IsDirectory bool
  57. }
  58. /*
  59. HierarchySpecificConfig Template
  60. */
  61. type EmptyHierarchySpecificConfig struct {
  62. HierarchyType string
  63. }
  64. func (e EmptyHierarchySpecificConfig) ResolveVrootPath(string, string) (string, error) {
  65. return "", nil
  66. }
  67. func (e EmptyHierarchySpecificConfig) ResolveRealPath(string, string) (string, error) {
  68. return "", nil
  69. }
  70. var DefaultEmptyHierarchySpecificConfig = EmptyHierarchySpecificConfig{
  71. HierarchyType: "placeholder",
  72. }
  73. //Check if the two file system are identical.
  74. func MatchingFileSystem(fsa *FileSystemHandler, fsb *FileSystemHandler) bool {
  75. return fsa.Filesystem == fsb.Filesystem
  76. }
  77. //Get the ID part of a virtual path, return ID, subpath and error
  78. func GetIDFromVirtualPath(vpath string) (string, string, error) {
  79. if !strings.Contains(vpath, ":") {
  80. return "", "", errors.New("Path missing Virtual Device ID. Given: " + vpath)
  81. }
  82. //Clean up the virutal path
  83. vpath = filepath.ToSlash(filepath.Clean(vpath))
  84. tmp := strings.Split(vpath, ":")
  85. vdID := tmp[0]
  86. pathSlice := tmp[1:]
  87. path := strings.Join(pathSlice, ":")
  88. return vdID, path, nil
  89. }
  90. func GetFileDataFromPath(fsh *FileSystemHandler, vpath string, realpath string, sizeRounding int) FileData {
  91. fileSize := fsh.FileSystemAbstraction.GetFileSize(realpath)
  92. displaySize := GetFileDisplaySize(fileSize, sizeRounding)
  93. modtime, _ := fsh.FileSystemAbstraction.GetModTime(realpath)
  94. var shortcutInfo *shortcut.ShortcutData = nil
  95. if filepath.Ext(realpath) == ".shortcut" {
  96. scd, err := shortcut.ReadShortcut(realpath)
  97. if err == nil {
  98. shortcutInfo = scd
  99. }
  100. }
  101. return FileData{
  102. Filename: filepath.Base(realpath),
  103. Filepath: vpath,
  104. Realpath: filepath.ToSlash(realpath),
  105. IsDir: fsh.FileSystemAbstraction.IsDir(realpath),
  106. Filesize: fileSize,
  107. Displaysize: displaySize,
  108. ModTime: modtime,
  109. IsShared: false,
  110. Shortcut: shortcutInfo,
  111. }
  112. }
  113. func CheckMounted(mountpoint string) bool {
  114. if runtime.GOOS == "windows" {
  115. //Windows
  116. //Check if the given folder exists
  117. info, err := os.Stat(mountpoint)
  118. if os.IsNotExist(err) {
  119. return false
  120. }
  121. return info.IsDir()
  122. } else {
  123. //Linux
  124. cmd := exec.Command("mountpoint", mountpoint)
  125. out, err := cmd.CombinedOutput()
  126. if err != nil {
  127. return false
  128. }
  129. outstring := strings.TrimSpace(string(out))
  130. if strings.Contains(outstring, " is a mountpoint") {
  131. return true
  132. } else {
  133. return false
  134. }
  135. }
  136. }
  137. func MountDevice(mountpt string, mountdev string, filesystem string) error {
  138. //Check if running under sudo mode and in linux
  139. if runtime.GOOS == "linux" {
  140. //Try to mount the file system
  141. if mountdev == "" {
  142. return errors.New("Disk with automount enabled has no mountdev value: " + mountpt)
  143. }
  144. if mountpt == "" {
  145. return errors.New("Invalid storage.json. Mount point not given or not exists for " + mountdev)
  146. }
  147. //Check if device exists
  148. if !FileExists(mountdev) {
  149. //Device driver not exists.
  150. return errors.New("Device not exists: " + mountdev)
  151. }
  152. //Mount the device
  153. if CheckMounted(mountpt) {
  154. log.Println(mountpt + " already mounted.")
  155. } else {
  156. log.Println("Mounting " + mountdev + "(" + filesystem + ") to " + filepath.Clean(mountpt))
  157. cmd := exec.Command("mount", "-t", filesystem, mountdev, filepath.Clean(mountpt))
  158. cmd.Stdout = os.Stdout
  159. cmd.Stderr = os.Stderr
  160. cmd.Run()
  161. }
  162. //Check if the path exists
  163. if !FileExists(mountpt) {
  164. //Mounted but path still not found. Skip this device
  165. return errors.New("Unable to find " + mountpt)
  166. }
  167. } else {
  168. return errors.New("Unsupported platform")
  169. }
  170. return nil
  171. }
  172. func GetFileSize(filename string) int64 {
  173. fi, err := os.Stat(filename)
  174. if err != nil {
  175. return 0
  176. }
  177. // get the size
  178. return fi.Size()
  179. }
  180. func IsInsideHiddenFolder(path string) bool {
  181. thisPathInfo := filepath.ToSlash(filepath.Clean(path))
  182. pathData := strings.Split(thisPathInfo, "/")
  183. for _, thispd := range pathData {
  184. if len(thispd) > 0 && thispd[:1] == "." {
  185. //This path contain one of the folder is hidden
  186. return true
  187. }
  188. }
  189. return false
  190. }
  191. /*
  192. Wildcard Replacement Glob, design to hanle path with [ or ] inside.
  193. You can also pass in normal path for globing if you are not sure.
  194. */
  195. func WGlob(path string) ([]string, error) {
  196. files, err := filepath.Glob(path)
  197. if err != nil {
  198. return []string{}, err
  199. }
  200. if strings.Contains(path, "[") == true || strings.Contains(path, "]") == true {
  201. if len(files) == 0 {
  202. //Handle reverse check. Replace all [ and ] with ?
  203. newSearchPath := strings.ReplaceAll(path, "[", "?")
  204. newSearchPath = strings.ReplaceAll(newSearchPath, "]", "?")
  205. //Scan with all the similar structure except [ and ]
  206. tmpFilelist, _ := filepath.Glob(newSearchPath)
  207. for _, file := range tmpFilelist {
  208. file = filepath.ToSlash(file)
  209. if strings.Contains(file, filepath.ToSlash(filepath.Dir(path))) {
  210. files = append(files, file)
  211. }
  212. }
  213. }
  214. }
  215. //Convert all filepaths to slash
  216. for i := 0; i < len(files); i++ {
  217. files[i] = filepath.ToSlash(files[i])
  218. }
  219. return files, nil
  220. }
  221. /*
  222. Get Directory size, require filepath and include Hidden files option(true / false)
  223. Return total file size and file count
  224. */
  225. func GetDirctorySize(filename string, includeHidden bool) (int64, int) {
  226. var size int64 = 0
  227. var fileCount int = 0
  228. err := filepath.Walk(filename, func(thisFilename string, info os.FileInfo, err error) error {
  229. if err != nil {
  230. return err
  231. }
  232. if !info.IsDir() {
  233. if includeHidden {
  234. //append all into the file count and size
  235. size += info.Size()
  236. fileCount++
  237. } else {
  238. //Check if this is hidden
  239. if !IsInsideHiddenFolder(thisFilename) {
  240. size += info.Size()
  241. fileCount++
  242. }
  243. }
  244. }
  245. return err
  246. })
  247. if err != nil {
  248. return 0, fileCount
  249. }
  250. return size, fileCount
  251. }
  252. func GetFileDisplaySize(filesize int64, rounding int) string {
  253. precisionString := "%." + strconv.Itoa(rounding) + "f"
  254. bytes := float64(filesize)
  255. kilobytes := float64(bytes / 1024)
  256. if kilobytes < 1 {
  257. return fmt.Sprintf(precisionString, bytes) + "Bytes"
  258. }
  259. megabytes := float64(kilobytes / 1024)
  260. if megabytes < 1 {
  261. return fmt.Sprintf(precisionString, kilobytes) + "KB"
  262. }
  263. gigabytes := float64(megabytes / 1024)
  264. if gigabytes < 1 {
  265. return fmt.Sprintf(precisionString, megabytes) + "MB"
  266. }
  267. terabytes := float64(gigabytes / 1024)
  268. if terabytes < 1 {
  269. return fmt.Sprintf(precisionString, gigabytes) + "GB"
  270. }
  271. petabytes := float64(terabytes / 1024)
  272. if petabytes < 1 {
  273. return fmt.Sprintf(precisionString, terabytes) + "TB"
  274. }
  275. exabytes := float64(petabytes / 1024)
  276. if exabytes < 1 {
  277. return fmt.Sprintf(precisionString, petabytes) + "PB"
  278. }
  279. zettabytes := float64(exabytes / 1024)
  280. if zettabytes < 1 {
  281. return fmt.Sprintf(precisionString, exabytes) + "EB"
  282. }
  283. return fmt.Sprintf(precisionString, zettabytes) + "ZB"
  284. }
  285. func DecodeURI(inputPath string) string {
  286. inputPath = strings.ReplaceAll(inputPath, "+", "{{plus_sign}}")
  287. inputPath, _ = url.QueryUnescape(inputPath)
  288. inputPath = strings.ReplaceAll(inputPath, "{{plus_sign}}", "+")
  289. return inputPath
  290. }
  291. func GetMime(filename string) (string, string, error) {
  292. fileMime, err := mimetype.DetectFile(filename)
  293. if err != nil {
  294. return mime.TypeByExtension(filepath.Ext(filename)), filepath.Ext(filename), nil
  295. }
  296. return fileMime.String(), fileMime.Extension(), nil
  297. }
  298. func GetModTime(filepath string) (int64, error) {
  299. f, err := os.Open(filepath)
  300. if err != nil {
  301. return -1, err
  302. }
  303. statinfo, err := f.Stat()
  304. if err != nil {
  305. return -1, err
  306. }
  307. f.Close()
  308. return statinfo.ModTime().Unix(), nil
  309. }
  310. func UnderTheSameRoot(srcAbs string, destAbs string) (bool, error) {
  311. srcRoot, err := GetPhysicalRootFromPath(srcAbs)
  312. if err != nil {
  313. return false, err
  314. }
  315. destRoot, err := GetPhysicalRootFromPath(destAbs)
  316. if err != nil {
  317. return false, err
  318. }
  319. if srcRoot != "" && destRoot != "" {
  320. if srcRoot == destRoot {
  321. //apply fast move
  322. return true, nil
  323. }
  324. }
  325. return false, nil
  326. }
  327. //Get the physical root of a given filepath, e.g. C: or /home
  328. func GetPhysicalRootFromPath(filename string) (string, error) {
  329. filename, err := filepath.Abs(filename)
  330. if err != nil {
  331. return "", err
  332. }
  333. if filename[:1] == "/" {
  334. //Handle cases like /home/pi/foo.txt => return home
  335. filename = filename[1:]
  336. }
  337. filename = strings.TrimSpace(filename)
  338. if filename == "" {
  339. return "", nil
  340. }
  341. filename = filepath.ToSlash(filepath.Clean(filename))
  342. pathChunks := strings.Split(filename, "/")
  343. return pathChunks[0], nil
  344. }
  345. func GetFileSHA256Sum(filename string) (string, error) {
  346. f, err := os.Open(filename)
  347. if err != nil {
  348. return "", err
  349. }
  350. defer f.Close()
  351. h := sha256.New()
  352. if _, err := io.Copy(h, f); err != nil {
  353. return "", err
  354. }
  355. return hex.EncodeToString(h.Sum(nil)), nil
  356. }
  357. func GetFileMD5Sum(fsh *FileSystemHandler, rpath string) (string, error) {
  358. file, err := fsh.FileSystemAbstraction.ReadStream(rpath)
  359. if err != nil {
  360. return "", err
  361. }
  362. h := md5.New()
  363. if _, err := io.Copy(h, file); err != nil {
  364. return "", err
  365. }
  366. file.Close()
  367. return hex.EncodeToString(h.Sum(nil)), nil
  368. }