static.go 9.3 KB

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