1 /* vi: set sw=4 ts=4: */
3 * Mini du implementation for busybox
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>
9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
11 /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
13 * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
14 * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
15 * The -d option allows setting of max depth (similar to gnu --max-depth).
16 * 2) Fixed incorrect size calculations for links and directories, especially
17 * when errors occurred. Calculates sizes should now match gnu du output.
18 * 3) Added error checking of output.
19 * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
22 //config: bool "du (6.5 kb)"
25 //config: du is used to report the amount of disk space used
26 //config: for specified files.
28 //config:config FEATURE_DU_DEFAULT_BLOCKSIZE_1K
29 //config: bool "Use default blocksize of 1024 bytes (else it's 512 bytes)"
31 //config: depends on DU
33 //applet:IF_DU(APPLET(du, BB_DIR_USR_BIN, BB_SUID_DROP))
35 //kbuild:lib-$(CONFIG_DU) += du.o
37 /* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
38 /* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
40 //usage:#define du_trivial_usage
41 //usage: "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..."
42 //usage:#define du_full_usage "\n\n"
43 //usage: "Summarize disk space used for FILEs (or directories)\n"
44 //usage: "\n -a Show file sizes too"
45 //usage: "\n -b Apparent size (including holes)"
46 //usage: "\n -L Follow all symlinks"
47 //usage: "\n -H Follow symlinks on command line"
48 //usage: "\n -d N Limit output to directories (and files with -a) of depth < N"
49 //usage: "\n -c Show grand total"
50 //usage: "\n -l Count sizes many times if hard linked"
51 //usage: "\n -s Display only a total for each argument"
52 //usage: "\n -x Skip directories on different filesystems"
53 //usage: IF_FEATURE_HUMAN_READABLE(
54 //usage: "\n -h Sizes in human readable format (e.g., 1K 243M 2G)"
55 //usage: "\n -m Sizes in megabytes"
57 //usage: "\n -k Sizes in kilobytes" IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)")
58 //usage: IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(
59 //usage: "\n Default unit is 512 bytes"
62 //usage:#define du_example_usage
65 //usage: "12 ./kernel-patches/CVS\n"
66 //usage: "80 ./kernel-patches\n"
67 //usage: "12 ./tests/CVS\n"
68 //usage: "36 ./tests\n"
69 //usage: "12 ./scripts/CVS\n"
70 //usage: "16 ./scripts\n"
71 //usage: "12 ./docs/CVS\n"
72 //usage: "104 ./docs\n"
76 #include "common_bufsiz.h"
79 OPT_a_files_too
= (1 << 0),
80 OPT_H_follow_links
= (1 << 1),
81 OPT_k_kbytes
= (1 << 2),
82 OPT_L_follow_links
= (1 << 3),
83 OPT_s_total_norecurse
= (1 << 4),
84 OPT_x_one_FS
= (1 << 5),
85 OPT_d_maxdepth
= (1 << 6),
86 OPT_l_hardlinks
= (1 << 7),
87 OPT_c_total
= (1 << 8),
89 OPT_h_for_humans
= (1 << 10),
90 OPT_m_mbytes
= (1 << 11),
94 #if ENABLE_FEATURE_HUMAN_READABLE
95 unsigned long disp_unit
;
105 #define G (*(struct globals*)bb_common_bufsiz1)
106 #define INIT_G() do { setup_common_bufsiz(); } while (0)
109 static void print(unsigned long long size
, const char *filename
)
111 /* TODO - May not want to defer error checking here. */
112 #if ENABLE_FEATURE_HUMAN_READABLE
114 /* ~30 bytes of code for extra compat:
115 * coreutils' du rounds sizes up:
116 * for example, 1025k file is shown as "2" by du -m.
117 * We round to nearest if human-readable [too hard to fix],
118 * else (fixed scale such as -m), we round up. To that end,
119 * add yet another half of the unit before displaying:
122 size
+= (G
.disp_unit
-1) / (unsigned)(512 * 2);
125 /* size x 512 / G.disp_unit.
126 * If G.disp_unit == 0, show one fractional
129 make_human_readable_str(size
, (option_mask32
& OPT_b
) ? 1 : 512, G
.disp_unit
),
133 if (!(option_mask32
& OPT_b
)) {
140 printf("%llu\t%s\n", size
, filename
);
144 /* tiny recursive du */
145 static unsigned long long du(const char *filename
)
148 unsigned long long sum
;
150 if (lstat(filename
, &statbuf
) != 0) {
151 bb_simple_perror_msg(filename
);
152 G
.status
= EXIT_FAILURE
;
156 if (option_mask32
& OPT_x_one_FS
) {
157 if (G
.du_depth
== 0) {
158 G
.dir_dev
= statbuf
.st_dev
;
159 } else if (G
.dir_dev
!= statbuf
.st_dev
) {
164 sum
= ((option_mask32
& OPT_b
) ? statbuf
.st_size
: statbuf
.st_blocks
);
166 if (S_ISLNK(statbuf
.st_mode
)) {
167 if (G
.slink_depth
> G
.du_depth
) { /* -H or -L */
168 if (stat(filename
, &statbuf
) != 0) {
169 bb_simple_perror_msg(filename
);
170 G
.status
= EXIT_FAILURE
;
173 sum
= ((option_mask32
& OPT_b
) ? statbuf
.st_size
: statbuf
.st_blocks
);
174 if (G
.slink_depth
== 1) {
175 /* Convert -H to -L */
176 G
.slink_depth
= INT_MAX
;
181 if (!(option_mask32
& OPT_l_hardlinks
)
182 && statbuf
.st_nlink
> 1
184 /* Add files/directories with links only once */
185 if (is_in_ino_dev_hashtable(&statbuf
)) {
188 add_to_ino_dev_hashtable(&statbuf
, NULL
);
191 if (S_ISDIR(statbuf
.st_mode
)) {
193 struct dirent
*entry
;
196 dir
= warn_opendir(filename
);
198 G
.status
= EXIT_FAILURE
;
202 while ((entry
= readdir(dir
))) {
203 newfile
= concat_subpath_file(filename
, entry
->d_name
);
213 if (!(option_mask32
& OPT_a_files_too
) && G
.du_depth
!= 0)
216 if (G
.du_depth
<= G
.max_print_depth
) {
217 print(sum
, filename
);
222 int du_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
223 int du_main(int argc UNUSED_PARAM
, char **argv
)
225 unsigned long long total
;
226 int slink_depth_save
;
231 #if ENABLE_FEATURE_HUMAN_READABLE
232 IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G
.disp_unit
= 1024;)
233 IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G
.disp_unit
= 512;)
234 if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
237 IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G
.disp_k
= 1;)
238 /* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */
240 G
.max_print_depth
= INT_MAX
;
242 /* Note: SUSv3 specifies that -a and -s options cannot be used together
243 * in strictly conforming applications. However, it also says that some
244 * du implementations may produce output when -a and -s are used together.
245 * gnu du exits with an error code in this case. We choose to simply
246 * ignore -a. This is consistent with -s being equivalent to -d 0.
248 #if ENABLE_FEATURE_HUMAN_READABLE
249 opt
= getopt32(argv
, "^"
251 "\0" "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s",
258 if (opt
& OPT_h_for_humans
) {
261 if (opt
& OPT_m_mbytes
) {
262 G
.disp_unit
= 1024*1024;
264 if (opt
& OPT_k_kbytes
) {
268 opt
= getopt32(argv
, "^"
270 "\0" "H-L:L-H:s-d:d-s",
274 # if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
275 if (opt
& OPT_k_kbytes
) {
280 if (opt
& OPT_H_follow_links
) {
283 if (opt
& OPT_L_follow_links
) {
284 G
.slink_depth
= INT_MAX
;
286 if (opt
& OPT_s_total_norecurse
) {
287 G
.max_print_depth
= 0;
290 /* go through remaining args (if any) */
292 *--argv
= (char*)".";
293 if (G
.slink_depth
== 1) {
298 slink_depth_save
= G
.slink_depth
;
302 G
.slink_depth
= slink_depth_save
;
305 if (ENABLE_FEATURE_CLEAN_UP
)
306 reset_ino_dev_hashtable();
307 if (opt
& OPT_c_total
)
308 print(total
, "total");
310 fflush_stdout_and_exit(G
.status
);