du_unix.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. //go:build !windows
  2. // +build !windows
  3. /*
  4. Copyright The containerd Authors.
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. */
  15. package fs
  16. import (
  17. "context"
  18. "os"
  19. "path/filepath"
  20. "syscall"
  21. )
  22. // blocksUnitSize is the unit used by `st_blocks` in `stat` in bytes.
  23. // See https://man7.org/linux/man-pages/man2/stat.2.html
  24. //
  25. // st_blocks
  26. // This field indicates the number of blocks allocated to the
  27. // file, in 512-byte units. (This may be smaller than
  28. // st_size/512 when the file has holes.)
  29. const blocksUnitSize = 512
  30. type inode struct {
  31. // TODO(stevvooe): Can probably reduce memory usage by not tracking
  32. // device, but we can leave this right for now.
  33. dev, ino uint64
  34. }
  35. func newInode(stat *syscall.Stat_t) inode {
  36. return inode{
  37. dev: uint64(stat.Dev), //nolint: unconvert // dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd
  38. ino: uint64(stat.Ino), //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd
  39. }
  40. }
  41. func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
  42. var (
  43. size int64
  44. inodes = map[inode]struct{}{} // expensive!
  45. )
  46. for _, root := range roots {
  47. if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
  48. if err != nil {
  49. return err
  50. }
  51. select {
  52. case <-ctx.Done():
  53. return ctx.Err()
  54. default:
  55. }
  56. stat := fi.Sys().(*syscall.Stat_t)
  57. inoKey := newInode(stat)
  58. if _, ok := inodes[inoKey]; !ok {
  59. inodes[inoKey] = struct{}{}
  60. size += stat.Blocks * blocksUnitSize
  61. }
  62. return nil
  63. }); err != nil {
  64. return Usage{}, err
  65. }
  66. }
  67. return Usage{
  68. Inodes: int64(len(inodes)),
  69. Size: size,
  70. }, nil
  71. }
  72. func diffUsage(ctx context.Context, a, b string) (Usage, error) {
  73. var (
  74. size int64
  75. inodes = map[inode]struct{}{} // expensive!
  76. )
  77. if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
  78. if err != nil {
  79. return err
  80. }
  81. if kind == ChangeKindAdd || kind == ChangeKindModify {
  82. stat := fi.Sys().(*syscall.Stat_t)
  83. inoKey := newInode(stat)
  84. if _, ok := inodes[inoKey]; !ok {
  85. inodes[inoKey] = struct{}{}
  86. size += stat.Blocks * blocksUnitSize
  87. }
  88. return nil
  89. }
  90. return nil
  91. }); err != nil {
  92. return Usage{}, err
  93. }
  94. return Usage{
  95. Inodes: int64(len(inodes)),
  96. Size: size,
  97. }, nil
  98. }