source: branches/3.2/mindi-busybox/coreutils/du.c @ 3232

Last change on this file since 3232 was 3232, checked in by Bruno Cornec, 7 years ago
  • Update mindi-busybox to 1.21.1
File size: 7.3 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
62enum {
63    OPT_a_files_too    = (1 << 0),
64    OPT_H_follow_links = (1 << 1),
65    OPT_k_kbytes       = (1 << 2),
66    OPT_L_follow_links = (1 << 3),
67    OPT_s_total_norecurse = (1 << 4),
68    OPT_x_one_FS       = (1 << 5),
69    OPT_d_maxdepth     = (1 << 6),
70    OPT_l_hardlinks    = (1 << 7),
71    OPT_c_total        = (1 << 8),
72    OPT_h_for_humans   = (1 << 9),
73    OPT_m_mbytes       = (1 << 10),
74};
75
76struct globals {
77#if ENABLE_FEATURE_HUMAN_READABLE
78    unsigned long disp_hr;
79#else
80    unsigned disp_k;
81#endif
82    int max_print_depth;
83    bool status;
84    int slink_depth;
85    int du_depth;
86    dev_t dir_dev;
87} FIX_ALIASING;
88#define G (*(struct globals*)&bb_common_bufsiz1)
89#define INIT_G() do { } while (0)
90
91
92/* FIXME? coreutils' du rounds sizes up:
93 * for example,  1025k file is shown as "2" by du -m.
94 * We round to nearest.
95 */
96static void print(unsigned long long size, const char *filename)
97{
98    /* TODO - May not want to defer error checking here. */
99#if ENABLE_FEATURE_HUMAN_READABLE
100    printf("%s\t%s\n",
101            /* size x 512 / G.disp_hr, show one fractional,
102             * use suffixes if G.disp_hr == 0 */
103            make_human_readable_str(size, 512, G.disp_hr),
104            filename);
105#else
106    if (G.disp_k) {
107        size++;
108        size >>= 1;
109    }
110    printf("%llu\t%s\n", size, filename);
111#endif
112}
113
114/* tiny recursive du */
115static unsigned long long du(const char *filename)
116{
117    struct stat statbuf;
118    unsigned long long sum;
119
120    if (lstat(filename, &statbuf) != 0) {
121        bb_simple_perror_msg(filename);
122        G.status = EXIT_FAILURE;
123        return 0;
124    }
125
126    if (option_mask32 & OPT_x_one_FS) {
127        if (G.du_depth == 0) {
128            G.dir_dev = statbuf.st_dev;
129        } else if (G.dir_dev != statbuf.st_dev) {
130            return 0;
131        }
132    }
133
134    sum = statbuf.st_blocks;
135
136    if (S_ISLNK(statbuf.st_mode)) {
137        if (G.slink_depth > G.du_depth) { /* -H or -L */
138            if (stat(filename, &statbuf) != 0) {
139                bb_simple_perror_msg(filename);
140                G.status = EXIT_FAILURE;
141                return 0;
142            }
143            sum = statbuf.st_blocks;
144            if (G.slink_depth == 1) {
145                /* Convert -H to -L */
146                G.slink_depth = INT_MAX;
147            }
148        }
149    }
150
151    if (!(option_mask32 & OPT_l_hardlinks)
152     && statbuf.st_nlink > 1
153    ) {
154        /* Add files/directories with links only once */
155        if (is_in_ino_dev_hashtable(&statbuf)) {
156            return 0;
157        }
158        add_to_ino_dev_hashtable(&statbuf, NULL);
159    }
160
161    if (S_ISDIR(statbuf.st_mode)) {
162        DIR *dir;
163        struct dirent *entry;
164        char *newfile;
165
166        dir = warn_opendir(filename);
167        if (!dir) {
168            G.status = EXIT_FAILURE;
169            return sum;
170        }
171
172        while ((entry = readdir(dir))) {
173            newfile = concat_subpath_file(filename, entry->d_name);
174            if (newfile == NULL)
175                continue;
176            ++G.du_depth;
177            sum += du(newfile);
178            --G.du_depth;
179            free(newfile);
180        }
181        closedir(dir);
182    } else {
183        if (!(option_mask32 & OPT_a_files_too) && G.du_depth != 0)
184            return sum;
185    }
186    if (G.du_depth <= G.max_print_depth) {
187        print(sum, filename);
188    }
189    return sum;
190}
191
192int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
193int du_main(int argc UNUSED_PARAM, char **argv)
194{
195    unsigned long long total;
196    int slink_depth_save;
197    unsigned opt;
198
199    INIT_G();
200
201#if ENABLE_FEATURE_HUMAN_READABLE
202    IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 1024;)
203    IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 512;)
204    if (getenv("POSIXLY_CORRECT"))  /* TODO - a new libbb function? */
205        G.disp_hr = 512;
206#else
207    IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;)
208    /* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */
209#endif
210    G.max_print_depth = INT_MAX;
211
212    /* Note: SUSv3 specifies that -a and -s options cannot be used together
213     * in strictly conforming applications.  However, it also says that some
214     * du implementations may produce output when -a and -s are used together.
215     * gnu du exits with an error code in this case.  We choose to simply
216     * ignore -a.  This is consistent with -s being equivalent to -d 0.
217     */
218#if ENABLE_FEATURE_HUMAN_READABLE
219    opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+";
220    opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth);
221    argv += optind;
222    if (opt & OPT_h_for_humans) {
223        G.disp_hr = 0;
224    }
225    if (opt & OPT_m_mbytes) {
226        G.disp_hr = 1024*1024;
227    }
228    if (opt & OPT_k_kbytes) {
229        G.disp_hr = 1024;
230    }
231#else
232    opt_complementary = "H-L:L-H:s-d:d-s:d+";
233    opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth);
234    argv += optind;
235#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
236    if (opt & OPT_k_kbytes) {
237        G.disp_k = 1;
238    }
239#endif
240#endif
241    if (opt & OPT_H_follow_links) {
242        G.slink_depth = 1;
243    }
244    if (opt & OPT_L_follow_links) {
245        G.slink_depth = INT_MAX;
246    }
247    if (opt & OPT_s_total_norecurse) {
248        G.max_print_depth = 0;
249    }
250
251    /* go through remaining args (if any) */
252    if (!*argv) {
253        *--argv = (char*)".";
254        if (G.slink_depth == 1) {
255            G.slink_depth = 0;
256        }
257    }
258
259    slink_depth_save = G.slink_depth;
260    total = 0;
261    do {
262        total += du(*argv);
263        /* otherwise du /dir /dir won't show /dir twice: */
264        reset_ino_dev_hashtable();
265        G.slink_depth = slink_depth_save;
266    } while (*++argv);
267
268    if (opt & OPT_c_total)
269        print(total, "total");
270
271    fflush_stdout_and_exit(G.status);
272}
Note: See TracBrowser for help on using the repository browser.