source: MondoRescue/branches/3.3/mindi-busybox/coreutils/du.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 7.6 KB
Line 
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 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 */
11
12/* 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
26//usage:#define du_trivial_usage
27//usage: "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..."
28//usage:#define du_full_usage "\n\n"
29//usage: "Summarize disk space used for each FILE and/or directory\n"
30//usage: "\n -a Show file sizes too"
31//usage: "\n -L Follow all symlinks"
32//usage: "\n -H Follow symlinks on command line"
33//usage: "\n -d N Limit output to directories (and files with -a) of depth < N"
34//usage: "\n -c Show grand total"
35//usage: "\n -l Count sizes many times if hard linked"
36//usage: "\n -s Display only a total for each argument"
37//usage: "\n -x Skip directories on different filesystems"
38//usage: IF_FEATURE_HUMAN_READABLE(
39//usage: "\n -h Sizes in human readable format (e.g., 1K 243M 2G)"
40//usage: "\n -m Sizes in megabytes"
41//usage: )
42//usage: "\n -k Sizes in kilobytes" IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)")
43//usage: IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(
44//usage: "\n Default unit is 512 bytes"
45//usage: )
46//usage:
47//usage:#define du_example_usage
48//usage: "$ du\n"
49//usage: "16 ./CVS\n"
50//usage: "12 ./kernel-patches/CVS\n"
51//usage: "80 ./kernel-patches\n"
52//usage: "12 ./tests/CVS\n"
53//usage: "36 ./tests\n"
54//usage: "12 ./scripts/CVS\n"
55//usage: "16 ./scripts\n"
56//usage: "12 ./docs/CVS\n"
57//usage: "104 ./docs\n"
58//usage: "2417 .\n"
59
60#include "libbb.h"
61#include "common_bufsiz.h"
62
63enum {
64 OPT_a_files_too = (1 << 0),
65 OPT_H_follow_links = (1 << 1),
66 OPT_k_kbytes = (1 << 2),
67 OPT_L_follow_links = (1 << 3),
68 OPT_s_total_norecurse = (1 << 4),
69 OPT_x_one_FS = (1 << 5),
70 OPT_d_maxdepth = (1 << 6),
71 OPT_l_hardlinks = (1 << 7),
72 OPT_c_total = (1 << 8),
73 OPT_h_for_humans = (1 << 9),
74 OPT_m_mbytes = (1 << 10),
75};
76
77struct globals {
78#if ENABLE_FEATURE_HUMAN_READABLE
79 unsigned long disp_unit;
80#else
81 unsigned disp_k;
82#endif
83 int max_print_depth;
84 bool status;
85 int slink_depth;
86 int du_depth;
87 dev_t dir_dev;
88} FIX_ALIASING;
89#define G (*(struct globals*)bb_common_bufsiz1)
90#define INIT_G() do { setup_common_bufsiz(); } while (0)
91
92
93static void print(unsigned long long size, const char *filename)
94{
95 /* TODO - May not want to defer error checking here. */
96#if ENABLE_FEATURE_HUMAN_READABLE
97# if ENABLE_DESKTOP
98 /* ~30 bytes of code for extra comtat:
99 * coreutils' du rounds sizes up:
100 * for example, 1025k file is shown as "2" by du -m.
101 * We round to nearest if human-readable [too hard to fix],
102 * else (fixed scale such as -m), we round up. To that end,
103 * add yet another half of the unit before displaying:
104 */
105 if (G.disp_unit)
106 size += (G.disp_unit-1) / (unsigned)(512 * 2);
107# endif
108 printf("%s\t%s\n",
109 /* size x 512 / G.disp_unit.
110 * If G.disp_unit == 0, show one fractional
111 * and use suffixes
112 */
113 make_human_readable_str(size, 512, G.disp_unit),
114 filename);
115#else
116 if (G.disp_k) {
117 size++;
118 size >>= 1;
119 }
120 printf("%llu\t%s\n", size, filename);
121#endif
122}
123
124/* tiny recursive du */
125static unsigned long long du(const char *filename)
126{
127 struct stat statbuf;
128 unsigned long long sum;
129
130 if (lstat(filename, &statbuf) != 0) {
131 bb_simple_perror_msg(filename);
132 G.status = EXIT_FAILURE;
133 return 0;
134 }
135
136 if (option_mask32 & OPT_x_one_FS) {
137 if (G.du_depth == 0) {
138 G.dir_dev = statbuf.st_dev;
139 } else if (G.dir_dev != statbuf.st_dev) {
140 return 0;
141 }
142 }
143
144 sum = statbuf.st_blocks;
145
146 if (S_ISLNK(statbuf.st_mode)) {
147 if (G.slink_depth > G.du_depth) { /* -H or -L */
148 if (stat(filename, &statbuf) != 0) {
149 bb_simple_perror_msg(filename);
150 G.status = EXIT_FAILURE;
151 return 0;
152 }
153 sum = statbuf.st_blocks;
154 if (G.slink_depth == 1) {
155 /* Convert -H to -L */
156 G.slink_depth = INT_MAX;
157 }
158 }
159 }
160
161 if (!(option_mask32 & OPT_l_hardlinks)
162 && statbuf.st_nlink > 1
163 ) {
164 /* Add files/directories with links only once */
165 if (is_in_ino_dev_hashtable(&statbuf)) {
166 return 0;
167 }
168 add_to_ino_dev_hashtable(&statbuf, NULL);
169 }
170
171 if (S_ISDIR(statbuf.st_mode)) {
172 DIR *dir;
173 struct dirent *entry;
174 char *newfile;
175
176 dir = warn_opendir(filename);
177 if (!dir) {
178 G.status = EXIT_FAILURE;
179 return sum;
180 }
181
182 while ((entry = readdir(dir))) {
183 newfile = concat_subpath_file(filename, entry->d_name);
184 if (newfile == NULL)
185 continue;
186 ++G.du_depth;
187 sum += du(newfile);
188 --G.du_depth;
189 free(newfile);
190 }
191 closedir(dir);
192 } else {
193 if (!(option_mask32 & OPT_a_files_too) && G.du_depth != 0)
194 return sum;
195 }
196 if (G.du_depth <= G.max_print_depth) {
197 print(sum, filename);
198 }
199 return sum;
200}
201
202int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
203int du_main(int argc UNUSED_PARAM, char **argv)
204{
205 unsigned long long total;
206 int slink_depth_save;
207 unsigned opt;
208
209 INIT_G();
210
211#if ENABLE_FEATURE_HUMAN_READABLE
212 IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_unit = 1024;)
213 IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_unit = 512;)
214 if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
215 G.disp_unit = 512;
216#else
217 IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;)
218 /* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */
219#endif
220 G.max_print_depth = INT_MAX;
221
222 /* Note: SUSv3 specifies that -a and -s options cannot be used together
223 * in strictly conforming applications. However, it also says that some
224 * du implementations may produce output when -a and -s are used together.
225 * gnu du exits with an error code in this case. We choose to simply
226 * ignore -a. This is consistent with -s being equivalent to -d 0.
227 */
228#if ENABLE_FEATURE_HUMAN_READABLE
229 opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+";
230 opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth);
231 argv += optind;
232 if (opt & OPT_h_for_humans) {
233 G.disp_unit = 0;
234 }
235 if (opt & OPT_m_mbytes) {
236 G.disp_unit = 1024*1024;
237 }
238 if (opt & OPT_k_kbytes) {
239 G.disp_unit = 1024;
240 }
241#else
242 opt_complementary = "H-L:L-H:s-d:d-s:d+";
243 opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth);
244 argv += optind;
245#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
246 if (opt & OPT_k_kbytes) {
247 G.disp_k = 1;
248 }
249#endif
250#endif
251 if (opt & OPT_H_follow_links) {
252 G.slink_depth = 1;
253 }
254 if (opt & OPT_L_follow_links) {
255 G.slink_depth = INT_MAX;
256 }
257 if (opt & OPT_s_total_norecurse) {
258 G.max_print_depth = 0;
259 }
260
261 /* go through remaining args (if any) */
262 if (!*argv) {
263 *--argv = (char*)".";
264 if (G.slink_depth == 1) {
265 G.slink_depth = 0;
266 }
267 }
268
269 slink_depth_save = G.slink_depth;
270 total = 0;
271 do {
272 total += du(*argv);
273 /* otherwise du /dir /dir won't show /dir twice: */
274 reset_ino_dev_hashtable();
275 G.slink_depth = slink_depth_save;
276 } while (*++argv);
277
278 if (opt & OPT_c_total)
279 print(total, "total");
280
281 fflush_stdout_and_exit(G.status);
282}
Note: See TracBrowser for help on using the repository browser.