fs.go 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. package utils
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "syscall"
  8. )
  9. // TreeSize walks a directory tree and returns its total size in bytes.
  10. func TreeSize(dir string) (size int64, err error) {
  11. data := make(map[uint64]bool)
  12. err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error {
  13. // Ignore directory sizes
  14. if fileInfo == nil {
  15. return nil
  16. }
  17. s := fileInfo.Size()
  18. if fileInfo.IsDir() || s == 0 {
  19. return nil
  20. }
  21. // Check inode to handle hard links correctly
  22. inode := fileInfo.Sys().(*syscall.Stat_t).Ino
  23. if _, exists := data[inode]; exists {
  24. return nil
  25. }
  26. data[inode] = false
  27. size += s
  28. return nil
  29. })
  30. return
  31. }
  32. // FollowSymlink will follow an existing link and scope it to the root
  33. // path provided.
  34. func FollowSymlinkInScope(link, root string) (string, error) {
  35. prev := "/"
  36. root, err := filepath.Abs(root)
  37. if err != nil {
  38. return "", err
  39. }
  40. link, err = filepath.Abs(link)
  41. if err != nil {
  42. return "", err
  43. }
  44. if !strings.HasPrefix(filepath.Dir(link), root) {
  45. return "", fmt.Errorf("%s is not within %s", link, root)
  46. }
  47. for _, p := range strings.Split(link, "/") {
  48. prev = filepath.Join(prev, p)
  49. prev = filepath.Clean(prev)
  50. for {
  51. stat, err := os.Lstat(prev)
  52. if err != nil {
  53. if os.IsNotExist(err) {
  54. break
  55. }
  56. return "", err
  57. }
  58. if stat.Mode()&os.ModeSymlink == os.ModeSymlink {
  59. dest, err := os.Readlink(prev)
  60. if err != nil {
  61. return "", err
  62. }
  63. switch dest[0] {
  64. case '/':
  65. prev = filepath.Join(root, dest)
  66. case '.':
  67. prev, _ = filepath.Abs(prev)
  68. if prev = filepath.Clean(filepath.Join(filepath.Dir(prev), dest)); len(prev) < len(root) {
  69. prev = filepath.Join(root, filepath.Base(dest))
  70. }
  71. }
  72. } else {
  73. break
  74. }
  75. }
  76. }
  77. return prev, nil
  78. }