aboutsummaryrefslogtreecommitdiff
path: root/coreutils/du.c
diff options
context:
space:
mode:
Diffstat (limited to 'coreutils/du.c')
-rw-r--r--coreutils/du.c227
1 files changed, 147 insertions, 80 deletions
diff --git a/coreutils/du.c b/coreutils/du.c
index 2e49b2147..702a9aa14 100644
--- a/coreutils/du.c
+++ b/coreutils/du.c
@@ -22,45 +22,65 @@
22 * 22 *
23 */ 23 */
24 24
25#include <sys/types.h> 25/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
26#include <fcntl.h> 26/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
27#include <dirent.h> 27
28#include <stdio.h> 28/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
29 *
30 * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
31 * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
32 * The -d option allows setting of max depth (similar to gnu --max-depth).
33 * 2) Fixed incorrect size calculations for links and directories, especially
34 * when errors occurred. Calculates sizes should now match gnu du output.
35 * 3) Added error checking of output.
36 * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
37 */
38
29#include <stdlib.h> 39#include <stdlib.h>
30#include <getopt.h> 40#include <limits.h>
31#include <string.h> 41#include <unistd.h>
32#include <errno.h> 42#include <dirent.h>
43#include <sys/stat.h>
33#include "busybox.h" 44#include "busybox.h"
34 45
35
36#ifdef CONFIG_FEATURE_HUMAN_READABLE 46#ifdef CONFIG_FEATURE_HUMAN_READABLE
47# ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
37static unsigned long disp_hr = KILOBYTE; 48static unsigned long disp_hr = KILOBYTE;
49# else
50static unsigned long disp_hr = 512;
51# endif
52#elif defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
53static unsigned int disp_k = 1;
54#else
55static unsigned int disp_k; /* bss inits to 0 */
38#endif 56#endif
39 57
40static int du_depth /*= 0*/; 58static int max_print_depth = INT_MAX;
41static int count_hardlinks /*= 0*/; 59static int count_hardlinks = INT_MAX;
42static int one_file_system /*= 0*/; 60
61static int status
62#if EXIT_SUCCESS == 0
63 = EXIT_SUCCESS
64#endif
65 ;
66static int print_files;
67static int slink_depth;
68static int du_depth;
69static int one_file_system;
43static dev_t dir_dev; 70static dev_t dir_dev;
44 71
45static void (*print) (long, char *);
46 72
47static void print_normal(long size, char *filename) 73static void print(long size, char *filename)
48{ 74{
75 /* TODO - May not want to defer error checking here. */
49#ifdef CONFIG_FEATURE_HUMAN_READABLE 76#ifdef CONFIG_FEATURE_HUMAN_READABLE
50 printf("%s\t%s\n", make_human_readable_str(size << 10, 1, disp_hr), 77 bb_printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr),
51 filename); 78 filename);
52#else 79#else
53 printf("%ld\t%s\n", size, filename); 80 bb_printf("%ld\t%s\n", size >> disp_k, filename);
54#endif 81#endif
55} 82}
56 83
57static void print_summary(long size, char *filename)
58{
59 if (du_depth == 1) {
60 print_normal(size, filename);
61 }
62}
63
64/* tiny recursive du */ 84/* tiny recursive du */
65static long du(char *filename) 85static long du(char *filename)
66{ 86{
@@ -68,23 +88,43 @@ static long du(char *filename)
68 long sum; 88 long sum;
69 89
70 if ((lstat(filename, &statbuf)) != 0) { 90 if ((lstat(filename, &statbuf)) != 0) {
71 perror_msg("%s", filename); 91 bb_perror_msg("%s", filename);
92 status = EXIT_FAILURE;
72 return 0; 93 return 0;
73 } 94 }
74 if (du_depth == 0)
75 dir_dev = statbuf.st_dev;
76 else if (one_file_system && dir_dev != statbuf.st_dev)
77 return 0;
78 95
79 du_depth++; 96 if (one_file_system) {
80 sum = (statbuf.st_blocks >> 1); 97 if (du_depth == 0) {
98 dir_dev = statbuf.st_dev;
99 } else if (dir_dev != statbuf.st_dev) {
100 return 0;
101 }
102 }
103
104 sum = statbuf.st_blocks;
81 105
82 /* Don't add in stuff pointed to by symbolic links */
83 if (S_ISLNK(statbuf.st_mode)) { 106 if (S_ISLNK(statbuf.st_mode)) {
84 sum = 0L; 107 if (slink_depth > du_depth) { /* -H or -L */
85 if (du_depth == 1) { 108 if ((stat(filename, &statbuf)) != 0) {
109 bb_perror_msg("%s", filename);
110 status = EXIT_FAILURE;
111 return 0;
112 }
113 sum = statbuf.st_blocks;
114 if (slink_depth == 1) {
115 slink_depth = INT_MAX; /* Convert -H to -L. */
116 }
86 } 117 }
87 } 118 }
119
120 if (statbuf.st_nlink > count_hardlinks) {
121 /* Add files/directories with links only once */
122 if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
123 return 0;
124 }
125 add_to_ino_dev_hashtable(&statbuf, NULL);
126 }
127
88 if (S_ISDIR(statbuf.st_mode)) { 128 if (S_ISDIR(statbuf.st_mode)) {
89 DIR *dir; 129 DIR *dir;
90 struct dirent *entry; 130 struct dirent *entry;
@@ -92,8 +132,9 @@ static long du(char *filename)
92 132
93 dir = opendir(filename); 133 dir = opendir(filename);
94 if (!dir) { 134 if (!dir) {
95 du_depth--; 135 bb_perror_msg("%s", filename);
96 return 0; 136 status = EXIT_FAILURE;
137 return sum;
97 } 138 }
98 139
99 newfile = last_char_is(filename, '/'); 140 newfile = last_char_is(filename, '/');
@@ -103,54 +144,86 @@ static long du(char *filename)
103 while ((entry = readdir(dir))) { 144 while ((entry = readdir(dir))) {
104 char *name = entry->d_name; 145 char *name = entry->d_name;
105 146
106 if ((strcmp(name, "..") == 0) 147 if ((name[0] == '.') && (!name[1] || (name[1] == '.' && !name[2]))) {
107 || (strcmp(name, ".") == 0)) {
108 continue; 148 continue;
109 } 149 }
110 newfile = concat_path_file(filename, name); 150 newfile = concat_path_file(filename, name);
151 ++du_depth;
111 sum += du(newfile); 152 sum += du(newfile);
153 --du_depth;
112 free(newfile); 154 free(newfile);
113 } 155 }
114 closedir(dir); 156 closedir(dir);
157 } else if (du_depth > print_files) {
158 return sum;
159 }
160 if (du_depth <= max_print_depth) {
115 print(sum, filename); 161 print(sum, filename);
116 } else if (statbuf.st_nlink > 1 && !count_hardlinks) {
117 /* Add files with hard links only once */
118 if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
119 sum = 0L;
120 if (du_depth == 1)
121 print(sum, filename);
122 } else {
123 add_to_ino_dev_hashtable(&statbuf, NULL);
124 }
125 } 162 }
126 du_depth--;
127 return sum; 163 return sum;
128} 164}
129 165
130int du_main(int argc, char **argv) 166int du_main(int argc, char **argv)
131{ 167{
132 int status = EXIT_SUCCESS; 168 long total;
133 int i; 169 int slink_depth_save;
170 int print_final_total = 0;
134 int c; 171 int c;
135 172
136 /* default behaviour */ 173#ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
137 print = print_normal; 174 if (getenv("POSIXLY_CORRECT")) { /* TODO - a new libbb function? */
175#ifdef CONFIG_FEATURE_HUMAN_READABLE
176 disp_hr = 512;
177#else
178 disp_k = 0;
179#endif
180 }
181#endif
182
183 /* Note: SUSv3 specifies that -a and -s options can not be used together
184 * in strictly conforming applications. However, it also says that some
185 * du implementations may produce output when -a and -s are used together.
186 * gnu du exits with an error code in this case. We choose to simply
187 * ignore -a. This is consistent with -s being equivalent to -d 0.
188 */
138 189
139 /* parse argv[] */ 190 while ((c = getopt(argc, argv, "aHkLsx" "d:" "lc"
140 while ((c = getopt(argc, argv, "slx"
141#ifdef CONFIG_FEATURE_HUMAN_READABLE 191#ifdef CONFIG_FEATURE_HUMAN_READABLE
142 "hm" 192 "hm"
143#endif 193#endif
144 "k")) != EOF) { 194 )) > 0) {
145 switch (c) { 195 switch (c) {
196 case 'a':
197 print_files = INT_MAX;
198 break;
199 case 'H':
200 slink_depth = 1;
201 break;
202 case 'k':
203#ifdef CONFIG_FEATURE_HUMAN_READABLE
204 disp_hr = KILOBYTE;
205#elif !defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
206 disp_k = 1;
207#endif
208 break;
209 case 'L':
210 slink_depth = INT_MAX;
211 break;
146 case 's': 212 case 's':
147 print = print_summary; 213 max_print_depth = 0;
214 break;
215 case 'x':
216 one_file_system = 1;
217 break;
218
219 case 'd':
220 max_print_depth = bb_xgetularg10_bnd(optarg, 0, INT_MAX);
148 break; 221 break;
149 case 'l': 222 case 'l':
150 count_hardlinks = 1; 223 count_hardlinks = 1;
151 break; 224 break;
152 case 'x': 225 case 'c':
153 one_file_system = 1; 226 print_final_total = 1;
154 break; 227 break;
155#ifdef CONFIG_FEATURE_HUMAN_READABLE 228#ifdef CONFIG_FEATURE_HUMAN_READABLE
156 case 'h': 229 case 'h':
@@ -160,37 +233,31 @@ int du_main(int argc, char **argv)
160 disp_hr = MEGABYTE; 233 disp_hr = MEGABYTE;
161 break; 234 break;
162#endif 235#endif
163 case 'k':
164 break;
165 default: 236 default:
166 show_usage(); 237 bb_show_usage();
167 } 238 }
168 } 239 }
169 240
170 /* go through remaining args (if any) */ 241 /* go through remaining args (if any) */
242 argv += optind;
171 if (optind >= argc) { 243 if (optind >= argc) {
172 if (du(".") == 0) 244 *--argv = ".";
173 status = EXIT_FAILURE; 245 if (slink_depth == 1) {
174 } else { 246 slink_depth = 0;
175 long sum;
176
177 for (i = optind; i < argc; i++) {
178 sum = du(argv[i]);
179 if (is_directory(argv[i], FALSE, NULL) == FALSE) {
180 print_normal(sum, argv[i]);
181 }
182 reset_ino_dev_hashtable();
183 } 247 }
184 } 248 }
185 249
186 return status; 250 slink_depth_save = slink_depth;
187} 251 total = 0;
252 do {
253 total += du(*argv);
254 slink_depth = slink_depth_save;
255 } while (*++argv);
256 reset_ino_dev_hashtable();
188 257
189/* $Id: du.c,v 1.55 2002/08/23 07:28:45 aaronl Exp $ */ 258 if (print_final_total) {
190/* 259 print(total, "total");
191Local Variables: 260 }
192c-file-style: "linux" 261
193c-basic-offset: 4 262 bb_fflush_stdout_and_exit(status);
194tab-width: 4 263}
195End:
196*/