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