du.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini du implementation for busybox
  4. *
  5. * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu
  6. * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
  7. * Copyright (C) 2002 Edward Betts <edward@debian.org>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22. *
  23. */
  24. /* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
  25. /* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
  26. /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
  27. *
  28. * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
  29. * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
  30. * The -d option allows setting of max depth (similar to gnu --max-depth).
  31. * 2) Fixed incorrect size calculations for links and directories, especially
  32. * when errors occurred. Calculates sizes should now match gnu du output.
  33. * 3) Added error checking of output.
  34. * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
  35. */
  36. #include <stdlib.h>
  37. #include <limits.h>
  38. #include <unistd.h>
  39. #include <dirent.h>
  40. #include <sys/stat.h>
  41. #include "busybox.h"
  42. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  43. # ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
  44. static unsigned long disp_hr = KILOBYTE;
  45. # else
  46. static unsigned long disp_hr = 512;
  47. # endif
  48. #elif defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
  49. static unsigned int disp_k = 1;
  50. #else
  51. static unsigned int disp_k; /* bss inits to 0 */
  52. #endif
  53. static int max_print_depth = INT_MAX;
  54. static int count_hardlinks = 1;
  55. static int status
  56. #if EXIT_SUCCESS == 0
  57. = EXIT_SUCCESS
  58. #endif
  59. ;
  60. static int print_files;
  61. static int slink_depth;
  62. static int du_depth;
  63. static int one_file_system;
  64. static dev_t dir_dev;
  65. static void print(long size, char *filename)
  66. {
  67. /* TODO - May not want to defer error checking here. */
  68. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  69. bb_printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr),
  70. filename);
  71. #else
  72. if (disp_k) {
  73. size++;
  74. size >>= 1;
  75. }
  76. bb_printf("%ld\t%s\n", size, filename);
  77. #endif
  78. }
  79. /* tiny recursive du */
  80. static long du(char *filename)
  81. {
  82. struct stat statbuf;
  83. long sum;
  84. if ((lstat(filename, &statbuf)) != 0) {
  85. bb_perror_msg("%s", filename);
  86. status = EXIT_FAILURE;
  87. return 0;
  88. }
  89. if (one_file_system) {
  90. if (du_depth == 0) {
  91. dir_dev = statbuf.st_dev;
  92. } else if (dir_dev != statbuf.st_dev) {
  93. return 0;
  94. }
  95. }
  96. sum = statbuf.st_blocks;
  97. if (S_ISLNK(statbuf.st_mode)) {
  98. if (slink_depth > du_depth) { /* -H or -L */
  99. if ((stat(filename, &statbuf)) != 0) {
  100. bb_perror_msg("%s", filename);
  101. status = EXIT_FAILURE;
  102. return 0;
  103. }
  104. sum = statbuf.st_blocks;
  105. if (slink_depth == 1) {
  106. slink_depth = INT_MAX; /* Convert -H to -L. */
  107. }
  108. }
  109. }
  110. if (statbuf.st_nlink > count_hardlinks) {
  111. /* Add files/directories with links only once */
  112. if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
  113. return 0;
  114. }
  115. add_to_ino_dev_hashtable(&statbuf, NULL);
  116. }
  117. if (S_ISDIR(statbuf.st_mode)) {
  118. DIR *dir;
  119. struct dirent *entry;
  120. char *newfile;
  121. dir = opendir(filename);
  122. if (!dir) {
  123. bb_perror_msg("%s", filename);
  124. status = EXIT_FAILURE;
  125. return sum;
  126. }
  127. newfile = last_char_is(filename, '/');
  128. if (newfile)
  129. *newfile = '\0';
  130. while ((entry = readdir(dir))) {
  131. char *name = entry->d_name;
  132. newfile = concat_subpath_file(filename, name);
  133. if(newfile == NULL)
  134. continue;
  135. ++du_depth;
  136. sum += du(newfile);
  137. --du_depth;
  138. free(newfile);
  139. }
  140. closedir(dir);
  141. } else if (du_depth > print_files) {
  142. return sum;
  143. }
  144. if (du_depth <= max_print_depth) {
  145. print(sum, filename);
  146. }
  147. return sum;
  148. }
  149. int du_main(int argc, char **argv)
  150. {
  151. long total;
  152. int slink_depth_save;
  153. int print_final_total;
  154. char *smax_print_depth;
  155. unsigned long opt;
  156. #ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
  157. if (getenv("POSIXLY_CORRECT")) { /* TODO - a new libbb function? */
  158. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  159. disp_hr = 512;
  160. #else
  161. disp_k = 0;
  162. #endif
  163. }
  164. #endif
  165. /* Note: SUSv3 specifies that -a and -s options can not be used together
  166. * in strictly conforming applications. However, it also says that some
  167. * du implementations may produce output when -a and -s are used together.
  168. * gnu du exits with an error code in this case. We choose to simply
  169. * ignore -a. This is consistent with -s being equivalent to -d 0.
  170. */
  171. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  172. bb_opt_complementaly = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s";
  173. opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth);
  174. if((opt & (1 << 9))) {
  175. /* -h opt */
  176. disp_hr = 0;
  177. }
  178. if((opt & (1 << 10))) {
  179. /* -m opt */
  180. disp_hr = MEGABYTE;
  181. }
  182. if((opt & (1 << 2))) {
  183. /* -k opt */
  184. disp_hr = KILOBYTE;
  185. }
  186. #else
  187. bb_opt_complementaly = "H-L:L-H:s-d:d-s";
  188. opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc", &smax_print_depth);
  189. #if !defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
  190. if((opt & (1 << 2))) {
  191. /* -k opt */
  192. disp_k = 1;
  193. }
  194. #endif
  195. #endif
  196. if((opt & (1 << 0))) {
  197. /* -a opt */
  198. print_files = INT_MAX;
  199. }
  200. if((opt & (1 << 1))) {
  201. /* -H opt */
  202. slink_depth = 1;
  203. }
  204. if((opt & (1 << 3))) {
  205. /* -L opt */
  206. slink_depth = INT_MAX;
  207. }
  208. if((opt & (1 << 4))) {
  209. /* -s opt */
  210. max_print_depth = 0;
  211. }
  212. one_file_system = opt & (1 << 5); /* -x opt */
  213. if((opt & (1 << 6))) {
  214. /* -d opt */
  215. max_print_depth = bb_xgetularg10_bnd(smax_print_depth, 0, INT_MAX);
  216. }
  217. if((opt & (1 << 7))) {
  218. /* -l opt */
  219. count_hardlinks = INT_MAX;
  220. }
  221. print_final_total = opt & (1 << 8); /* -c opt */
  222. /* go through remaining args (if any) */
  223. argv += optind;
  224. if (optind >= argc) {
  225. *--argv = ".";
  226. if (slink_depth == 1) {
  227. slink_depth = 0;
  228. }
  229. }
  230. slink_depth_save = slink_depth;
  231. total = 0;
  232. do {
  233. total += du(*argv);
  234. slink_depth = slink_depth_save;
  235. } while (*++argv);
  236. #ifdef CONFIG_FEATURE_CLEAN_UP
  237. reset_ino_dev_hashtable();
  238. #endif
  239. if (print_final_total) {
  240. print(total, "total");
  241. }
  242. bb_fflush_stdout_and_exit(status);
  243. }