static.go 9.8 KB

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