du.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /* du.c - disk usage program.
  2. *
  3. * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
  4. *
  5. * See http://opengroup.org/onlinepubs/9699919799/utilities/du.html
  6. *
  7. * TODO: cleanup (should seen_inode be lib?)
  8. * 32 bit du -b maxes out at 4 gigs (instead of 2 terabytes via *512 trick)
  9. * because dirtree->extra is a long.
  10. USE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsxb[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
  11. config DU
  12. bool "du"
  13. default y
  14. help
  15. usage: du [-d N] [-askxHLlmc] [FILE...]
  16. Show disk usage, space consumed by files and directories.
  17. Size in:
  18. -b Apparent bytes (directory listing size, not space used)
  19. -k 1024 byte blocks (default)
  20. -K 512 byte blocks (posix)
  21. -m Megabytes
  22. -h Human readable (e.g., 1K 243M 2G)
  23. What to show:
  24. -a All files, not just directories
  25. -H Follow symlinks on cmdline
  26. -L Follow all symlinks
  27. -s Only total size of each argument
  28. -x Don't leave this filesystem
  29. -c Cumulative total
  30. -d N Only depth < N
  31. -l Disable hardlink filter
  32. */
  33. #define FOR_du
  34. #include "toys.h"
  35. GLOBALS(
  36. long d;
  37. unsigned long depth, total;
  38. dev_t st_dev;
  39. void *inodes;
  40. )
  41. typedef struct node_size {
  42. struct dirtree *node;
  43. long size;
  44. } node_size;
  45. // Print the size and name, given size in bytes
  46. static void print(long long size, struct dirtree *node)
  47. {
  48. char *name = "total";
  49. if (TT.depth > TT.d) return;
  50. if (FLAG(h)) {
  51. human_readable(toybuf, size, 0);
  52. printf("%s", toybuf);
  53. } else {
  54. int bits = 10;
  55. if (FLAG(K)) bits = 9;
  56. else if (FLAG(m)) bits = 20;
  57. if (FLAG(b) && bits == 10 && !FLAG(k)) printf("%llu", size);
  58. else printf("%llu", (size>>bits)+!!(size&((1<<bits)-1)));
  59. }
  60. if (node) name = dirtree_path(node, NULL);
  61. xprintf("\t%s\n", name);
  62. if (node) free(name);
  63. }
  64. // Return whether or not we've seen this inode+dev, adding it to the list if
  65. // we haven't.
  66. static int seen_inode(void **list, struct stat *st)
  67. {
  68. if (!st) llist_traverse(st, free);
  69. // Skipping dir nodes isn't _quite_ right. They're not hardlinked, but could
  70. // be bind mounted. Still, it's more efficient and the archivers can't use
  71. // hardlinked directory info anyway. (Note that we don't catch bind mounted
  72. // _files_ because it doesn't change st_nlink.)
  73. else if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
  74. struct inode_list {
  75. struct inode_list *next;
  76. ino_t ino;
  77. dev_t dev;
  78. } *new;
  79. for (new = *list; new; new = new->next)
  80. if(new->ino == st->st_ino && new->dev == st->st_dev)
  81. return 1;
  82. new = xzalloc(sizeof(*new));
  83. new->ino = st->st_ino;
  84. new->dev = st->st_dev;
  85. new->next = *list;
  86. *list = new;
  87. }
  88. return 0;
  89. }
  90. // dirtree callback, compute/display size of node
  91. static int do_du(struct dirtree *node)
  92. {
  93. unsigned long blocks;
  94. if (!node->parent) TT.st_dev = node->st.st_dev;
  95. else if (!dirtree_notdotdot(node)) return 0;
  96. // detect swiching filesystems
  97. if (FLAG(x) && (TT.st_dev != node->st.st_dev))
  98. return 0;
  99. // Don't loop endlessly on recursive directory symlink
  100. if (FLAG(L)) {
  101. struct dirtree *try = node;
  102. while ((try = try->parent))
  103. if (node->st.st_dev==try->st.st_dev && node->st.st_ino==try->st.st_ino)
  104. return 0;
  105. }
  106. // Don't count hard links twice
  107. if (!FLAG(l) && !node->again)
  108. if (seen_inode(&TT.inodes, &node->st)) return 0;
  109. // Collect child info before printing directory size
  110. if (S_ISDIR(node->st.st_mode)) {
  111. if (!node->again) {
  112. TT.depth++;
  113. return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!FLAG(L));
  114. } else TT.depth--;
  115. }
  116. // Modern compilers' optimizers are insane and think signed overflow
  117. // behaves differently than unsigned overflow. Sigh. Big hammer.
  118. blocks = FLAG(b) ? node->st.st_size : node->st.st_blocks;
  119. blocks += (unsigned long)node->extra;
  120. node->extra = blocks;
  121. if (node->parent)
  122. node->parent->extra = (unsigned long)node->parent->extra+blocks;
  123. else TT.total += node->extra;
  124. if (FLAG(a) || !node->parent || (S_ISDIR(node->st.st_mode) && !FLAG(s))) {
  125. blocks = node->extra;
  126. print(FLAG(b) ? blocks : blocks*512LL, node);
  127. }
  128. return 0;
  129. }
  130. void du_main(void)
  131. {
  132. char *noargs[] = {".", 0}, **args;
  133. // Loop over command line arguments, recursing through children
  134. for (args = toys.optc ? toys.optargs : noargs; *args; args++)
  135. dirtree_flagread(*args, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)),
  136. do_du);
  137. if (FLAG(c)) print(FLAG(b) ? TT.total : TT.total*512, 0);
  138. if (CFG_TOYBOX_FREE) seen_inode(TT.inodes, 0);
  139. }