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.
12 /* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
13 /* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
15 /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
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.
29 OPT_a_files_too
= (1 << 0),
30 OPT_H_follow_links
= (1 << 1),
31 OPT_k_kbytes
= (1 << 2),
32 OPT_L_follow_links
= (1 << 3),
33 OPT_s_total_norecurse
= (1 << 4),
34 OPT_x_one_FS
= (1 << 5),
35 OPT_d_maxdepth
= (1 << 6),
36 OPT_l_hardlinks
= (1 << 7),
37 OPT_c_total
= (1 << 8),
38 OPT_h_for_humans
= (1 << 9),
39 OPT_m_mbytes
= (1 << 10),
43 #if ENABLE_FEATURE_HUMAN_READABLE
44 unsigned long disp_hr
;
54 #define G (*(struct globals*)&bb_common_bufsiz1)
57 static void print(unsigned long size
, const char *filename
)
59 /* TODO - May not want to defer error checking here. */
60 #if ENABLE_FEATURE_HUMAN_READABLE
62 /* size x 512 / G.disp_hr, show one fractional,
63 * use suffixes if G.disp_hr == 0 */
64 make_human_readable_str(size
, 512, G
.disp_hr
),
71 printf("%lu\t%s\n", size
, filename
);
75 /* tiny recursive du */
76 static unsigned long du(const char *filename
)
81 if (lstat(filename
, &statbuf
) != 0) {
82 bb_simple_perror_msg(filename
);
83 G
.status
= EXIT_FAILURE
;
87 if (option_mask32
& OPT_x_one_FS
) {
88 if (G
.du_depth
== 0) {
89 G
.dir_dev
= statbuf
.st_dev
;
90 } else if (G
.dir_dev
!= statbuf
.st_dev
) {
95 sum
= statbuf
.st_blocks
;
97 if (S_ISLNK(statbuf
.st_mode
)) {
98 if (G
.slink_depth
> G
.du_depth
) { /* -H or -L */
99 if (stat(filename
, &statbuf
) != 0) {
100 bb_simple_perror_msg(filename
);
101 G
.status
= EXIT_FAILURE
;
104 sum
= statbuf
.st_blocks
;
105 if (G
.slink_depth
== 1) {
106 /* Convert -H to -L */
107 G
.slink_depth
= INT_MAX
;
112 if (!(option_mask32
& OPT_l_hardlinks
)
113 && statbuf
.st_nlink
> 1
115 /* Add files/directories with links only once */
116 if (is_in_ino_dev_hashtable(&statbuf
)) {
119 add_to_ino_dev_hashtable(&statbuf
, NULL
);
122 if (S_ISDIR(statbuf
.st_mode
)) {
124 struct dirent
*entry
;
127 dir
= warn_opendir(filename
);
129 G
.status
= EXIT_FAILURE
;
133 while ((entry
= readdir(dir
))) {
134 newfile
= concat_subpath_file(filename
, entry
->d_name
);
144 if (!(option_mask32
& OPT_a_files_too
) && G
.du_depth
!= 0)
147 if (G
.du_depth
<= G
.max_print_depth
) {
148 print(sum
, filename
);
153 int du_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
154 int du_main(int argc UNUSED_PARAM
, char **argv
)
157 int slink_depth_save
;
160 #if ENABLE_FEATURE_HUMAN_READABLE
161 IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G
.disp_hr
= 1024;)
162 IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G
.disp_hr
= 512;)
163 if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
166 IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G
.disp_k
= 1;)
167 /* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */
169 G
.max_print_depth
= INT_MAX
;
171 /* Note: SUSv3 specifies that -a and -s options cannot be used together
172 * in strictly conforming applications. However, it also says that some
173 * du implementations may produce output when -a and -s are used together.
174 * gnu du exits with an error code in this case. We choose to simply
175 * ignore -a. This is consistent with -s being equivalent to -d 0.
177 #if ENABLE_FEATURE_HUMAN_READABLE
178 opt_complementary
= "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+";
179 opt
= getopt32(argv
, "aHkLsx" "d:" "lc" "hm", &G
.max_print_depth
);
181 if (opt
& OPT_h_for_humans
) {
184 if (opt
& OPT_m_mbytes
) {
185 G
.disp_hr
= 1024*1024;
187 if (opt
& OPT_k_kbytes
) {
191 opt_complementary
= "H-L:L-H:s-d:d-s:d+";
192 opt
= getopt32(argv
, "aHkLsx" "d:" "lc", &G
.max_print_depth
);
194 #if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
195 if (opt
& OPT_k_kbytes
) {
200 if (opt
& OPT_H_follow_links
) {
203 if (opt
& OPT_L_follow_links
) {
204 G
.slink_depth
= INT_MAX
;
206 if (opt
& OPT_s_total_norecurse
) {
207 G
.max_print_depth
= 0;
210 /* go through remaining args (if any) */
212 *--argv
= (char*)".";
213 if (G
.slink_depth
== 1) {
218 slink_depth_save
= G
.slink_depth
;
222 /* otherwise du /dir /dir won't show /dir twice: */
223 reset_ino_dev_hashtable();
224 G
.slink_depth
= slink_depth_save
;
227 if (opt
& OPT_c_total
)
228 print(total
, "total");
230 fflush_stdout_and_exit(G
.status
);