1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 /* 34 * du -- summarize disk usage 35 * /bin/du [-a][-d][-h|-k][-H|-L][-r][-o|-s] [file ...] 36 * /usr/xpg4/bin/du [-a][-h|-k][-H|-L][-r][-s][-x] [file ...] 37 */ 38 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/avl.h> 42 #include <fcntl.h> 43 #include <dirent.h> 44 #include <limits.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <locale.h> 50 #include <libcmdutils.h> 51 52 53 static int aflg = 0; 54 static int rflg = 0; 55 static int sflg = 0; 56 static int kflg = 0; 57 static int oflg = 0; 58 static int dflg = 0; 59 static int hflg = 0; 60 static int Hflg = 0; 61 static int Lflg = 0; 62 static int cmdarg = 0; /* Command line argument */ 63 static char *dot = "."; 64 static int level = 0; /* Level of recursion */ 65 66 static char *base; 67 static char *name; 68 static size_t base_len = PATH_MAX + 1; /* # of chars for base */ 69 static size_t name_len = PATH_MAX + 1; /* # of chars for name */ 70 71 #define NUMBER_WIDTH 64 72 typedef char numbuf_t[NUMBER_WIDTH]; 73 74 /* 75 * convert DEV_BSIZE blocks to K blocks 76 */ 77 #define DEV_BSIZE 512 78 #define DEV_KSHIFT 1 79 #define kb(n) (((u_longlong_t)(n)) >> DEV_KSHIFT) 80 81 long wait(); 82 static u_longlong_t descend(char *curname, int curfd, int *retcode, 83 dev_t device); 84 static void printsize(blkcnt_t blocks, char *path); 85 static void exitdu(int exitcode); 86 87 static avl_tree_t *tree = NULL; 88 89 int 90 main(int argc, char **argv) 91 { 92 blkcnt_t blocks = 0; 93 int c; 94 extern int optind; 95 char *np; 96 pid_t pid, wpid; 97 int status, retcode = 0; 98 setbuf(stderr, NULL); 99 (void) setlocale(LC_ALL, ""); 100 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 101 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 102 #endif 103 (void) textdomain(TEXT_DOMAIN); 104 105 #ifdef XPG4 106 rflg++; /* "-r" is not an option but ON always */ 107 #endif 108 109 #ifdef XPG4 110 while ((c = getopt(argc, argv, "ahHkLrsx")) != EOF) 111 #else 112 while ((c = getopt(argc, argv, "adhHkLors")) != EOF) 113 #endif 114 switch (c) { 115 116 case 'a': 117 aflg++; 118 continue; 119 120 case 'h': 121 hflg++; 122 continue; 123 124 case 'r': 125 rflg++; 126 continue; 127 128 case 's': 129 sflg++; 130 continue; 131 132 case 'k': 133 kflg++; 134 continue; 135 136 case 'o': 137 oflg++; 138 continue; 139 140 case 'd': 141 dflg++; 142 continue; 143 144 case 'x': 145 dflg++; 146 continue; 147 148 case 'H': 149 Hflg++; 150 /* -H and -L are mutually exclusive */ 151 Lflg = 0; 152 cmdarg++; 153 continue; 154 155 case 'L': 156 Lflg++; 157 /* -H and -L are mutually exclusive */ 158 Hflg = 0; 159 cmdarg = 0; 160 continue; 161 #ifdef XPG4 162 case '?': 163 (void) fprintf(stderr, gettext( 164 "usage: du [-a] [-h|-k] [-r] [-s] [-x] [-H|-L]" 165 " [file ...]\n")); 166 exit(2); 167 #else 168 case '?': 169 (void) fprintf(stderr, gettext( 170 "usage: du [-a] [-d] [-h|-k] [-r] [-o|-s] [-H|-L]" 171 " [file ...]\n")); 172 exit(2); 173 #endif 174 } 175 if (optind == argc) { 176 argv = ˙ 177 argc = 1; 178 optind = 0; 179 } 180 181 /* "-o" and "-s" don't make any sense together. */ 182 if (oflg && sflg) 183 oflg = 0; 184 185 if ((base = (char *)calloc(base_len, sizeof (char))) == NULL) { 186 perror("du"); 187 exit(1); 188 } 189 if ((name = (char *)calloc(name_len, sizeof (char))) == NULL) { 190 perror("du"); 191 free(base); 192 exit(1); 193 } 194 do { 195 if (optind < argc - 1) { 196 pid = fork(); 197 if (pid == (pid_t)-1) { 198 perror(gettext("du: No more processes")); 199 exitdu(1); 200 } 201 if (pid != 0) { 202 while ((wpid = wait(&status)) != pid && 203 wpid != (pid_t)-1) 204 ; 205 if (pid != (pid_t)-1 && status != 0) 206 retcode = 1; 207 } 208 } 209 if (optind == argc - 1 || pid == 0) { 210 while (base_len < (strlen(argv[optind]) + 1)) { 211 base_len = base_len * 2; 212 if ((base = (char *)realloc(base, base_len * 213 sizeof (char))) == NULL) { 214 if (rflg) { 215 (void) fprintf(stderr, gettext( 216 "du: can't process %s"), 217 argv[optind]); 218 perror(""); 219 } 220 exitdu(1); 221 } 222 } 223 if (base_len > name_len) { 224 name_len = base_len; 225 if ((name = (char *)realloc(name, name_len * 226 sizeof (char))) == NULL) { 227 if (rflg) { 228 (void) fprintf(stderr, gettext( 229 "du: can't process %s"), 230 argv[optind]); 231 perror(""); 232 } 233 exitdu(1); 234 } 235 } 236 (void) strcpy(base, argv[optind]); 237 (void) strcpy(name, argv[optind]); 238 if (np = strrchr(name, '/')) { 239 *np++ = '\0'; 240 if (chdir(*name ? name : "/") < 0) { 241 if (rflg) { 242 (void) fprintf(stderr, "du: "); 243 perror(*name ? name : "/"); 244 exitdu(1); 245 } 246 exitdu(0); 247 } 248 } else 249 np = base; 250 blocks = descend(*np ? np : ".", 0, &retcode, 251 (dev_t)0); 252 if (sflg) 253 printsize(blocks, base); 254 if (optind < argc - 1) 255 exitdu(retcode); 256 } 257 optind++; 258 } while (optind < argc); 259 exitdu(retcode); 260 261 return (retcode); 262 } 263 264 /* 265 * descend recursively, adding up the allocated blocks. 266 * If curname is NULL, curfd is used. 267 */ 268 static u_longlong_t 269 descend(char *curname, int curfd, int *retcode, dev_t device) 270 { 271 static DIR *dirp = NULL; 272 char *ebase0, *ebase; 273 struct stat stb, stb1; 274 int i, j, ret, fd, tmpflg; 275 blkcnt_t blocks = 0; 276 off_t curoff = 0; 277 ptrdiff_t offset; 278 ptrdiff_t offset0; 279 struct dirent *dp; 280 char dirbuf[PATH_MAX + 1]; 281 u_longlong_t retval; 282 283 ebase0 = ebase = strchr(base, 0); 284 if (ebase > base && ebase[-1] == '/') 285 ebase--; 286 offset = ebase - base; 287 offset0 = ebase0 - base; 288 289 if (curname) 290 curfd = AT_FDCWD; 291 292 /* 293 * If neither a -L or a -H was specified, don't follow symlinks. 294 * If a -H was specified, don't follow symlinks if the file is 295 * not a command line argument. 296 */ 297 if (((Lflg == 0) && (Hflg == 0)) || ((Hflg) && !(cmdarg))) { 298 i = fstatat(curfd, curname, &stb, AT_SYMLINK_NOFOLLOW); 299 j = 0; 300 } else { 301 i = fstatat(curfd, curname, &stb, 0); 302 j = fstatat(curfd, curname, &stb1, AT_SYMLINK_NOFOLLOW); 303 304 /* 305 * Make sure any files encountered while traversing the 306 * hierarchy are not considered command line arguments. 307 */ 308 if (Hflg) { 309 cmdarg = 0; 310 } 311 } 312 313 if ((i < 0) || (j < 0)) { 314 if (rflg) { 315 (void) fprintf(stderr, "du: "); 316 perror(base); 317 } 318 319 /* 320 * POSIX states that non-zero status codes are only set 321 * when an error message is printed out on stderr 322 */ 323 *retcode = (rflg ? 1 : 0); 324 *ebase0 = 0; 325 return (0); 326 } 327 if (device) { 328 if (dflg && stb.st_dev != device) { 329 *ebase0 = 0; 330 return (0); 331 } 332 } 333 else 334 device = stb.st_dev; 335 336 /* 337 * If following links (-L) we need to keep track of all inodes 338 * visited so they are only visited/reported once and cycles 339 * are avoided. Otherwise, only keep track of files which are 340 * hard links so they only get reported once, and of directories 341 * so we don't report a directory and its hierarchy more than 342 * once in the special case in which it lies under the 343 * hierarchy of a directory which is a hard link. 344 * Note: Files with multiple links should only be counted 345 * once. Since each inode could possibly be referenced by a 346 * symbolic link, we need to keep track of all inodes when -L 347 * is specified. 348 */ 349 if ((Lflg) || ((stb.st_mode & S_IFMT) == S_IFDIR) || 350 (stb.st_nlink > 1)) { 351 int rc; 352 if ((rc = add_tnode(&tree, stb.st_dev, stb.st_ino)) != 1) { 353 if (rc == 0) { 354 /* 355 * This hierarchy, or file with multiple 356 * links, has already been visited/reported. 357 */ 358 return (0); 359 } else { 360 /* 361 * An error occurred while trying to add the 362 * node to the tree. 363 */ 364 if (rflg) { 365 perror("du"); 366 } 367 exitdu(1); 368 } 369 } 370 } 371 blocks = stb.st_blocks; 372 /* 373 * If there are extended attributes on the current file, add their 374 * block usage onto the block count. 375 */ 376 if (curname && pathconf(curname, _PC_XATTR_EXISTS) == 1) { 377 if ((fd = attropen(curname, ".", O_RDONLY)) < 0) { 378 if (rflg) 379 perror(gettext( 380 "du: can't access extended attributes")); 381 } 382 else 383 { 384 tmpflg = sflg; 385 sflg = 1; 386 blocks += descend(NULL, fd, retcode, device); 387 sflg = tmpflg; 388 } 389 } 390 if ((stb.st_mode & S_IFMT) != S_IFDIR) { 391 /* 392 * Don't print twice: if sflg, file will get printed in main(). 393 * Otherwise, level == 0 means this file is listed on the 394 * command line, so print here; aflg means print all files. 395 */ 396 if (sflg == 0 && (aflg || level == 0)) 397 printsize(blocks, base); 398 return (blocks); 399 } 400 if (dirp != NULL) 401 /* 402 * Close the parent directory descriptor, we will reopen 403 * the directory when we pop up from this level of the 404 * recursion. 405 */ 406 (void) closedir(dirp); 407 if (curname == NULL) 408 dirp = fdopendir(curfd); 409 else 410 dirp = opendir(curname); 411 if (dirp == NULL) { 412 if (rflg) { 413 (void) fprintf(stderr, "du: "); 414 perror(base); 415 } 416 *retcode = 1; 417 *ebase0 = 0; 418 return (0); 419 } 420 level++; 421 if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode))) { 422 if (getcwd(dirbuf, PATH_MAX) == NULL) { 423 if (rflg) { 424 (void) fprintf(stderr, "du: "); 425 perror(base); 426 } 427 exitdu(1); 428 } 429 } 430 if ((curname ? (chdir(curname) < 0) : (fchdir(curfd) < 0))) { 431 if (rflg) { 432 (void) fprintf(stderr, "du: "); 433 perror(base); 434 } 435 *retcode = 1; 436 *ebase0 = 0; 437 (void) closedir(dirp); 438 dirp = NULL; 439 level--; 440 return (0); 441 } 442 while (dp = readdir(dirp)) { 443 if ((strcmp(dp->d_name, ".") == 0) || 444 (strcmp(dp->d_name, "..") == 0)) 445 continue; 446 /* 447 * we're about to append "/" + dp->d_name 448 * onto end of base; make sure there's enough 449 * space 450 */ 451 while ((offset + strlen(dp->d_name) + 2) > base_len) { 452 base_len = base_len * 2; 453 if ((base = (char *)realloc(base, 454 base_len * sizeof (char))) == NULL) { 455 if (rflg) { 456 perror("du"); 457 } 458 exitdu(1); 459 } 460 ebase = base + offset; 461 ebase0 = base + offset0; 462 } 463 /* LINTED - unbounded string specifier */ 464 (void) sprintf(ebase, "/%s", dp->d_name); 465 curoff = telldir(dirp); 466 retval = descend(ebase + 1, 0, retcode, device); 467 /* base may have been moved via realloc in descend() */ 468 ebase = base + offset; 469 ebase0 = base + offset0; 470 *ebase = 0; 471 blocks += retval; 472 if (dirp == NULL) { 473 if ((dirp = opendir(".")) == NULL) { 474 if (rflg) { 475 (void) fprintf(stderr, 476 gettext("du: Can't reopen in ")); 477 perror(base); 478 } 479 *retcode = 1; 480 level--; 481 return (0); 482 } 483 seekdir(dirp, curoff); 484 } 485 } 486 (void) closedir(dirp); 487 level--; 488 dirp = NULL; 489 if (sflg == 0) 490 printsize(blocks, base); 491 if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode))) 492 ret = chdir(dirbuf); 493 else 494 ret = chdir(".."); 495 if (ret < 0) { 496 if (rflg) { 497 (void) sprintf(strchr(base, '\0'), "/.."); 498 (void) fprintf(stderr, 499 gettext("du: Can't change dir to '..' in ")); 500 perror(base); 501 } 502 exitdu(1); 503 } 504 *ebase0 = 0; 505 if (oflg) 506 return (0); 507 else 508 return (blocks); 509 } 510 511 /* 512 * Convert an unsigned long long to a string representation and place the 513 * result in the caller-supplied buffer. 514 * The given number is in units of "unit_from" size, 515 * this will first be converted to a number in 1024 or 1000 byte size, 516 * depending on the scaling factor. 517 * Then the number is scaled down until it is small enough to be in a good 518 * human readable format i.e. in the range 0 thru scale-1. 519 * If it's smaller than 10 there's room enough to provide one decimal place. 520 * The value "(unsigned long long)-1" is a special case and is always 521 * converted to "-1". 522 * Returns a pointer to the caller-supplied buffer. 523 */ 524 static char * 525 number_to_scaled_string( 526 numbuf_t buf, /* put the result here */ 527 unsigned long long number, /* convert this number */ 528 unsigned long long unit_from, /* number of bytes per input unit */ 529 unsigned long long scale) /* 1024 (-h) or 1000 (-H) */ 530 { 531 unsigned long long save = 0; 532 char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */ 533 char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */ 534 535 if ((long long)number == (long long)-1) { 536 (void) strcpy(buf, "-1"); 537 return (buf); 538 } 539 540 /* 541 * Convert number from unit_from to given scale (1024 or 1000) 542 * This means multiply number with unit_from and divide by scale. 543 * if number is large enough, we first divide and then multiply 544 * to avoid an overflow 545 * (large enough here means 100 (rather arbitrary value) 546 * times scale in order to reduce rounding errors) 547 * otherwise, we first multiply and then divide 548 * to avoid an underflow 549 */ 550 if (number >= 100L * scale) { 551 number = number / scale; 552 number = number * unit_from; 553 } else { 554 number = number * unit_from; 555 number = number / scale; 556 } 557 558 /* 559 * Now we have number as a count of scale units. 560 * Stop scaling when we reached exa bytes, then something is 561 * probably wrong with our number. 562 */ 563 while ((number >= scale) && (*uom != 'E')) { 564 uom++; /* next unit of measurement */ 565 save = number; 566 number = (number + (scale / 2)) / scale; 567 } 568 569 /* check if we should output a decimal place after the point */ 570 if (save && ((save / scale) < 10)) { 571 /* sprintf() will round for us */ 572 float fnum = (float)save / scale; 573 (void) sprintf(buf, "%4.1f%c", fnum, *uom); 574 } else { 575 (void) sprintf(buf, "%4llu%c", number, *uom); 576 } 577 return (buf); 578 } 579 580 static void 581 printsize(blkcnt_t blocks, char *path) 582 { 583 if (hflg) { 584 numbuf_t numbuf; 585 unsigned long long scale = 1024L; 586 #ifdef XPG4 587 (void) printf("%s %s\n", 588 number_to_scaled_string(numbuf, blocks, DEV_BSIZE, scale), 589 path); 590 #else 591 (void) printf("%s\t%s\n", 592 number_to_scaled_string(numbuf, blocks, DEV_BSIZE, scale), 593 path); 594 #endif 595 } else if (kflg) { 596 #ifdef XPG4 597 (void) printf("%lld %s\n", (long long)kb(blocks), path); 598 #else 599 (void) printf("%lld\t%s\n", (long long)kb(blocks), path); 600 #endif 601 } else { 602 #ifdef XPG4 603 (void) printf("%lld %s\n", (long long)blocks, path); 604 #else 605 (void) printf("%lld\t%s\n", (long long)blocks, path); 606 #endif 607 } 608 } 609 610 static void 611 exitdu(int exitcode) 612 { 613 free(base); 614 free(name); 615 exit(exitcode); 616 } 617