blob: a1ca5b59b6cd73c2bedc9667ca471451ff6b05fa [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
John Beppu0f5e1ab1999-12-09 18:23:54 +00002/*
3 * Mini du implementation for busybox
4 *
Eric Andersenbdfd0d72001-10-24 05:00:29 +00005 * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
Eric Andersen265d2292002-04-06 23:16:44 +00007 * Copyright (C) 2002 Edward Betts <edward@debian.org>
John Beppu0f5e1ab1999-12-09 18:23:54 +00008 *
Bernhard Reutner-Fischercb448162006-04-12 07:35:12 +00009 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
John Beppu0f5e1ab1999-12-09 18:23:54 +000010 */
11
Manuel Novoa III cad53642003-03-19 09:13:01 +000012/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
13/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
14
15/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
16 *
17 * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
18 * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
19 * The -d option allows setting of max depth (similar to gnu --max-depth).
20 * 2) Fixed incorrect size calculations for links and directories, especially
21 * when errors occurred. Calculates sizes should now match gnu du output.
22 * 3) Added error checking of output.
23 * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
24 */
25
Eric Andersencbe31da2001-02-20 06:14:08 +000026#include "busybox.h"
Eric Andersencbe31da2001-02-20 06:14:08 +000027
Denis Vlasenkof42ff902006-12-18 21:22:16 +000028#if ENABLE_FEATURE_HUMAN_READABLE
29# if ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
Denis Vlasenkodca0b702006-10-27 09:05:02 +000030static unsigned long disp_hr = 1024;
Manuel Novoa III cad53642003-03-19 09:13:01 +000031# else
32static unsigned long disp_hr = 512;
33# endif
Denis Vlasenkof42ff902006-12-18 21:22:16 +000034#elif ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
35static unsigned disp_k = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +000036#else
Denis Vlasenkof42ff902006-12-18 21:22:16 +000037static unsigned disp_k; /* bss inits to 0 */
Richard June6d0921c2001-01-22 22:35:38 +000038#endif
39
Manuel Novoa III cad53642003-03-19 09:13:01 +000040static int max_print_depth = INT_MAX;
"Vladimir N. Oleynik"57545c82006-01-31 12:06:57 +000041static nlink_t count_hardlinks = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +000042
Denis Vlasenkof42ff902006-12-18 21:22:16 +000043static int status;
Manuel Novoa III cad53642003-03-19 09:13:01 +000044static int print_files;
45static int slink_depth;
46static int du_depth;
47static int one_file_system;
Glenn L McGrath14dad702002-08-23 03:25:22 +000048static dev_t dir_dev;
John Beppue1618e41999-12-15 18:52:17 +000049
Erik Andersene49d5ec2000-02-08 19:58:47 +000050
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +000051static void print(long size, const char * const filename)
John Beppu0f5e1ab1999-12-09 18:23:54 +000052{
Manuel Novoa III cad53642003-03-19 09:13:01 +000053 /* TODO - May not want to defer error checking here. */
Denis Vlasenkof42ff902006-12-18 21:22:16 +000054#if ENABLE_FEATURE_HUMAN_READABLE
Denis Vlasenkof0ed3762006-10-26 23:21:47 +000055 printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr),
Denis Vlasenkof42ff902006-12-18 21:22:16 +000056 filename);
Richard June6d0921c2001-01-22 22:35:38 +000057#else
Glenn L McGrathc66ebe42004-03-10 09:58:51 +000058 if (disp_k) {
59 size++;
60 size >>= 1;
61 }
Denis Vlasenkof0ed3762006-10-26 23:21:47 +000062 printf("%ld\t%s\n", size, filename);
Richard June6d0921c2001-01-22 22:35:38 +000063#endif
John Beppu0f5e1ab1999-12-09 18:23:54 +000064}
65
66/* tiny recursive du */
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +000067static long du(const char * const filename)
John Beppu0f5e1ab1999-12-09 18:23:54 +000068{
Erik Andersene49d5ec2000-02-08 19:58:47 +000069 struct stat statbuf;
Erik Andersenfac10d72000-02-07 05:29:42 +000070 long sum;
John Beppu14c82b61999-12-10 06:15:27 +000071
Denis Vlasenkof42ff902006-12-18 21:22:16 +000072 if (lstat(filename, &statbuf) != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000073 bb_perror_msg("%s", filename);
74 status = EXIT_FAILURE;
Eric Andersene5dfced2001-04-09 22:48:12 +000075 return 0;
Erik Andersene49d5ec2000-02-08 19:58:47 +000076 }
77
Manuel Novoa III cad53642003-03-19 09:13:01 +000078 if (one_file_system) {
79 if (du_depth == 0) {
80 dir_dev = statbuf.st_dev;
81 } else if (dir_dev != statbuf.st_dev) {
82 return 0;
Eric Andersen8fa1bf72001-06-30 17:54:20 +000083 }
Erik Andersen9ffdaa62000-02-11 21:55:04 +000084 }
Manuel Novoa III cad53642003-03-19 09:13:01 +000085
86 sum = statbuf.st_blocks;
87
88 if (S_ISLNK(statbuf.st_mode)) {
89 if (slink_depth > du_depth) { /* -H or -L */
Denis Vlasenkof42ff902006-12-18 21:22:16 +000090 if (stat(filename, &statbuf) != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000091 bb_perror_msg("%s", filename);
92 status = EXIT_FAILURE;
93 return 0;
94 }
95 sum = statbuf.st_blocks;
96 if (slink_depth == 1) {
97 slink_depth = INT_MAX; /* Convert -H to -L. */
98 }
99 }
100 }
101
102 if (statbuf.st_nlink > count_hardlinks) {
103 /* Add files/directories with links only once */
104 if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
105 return 0;
106 }
107 add_to_ino_dev_hashtable(&statbuf, NULL);
108 }
109
Erik Andersene49d5ec2000-02-08 19:58:47 +0000110 if (S_ISDIR(statbuf.st_mode)) {
111 DIR *dir;
112 struct dirent *entry;
Eric Andersen04b03542001-05-07 22:49:43 +0000113 char *newfile;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000114
Rob Landley86b4d642006-08-03 17:58:17 +0000115 dir = warn_opendir(filename);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000116 if (!dir) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000117 status = EXIT_FAILURE;
118 return sum;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000119 }
Erik Andersen42387e42000-02-21 17:27:17 +0000120
Eric Andersen04b03542001-05-07 22:49:43 +0000121 newfile = last_char_is(filename, '/');
122 if (newfile)
123 *newfile = '\0';
Erik Andersen42387e42000-02-21 17:27:17 +0000124
Erik Andersene49d5ec2000-02-08 19:58:47 +0000125 while ((entry = readdir(dir))) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000126 char *name = entry->d_name;
127
Glenn L McGrath393183d2003-05-26 14:07:50 +0000128 newfile = concat_subpath_file(filename, name);
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000129 if (newfile == NULL)
Erik Andersene49d5ec2000-02-08 19:58:47 +0000130 continue;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000131 ++du_depth;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000132 sum += du(newfile);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000133 --du_depth;
Eric Andersene5dfced2001-04-09 22:48:12 +0000134 free(newfile);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000135 }
136 closedir(dir);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000137 } else if (du_depth > print_files) {
138 return sum;
Erik Andersen27fdd082000-02-19 18:16:49 +0000139 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000140 if (du_depth <= max_print_depth) {
141 print(sum, filename);
142 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000143 return sum;
John Beppu0f5e1ab1999-12-09 18:23:54 +0000144}
145
Erik Andersene49d5ec2000-02-08 19:58:47 +0000146int du_main(int argc, char **argv)
147{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000148 long total;
149 int slink_depth_save;
Eric Andersen8876fb22003-06-20 09:01:58 +0000150 int print_final_total;
151 char *smax_print_depth;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000152 unsigned opt;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000153
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000154#if ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
Manuel Novoa III cad53642003-03-19 09:13:01 +0000155 if (getenv("POSIXLY_CORRECT")) { /* TODO - a new libbb function? */
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000156#if ENABLE_FEATURE_HUMAN_READABLE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000157 disp_hr = 512;
158#else
159 disp_k = 0;
160#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000161 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000162#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +0000163
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +0000164 /* Note: SUSv3 specifies that -a and -s options cannot be used together
Manuel Novoa III cad53642003-03-19 09:13:01 +0000165 * in strictly conforming applications. However, it also says that some
166 * du implementations may produce output when -a and -s are used together.
167 * gnu du exits with an error code in this case. We choose to simply
168 * ignore -a. This is consistent with -s being equivalent to -d 0.
169 */
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000170#if ENABLE_FEATURE_HUMAN_READABLE
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000171 opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s";
172 opt = getopt32(argc, argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth);
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000173 if (opt & (1 << 9)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000174 /* -h opt */
175 disp_hr = 0;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000176 }
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000177 if (opt & (1 << 10)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000178 /* -m opt */
Denis Vlasenkodca0b702006-10-27 09:05:02 +0000179 disp_hr = 1024*1024;
Eric Andersen8876fb22003-06-20 09:01:58 +0000180 }
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000181 if (opt & (1 << 2)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000182 /* -k opt */
Denis Vlasenkodca0b702006-10-27 09:05:02 +0000183 disp_hr = 1024;
Eric Andersen8876fb22003-06-20 09:01:58 +0000184 }
185#else
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000186 opt_complementary = "H-L:L-H:s-d:d-s";
187 opt = getopt32(argc, argv, "aHkLsx" "d:" "lc", &smax_print_depth);
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000188#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
189 if (opt & (1 << 2)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000190 /* -k opt */
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000191 disp_k = 1;
Eric Andersen8876fb22003-06-20 09:01:58 +0000192 }
193#endif
194#endif
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000195 if (opt & (1 << 0)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000196 /* -a opt */
197 print_files = INT_MAX;
198 }
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000199 if (opt & (1 << 1)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000200 /* -H opt */
201 slink_depth = 1;
202 }
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000203 if (opt & (1 << 3)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000204 /* -L opt */
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000205 slink_depth = INT_MAX;
Eric Andersen8876fb22003-06-20 09:01:58 +0000206 }
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000207 if (opt & (1 << 4)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000208 /* -s opt */
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000209 max_print_depth = 0;
210 }
Eric Andersen8876fb22003-06-20 09:01:58 +0000211 one_file_system = opt & (1 << 5); /* -x opt */
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000212 if (opt & (1 << 6)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000213 /* -d opt */
Denis Vlasenko13858992006-10-08 12:49:22 +0000214 max_print_depth = xatoi_u(smax_print_depth);
Eric Andersen8876fb22003-06-20 09:01:58 +0000215 }
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000216 if (opt & (1 << 7)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000217 /* -l opt */
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000218 count_hardlinks = MAXINT(nlink_t);
Eric Andersen8876fb22003-06-20 09:01:58 +0000219 }
220 print_final_total = opt & (1 << 8); /* -c opt */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000221
222 /* go through remaining args (if any) */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000223 argv += optind;
Eric Andersen17ad45a2000-07-14 18:38:26 +0000224 if (optind >= argc) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000225 *--argv = ".";
226 if (slink_depth == 1) {
227 slink_depth = 0;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000228 }
229 }
230
Manuel Novoa III cad53642003-03-19 09:13:01 +0000231 slink_depth_save = slink_depth;
232 total = 0;
233 do {
234 total += du(*argv);
235 slink_depth = slink_depth_save;
236 } while (*++argv);
Denis Vlasenkof42ff902006-12-18 21:22:16 +0000237#if ENABLE_FEATURE_CLEAN_UP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000238 reset_ino_dev_hashtable();
Eric Andersen8876fb22003-06-20 09:01:58 +0000239#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +0000240
Manuel Novoa III cad53642003-03-19 09:13:01 +0000241 if (print_final_total) {
242 print(total, "total");
243 }
244
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000245 fflush_stdout_and_exit(status);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000246}