1 /* 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Chris Newcomb. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static const char copyright[] = 39 "@(#) Copyright (c) 1989, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 #if 0 45 static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; 46 #endif 47 static const char rcsid[] = 48 "$FreeBSD$"; 49 #endif /* not lint */ 50 51 52 #include <sys/param.h> 53 #include <sys/queue.h> 54 #include <sys/stat.h> 55 56 #include <err.h> 57 #include <errno.h> 58 #include <fnmatch.h> 59 #include <fts.h> 60 #include <math.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <sysexits.h> 65 #include <unistd.h> 66 67 #define KILO_SZ(n) (n) 68 #define MEGA_SZ(n) ((n) * (n)) 69 #define GIGA_SZ(n) ((n) * (n) * (n)) 70 #define TERA_SZ(n) ((n) * (n) * (n) * (n)) 71 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) 72 73 #define KILO_2_SZ (KILO_SZ(1024ULL)) 74 #define MEGA_2_SZ (MEGA_SZ(1024ULL)) 75 #define GIGA_2_SZ (GIGA_SZ(1024ULL)) 76 #define TERA_2_SZ (TERA_SZ(1024ULL)) 77 #define PETA_2_SZ (PETA_SZ(1024ULL)) 78 79 #define KILO_SI_SZ (KILO_SZ(1000ULL)) 80 #define MEGA_SI_SZ (MEGA_SZ(1000ULL)) 81 #define GIGA_SI_SZ (GIGA_SZ(1000ULL)) 82 #define TERA_SI_SZ (TERA_SZ(1000ULL)) 83 #define PETA_SI_SZ (PETA_SZ(1000ULL)) 84 85 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; 86 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; 87 unsigned long long *valp; 88 89 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; 90 91 int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; 92 93 SLIST_HEAD(ignhead, ignentry) ignores; 94 struct ignentry { 95 char *mask; 96 SLIST_ENTRY(ignentry) next; 97 }; 98 99 int linkchk(FTSENT *); 100 static void usage(void); 101 void prthumanval(double); 102 unit_t unit_adjust(double *); 103 void ignoreadd(const char *); 104 void ignoreclean(void); 105 int ignorep(FTSENT *); 106 107 int 108 main(argc, argv) 109 int argc; 110 char *argv[]; 111 { 112 FTS *fts; 113 FTSENT *p; 114 long blocksize, savednumber = 0; 115 int ftsoptions; 116 int listall; 117 int depth; 118 int Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval; 119 char **save; 120 static char dot[] = "."; 121 122 Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0; 123 124 save = argv; 125 ftsoptions = 0; 126 depth = INT_MAX; 127 SLIST_INIT(&ignores); 128 129 while ((ch = getopt(argc, argv, "HI:LPasd:chkrx")) != -1) 130 switch (ch) { 131 case 'H': 132 Hflag = 1; 133 break; 134 case 'I': 135 ignoreadd(optarg); 136 break; 137 case 'L': 138 if (Pflag) 139 usage(); 140 Lflag = 1; 141 break; 142 case 'P': 143 if (Lflag) 144 usage(); 145 Pflag = 1; 146 break; 147 case 'a': 148 aflag = 1; 149 break; 150 case 's': 151 sflag = 1; 152 break; 153 case 'd': 154 dflag = 1; 155 errno = 0; 156 depth = atoi(optarg); 157 if (errno == ERANGE || depth < 0) { 158 warnx("invalid argument to option d: %s", optarg); 159 usage(); 160 } 161 break; 162 case 'c': 163 cflag = 1; 164 break; 165 case 'h': 166 putenv("BLOCKSIZE=512"); 167 hflag = 1; 168 valp = vals_base2; 169 break; 170 case 'k': 171 if (!hflag) 172 putenv("BLOCKSIZE=1024"); 173 break; 174 case 'r': /* Compatibility. */ 175 break; 176 case 'x': 177 ftsoptions |= FTS_XDEV; 178 break; 179 case '?': 180 default: 181 usage(); 182 } 183 184 argc -= optind; 185 argv += optind; 186 187 /* 188 * XXX 189 * Because of the way that fts(3) works, logical walks will not count 190 * the blocks actually used by symbolic links. We rationalize this by 191 * noting that users computing logical sizes are likely to do logical 192 * copies, so not counting the links is correct. The real reason is 193 * that we'd have to re-implement the kernel's symbolic link traversing 194 * algorithm to get this right. If, for example, you have relative 195 * symbolic links referencing other relative symbolic links, it gets 196 * very nasty, very fast. The bottom line is that it's documented in 197 * the man page, so it's a feature. 198 */ 199 200 if (Hflag + Lflag + Pflag > 1) 201 usage(); 202 203 if (Hflag + Lflag + Pflag == 0) 204 Pflag = 1; /* -P (physical) is default */ 205 206 if (Hflag) 207 ftsoptions |= FTS_COMFOLLOW; 208 209 if (Lflag) 210 ftsoptions |= FTS_LOGICAL; 211 212 if (Pflag) 213 ftsoptions |= FTS_PHYSICAL; 214 215 listall = 0; 216 217 if (aflag) { 218 if (sflag || dflag) 219 usage(); 220 listall = 1; 221 } else if (sflag) { 222 if (dflag) 223 usage(); 224 depth = 0; 225 } 226 227 if (!*argv) { 228 argv = save; 229 argv[0] = dot; 230 argv[1] = NULL; 231 } 232 233 (void) getbsize(¬used, &blocksize); 234 blocksize /= 512; 235 236 rval = 0; 237 238 if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) 239 err(1, "fts_open"); 240 241 while ((p = fts_read(fts)) != NULL) { 242 switch (p->fts_info) { 243 case FTS_D: /* Ignore. */ 244 if (ignorep(p)) 245 fts_set(fts, p, FTS_SKIP); 246 break; 247 case FTS_DP: 248 if (ignorep(p)) 249 break; 250 251 p->fts_parent->fts_number += 252 p->fts_number += p->fts_statp->st_blocks; 253 254 if (p->fts_level <= depth) { 255 if (hflag) { 256 (void) prthumanval(howmany(p->fts_number, blocksize)); 257 (void) printf("\t%s\n", p->fts_path); 258 } else { 259 (void) printf("%ld\t%s\n", 260 howmany(p->fts_number, blocksize), 261 p->fts_path); 262 } 263 } 264 break; 265 case FTS_DC: /* Ignore. */ 266 break; 267 case FTS_DNR: /* Warn, continue. */ 268 case FTS_ERR: 269 case FTS_NS: 270 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 271 rval = 1; 272 break; 273 default: 274 if (ignorep(p)) 275 break; 276 277 if (p->fts_statp->st_nlink > 1 && linkchk(p)) 278 break; 279 280 if (listall || p->fts_level == 0) { 281 if (hflag) { 282 (void) prthumanval(howmany(p->fts_statp->st_blocks, 283 blocksize)); 284 (void) printf("\t%s\n", p->fts_path); 285 } else { 286 (void) printf("%qd\t%s\n", 287 (long long)howmany(p->fts_statp->st_blocks, blocksize), 288 p->fts_path); 289 } 290 } 291 292 p->fts_parent->fts_number += p->fts_statp->st_blocks; 293 } 294 savednumber = p->fts_parent->fts_number; 295 } 296 297 if (errno) 298 err(1, "fts_read"); 299 300 if (cflag) { 301 if (hflag) { 302 (void) prthumanval(howmany(savednumber, blocksize)); 303 (void) printf("\ttotal\n"); 304 } else { 305 (void) printf("%ld\ttotal\n", howmany(savednumber, blocksize)); 306 } 307 } 308 309 ignoreclean(); 310 exit(rval); 311 } 312 313 314 typedef struct _ID { 315 dev_t dev; 316 ino_t inode; 317 } ID; 318 319 320 int 321 linkchk(p) 322 FTSENT *p; 323 { 324 static ID *files; 325 static int maxfiles, nfiles; 326 ID *fp, *start; 327 ino_t ino; 328 dev_t dev; 329 330 ino = p->fts_statp->st_ino; 331 dev = p->fts_statp->st_dev; 332 if ((start = files) != NULL) 333 for (fp = start + nfiles - 1; fp >= start; --fp) 334 if (ino == fp->inode && dev == fp->dev) 335 return (1); 336 337 if (nfiles == maxfiles && (files = realloc((char *)files, 338 (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL) 339 errx(1, "can't allocate memory"); 340 files[nfiles].inode = ino; 341 files[nfiles].dev = dev; 342 ++nfiles; 343 return (0); 344 } 345 346 /* 347 * Output in "human-readable" format. Uses 3 digits max and puts 348 * unit suffixes at the end. Makes output compact and easy to read, 349 * especially on huge disks. 350 * 351 */ 352 unit_t 353 unit_adjust(val) 354 double *val; 355 { 356 double abval; 357 unit_t unit; 358 unsigned int unit_sz; 359 360 abval = fabs(*val); 361 362 unit_sz = abval ? ilogb(abval) / 10 : 0; 363 364 if (unit_sz >= UNIT_MAX) { 365 unit = NONE; 366 } else { 367 unit = unitp[unit_sz]; 368 *val /= (double)valp[unit_sz]; 369 } 370 371 return (unit); 372 } 373 374 void 375 prthumanval(bytes) 376 double bytes; 377 { 378 unit_t unit; 379 380 bytes *= 512; 381 unit = unit_adjust(&bytes); 382 383 if (bytes == 0) 384 (void)printf(" 0B"); 385 else if (bytes > 10) 386 (void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]); 387 else 388 (void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]); 389 } 390 391 static void 392 usage() 393 { 394 (void)fprintf(stderr, 395 "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k] [-x] [-I mask] [file ...]\n"); 396 exit(EX_USAGE); 397 } 398 399 void 400 ignoreadd(mask) 401 const char *mask; 402 { 403 struct ignentry *ign; 404 405 ign = calloc(1, sizeof(*ign)); 406 if (ign == NULL) 407 errx(1, "cannot allocate memory"); 408 ign->mask = strdup(mask); 409 if (ign->mask == NULL) 410 errx(1, "cannot allocate memory"); 411 SLIST_INSERT_HEAD(&ignores, ign, next); 412 } 413 414 void 415 ignoreclean() 416 { 417 struct ignentry *ign; 418 419 while (!SLIST_EMPTY(&ignores)) { 420 ign = SLIST_FIRST(&ignores); 421 SLIST_REMOVE_HEAD(&ignores, next); 422 free(ign->mask); 423 free(ign); 424 } 425 } 426 427 int 428 ignorep(ent) 429 FTSENT *ent; 430 { 431 struct ignentry *ign; 432 433 SLIST_FOREACH(ign, &ignores, next) 434 if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH) 435 return 1; 436 return 0; 437 } 438