static.go 8.8 KB

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