busybox/coreutils/du.c
<<
>>
Prefs
   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 tarball for details.
  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#include "libbb.h"
  27
  28enum {
  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),
  40};
  41
  42struct globals {
  43#if ENABLE_FEATURE_HUMAN_READABLE
  44        unsigned long disp_hr;
  45#else
  46        unsigned disp_k;
  47#endif
  48        int max_print_depth;
  49        bool status;
  50        int slink_depth;
  51        int du_depth;
  52        dev_t dir_dev;
  53};
  54#define G (*(struct globals*)&bb_common_bufsiz1)
  55
  56
  57static void print(unsigned long size, const char *filename)
  58{
  59        /* TODO - May not want to defer error checking here. */
  60#if ENABLE_FEATURE_HUMAN_READABLE
  61        printf("%s\t%s\n", make_human_readable_str(size, 512, G.disp_hr),
  62                        filename);
  63#else
  64        if (G.disp_k) {
  65                size++;
  66                size >>= 1;
  67        }
  68        printf("%ld\t%s\n", size, filename);
  69#endif
  70}
  71
  72/* tiny recursive du */
  73static unsigned long du(const char *filename)
  74{
  75        struct stat statbuf;
  76        unsigned long sum;
  77
  78        if (lstat(filename, &statbuf) != 0) {
  79                bb_simple_perror_msg(filename);
  80                G.status = EXIT_FAILURE;
  81                return 0;
  82        }
  83
  84        if (option_mask32 & OPT_x_one_FS) {
  85                if (G.du_depth == 0) {
  86                        G.dir_dev = statbuf.st_dev;
  87                } else if (G.dir_dev != statbuf.st_dev) {
  88                        return 0;
  89                }
  90        }
  91
  92        sum = statbuf.st_blocks;
  93
  94        if (S_ISLNK(statbuf.st_mode)) {
  95                if (G.slink_depth > G.du_depth) { /* -H or -L */
  96                        if (stat(filename, &statbuf) != 0) {
  97                                bb_simple_perror_msg(filename);
  98                                G.status = EXIT_FAILURE;
  99                                return 0;
 100                        }
 101                        sum = statbuf.st_blocks;
 102                        if (G.slink_depth == 1) {
 103                                /* Convert -H to -L */
 104                                G.slink_depth = INT_MAX;
 105                        }
 106                }
 107        }
 108
 109        if (!(option_mask32 & OPT_l_hardlinks)
 110         && statbuf.st_nlink > 1
 111        ) {
 112                /* Add files/directories with links only once */
 113                if (is_in_ino_dev_hashtable(&statbuf)) {
 114                        return 0;
 115                }
 116                add_to_ino_dev_hashtable(&statbuf, NULL);
 117        }
 118
 119        if (S_ISDIR(statbuf.st_mode)) {
 120                DIR *dir;
 121                struct dirent *entry;
 122                char *newfile;
 123
 124                dir = warn_opendir(filename);
 125                if (!dir) {
 126                        G.status = EXIT_FAILURE;
 127                        return sum;
 128                }
 129
 130                while ((entry = readdir(dir))) {
 131                        newfile = concat_subpath_file(filename, entry->d_name);
 132                        if (newfile == NULL)
 133                                continue;
 134                        ++G.du_depth;
 135                        sum += du(newfile);
 136                        --G.du_depth;
 137                        free(newfile);
 138                }
 139                closedir(dir);
 140        } else {
 141                if (!(option_mask32 & OPT_a_files_too) && G.du_depth != 0)
 142                        return sum;
 143        }
 144        if (G.du_depth <= G.max_print_depth) {
 145                print(sum, filename);
 146        }
 147        return sum;
 148}
 149
 150int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 151int du_main(int argc UNUSED_PARAM, char **argv)
 152{
 153        unsigned long total;
 154        int slink_depth_save;
 155        unsigned opt;
 156
 157#if ENABLE_FEATURE_HUMAN_READABLE
 158        USE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 1024;)
 159        SKIP_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 512;)
 160        if (getenv("POSIXLY_CORRECT"))  /* TODO - a new libbb function? */
 161                G.disp_hr = 512;
 162#else
 163        USE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;)
 164        /* SKIP_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */
 165#endif
 166        G.max_print_depth = INT_MAX;
 167
 168        /* Note: SUSv3 specifies that -a and -s options cannot be used together
 169         * in strictly conforming applications.  However, it also says that some
 170         * du implementations may produce output when -a and -s are used together.
 171         * gnu du exits with an error code in this case.  We choose to simply
 172         * ignore -a.  This is consistent with -s being equivalent to -d 0.
 173         */
 174#if ENABLE_FEATURE_HUMAN_READABLE
 175        opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+";
 176        opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth);
 177        argv += optind;
 178        if (opt & OPT_h_for_humans) {
 179                G.disp_hr = 0;
 180        }
 181        if (opt & OPT_m_mbytes) {
 182                G.disp_hr = 1024*1024;
 183        }
 184        if (opt & OPT_k_kbytes) {
 185                G.disp_hr = 1024;
 186        }
 187#else
 188        opt_complementary = "H-L:L-H:s-d:d-s:d+";
 189        opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth);
 190        argv += optind;
 191#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
 192        if (opt & OPT_k_kbytes) {
 193                G.disp_k = 1;
 194        }
 195#endif
 196#endif
 197        if (opt & OPT_H_follow_links) {
 198                G.slink_depth = 1;
 199        }
 200        if (opt & OPT_L_follow_links) {
 201                G.slink_depth = INT_MAX;
 202        }
 203        if (opt & OPT_s_total_norecurse) {
 204                G.max_print_depth = 0;
 205        }
 206
 207        /* go through remaining args (if any) */
 208        if (!*argv) {
 209                *--argv = (char*)".";
 210                if (G.slink_depth == 1) {
 211                        G.slink_depth = 0;
 212                }
 213        }
 214
 215        slink_depth_save = G.slink_depth;
 216        total = 0;
 217        do {
 218                total += du(*argv);
 219                /* otherwise du /dir /dir won't show /dir twice: */
 220                reset_ino_dev_hashtable();
 221                G.slink_depth = slink_depth_save;
 222        } while (*++argv);
 223
 224        if (opt & OPT_c_total)
 225                print(total, "total");
 226
 227        fflush_stdout_and_exit(G.status);
 228}
 229