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 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * df 44 */ 45 #include <stdio.h> 46 #include <fcntl.h> 47 #include <sys/param.h> 48 #include <sys/types.h> 49 #include <sys/mntent.h> 50 #include <sys/fs/ufs_fs.h> 51 #include <sys/stat.h> 52 #include <sys/vfs.h> 53 #include <sys/file.h> 54 #include <sys/statvfs.h> 55 #include <sys/mnttab.h> 56 #include <sys/mkdev.h> 57 #include <locale.h> 58 #include <stdarg.h> 59 #include <string.h> 60 #include <errno.h> 61 #include <libintl.h> 62 63 extern char *getenv(); 64 extern char *getcwd(); 65 extern char *realpath(); 66 extern off_t lseek(); 67 68 /* 69 * Raw name to block device name translation function. 70 * This comes from libadm. 71 */ 72 extern char *getfullblkname(); 73 74 static void usage(), pheader(); 75 static char *mpath(), *zap_chroot(); 76 static char *pathsuffix(); 77 static char *xmalloc(); 78 static int chroot_stat(); 79 static int bread(); 80 static int abspath(), subpath(); 81 static void show_inode_usage(); 82 static void dfreedev(); 83 static void dfreemnt(); 84 static void print_totals(); 85 static void print_itotals(); 86 static void print_statvfs(); 87 static struct mntlist *mkmntlist(); 88 static struct mnttab *mntdup(), *mdev(char *); 89 static struct mntlist *findmntent(); 90 91 #define bcopy(f, t, n) memcpy(t, f, n) 92 #define bzero(s, n) memset(s, 0, n) 93 #define bcmp(s, d, n) memcmp(s, d, n) 94 95 #define index(s, r) strchr(s, r) 96 #define rindex(s, r) strrchr(s, r) 97 98 #define dbtok(x, b) \ 99 ((b) < (fsblkcnt64_t)1024 ? \ 100 (x) / ((fsblkcnt64_t)1024 / (b)) : (x) * ((b) / (fsblkcnt64_t)1024)) 101 102 int aflag = 0; /* even the uninteresting ones */ 103 int bflag = 0; /* print only number of kilobytes free */ 104 int eflag = 0; /* print only number of file entries free */ 105 int gflag = 0; /* print entire statvfs structure */ 106 int hflag = 0; /* don't print header */ 107 int iflag = 0; /* information for inodes */ 108 int nflag = 0; /* print VFStype name */ 109 int tflag = 0; /* print totals */ 110 int errflag = 0; 111 int errcode = 0; 112 char *typestr = "ufs"; 113 fsblkcnt64_t t_totalblks, t_avail, t_free, t_used, t_reserved; 114 int t_inodes, t_iused, t_ifree; 115 116 /* 117 * cached information recording previous chroot history. 118 */ 119 static char *chrootpath; 120 121 extern int optind; 122 extern char *optarg; 123 124 union { 125 struct fs iu_fs; 126 char dummy[SBSIZE]; 127 } sb; 128 #define sblock sb.iu_fs 129 130 /* 131 * This structure is used to chain mntent structures into a list 132 * and to cache stat information for each member of the list. 133 */ 134 struct mntlist { 135 struct mnttab *mntl_mnt; 136 struct mntlist *mntl_next; 137 dev_t mntl_dev; 138 int mntl_devvalid; 139 }; 140 141 char *subopts [] = { 142 #define A_FLAG 0 143 "a", 144 #define I_FLAG 1 145 "i", 146 NULL 147 }; 148 149 void 150 main(argc, argv) 151 int argc; 152 char *argv[]; 153 { 154 struct mnttab mnt; 155 int opt; 156 char *suboptions, *value; 157 158 (void) setlocale(LC_ALL, ""); 159 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 160 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 161 #endif 162 (void) textdomain(TEXT_DOMAIN); 163 164 while ((opt = getopt(argc, argv, "beghkno:t")) != EOF) { 165 switch (opt) { 166 167 case 'b': /* print only number of kilobytes free */ 168 bflag++; 169 break; 170 171 case 'e': 172 eflag++; /* print only number of file entries free */ 173 iflag++; 174 break; 175 176 case 'g': 177 gflag++; 178 break; 179 180 case 'n': 181 nflag++; 182 break; 183 184 case 'k': 185 break; 186 187 case 'h': 188 hflag++; 189 break; 190 191 case 'o': 192 /* 193 * ufs specific options. 194 */ 195 suboptions = optarg; 196 while (*suboptions != '\0') { 197 switch (getsubopt(&suboptions, 198 subopts, &value)) { 199 200 case I_FLAG: /* information for inodes */ 201 iflag++; 202 break; 203 204 default: 205 usage(); 206 } 207 } 208 break; 209 210 case 't': /* print totals */ 211 tflag++; 212 break; 213 214 case 'V': /* Print command line */ 215 { 216 char *opt_text; 217 int opt_count; 218 219 (void) fprintf(stdout, "df -F ufs "); 220 for (opt_count = 1; opt_count < argc; 221 opt_count++) { 222 opt_text = argv[opt_count]; 223 if (opt_text) 224 (void) fprintf(stdout, " %s ", 225 opt_text); 226 } 227 (void) fprintf(stdout, "\n"); 228 } 229 break; 230 231 case '?': 232 errflag++; 233 } 234 } 235 if (errflag) 236 usage(); 237 if (gflag && iflag) { 238 printf(gettext("df: '-g' and '-o i' are mutually exclusive\n")); 239 exit(1); 240 } 241 if (bflag || eflag) 242 tflag = 0; 243 244 /* 245 * Cache CHROOT information for later use; assume that $CHROOT matches 246 * the cumulative arguments given to chroot calls. 247 */ 248 chrootpath = getenv("CHROOT"); 249 if (chrootpath != NULL && strcmp(chrootpath, "/") == 0) 250 chrootpath = NULL; 251 252 if (argc <= optind) { 253 /* 254 * Take this path when "/usr/lib/fs/ufs/df" is specified, and 255 * there are no mountpoints specified. 256 * E.g., these command lines take us down this path 257 * /usr/lib/fs/ufs/df -o i 258 * /usr/lib/fs/ufs/df 259 */ 260 register FILE *mtabp; 261 262 if ((mtabp = fopen(MNTTAB, "r")) == NULL) { 263 (void) fprintf(stderr, "df: "); 264 perror(MNTTAB); 265 exit(1); 266 } 267 pheader(); 268 while (getmntent(mtabp, &mnt) == 0) { 269 if (strcmp(typestr, mnt.mnt_fstype) != 0) { 270 continue; 271 } 272 dfreemnt(mnt.mnt_mountp, &mnt); 273 } 274 if (tflag) 275 if (iflag) 276 print_itotals(); 277 else 278 print_totals(); 279 (void) fclose(mtabp); 280 } else { 281 int i; 282 struct mntlist *mntl; 283 struct stat64 *argstat; 284 char **devnames; 285 char *cp; 286 287 /* 288 * Obtain stat64 information for each argument before 289 * constructing the list of mounted file systems. This 290 * ordering forces the automounter to establish any 291 * mounts required to access the arguments, so that the 292 * corresponding mount table entries will exist when 293 * we look for them. 294 */ 295 argv++; 296 argc--; 297 argstat = (struct stat64 *)xmalloc(argc * sizeof (*argstat)); 298 devnames = (char **)xmalloc(argc * sizeof (char *)); 299 for (i = 0; i < argc; i++) { 300 301 /* 302 * Given a raw device name, get the block device name 303 */ 304 cp = getfullblkname(argv[i]); 305 if (cp == NULL || *cp == '\0') { 306 if (cp != NULL) 307 free(cp); 308 cp = strdup(argv[i]); 309 310 if (cp == NULL) { 311 int j; 312 313 fprintf(stderr, gettext( 314 "df: memory allocation failure\n")); 315 316 for (j = 0; j < i; j++) 317 free(devnames[j]); 318 free(devnames); 319 free(argstat); 320 exit(1); 321 } 322 } 323 if (stat64(cp, &argstat[i]) < 0) { 324 errcode = errno; 325 /* 326 * Mark as no longer interesting. 327 */ 328 argv[i] = NULL; 329 devnames[i] = NULL; 330 free(cp); 331 } else { 332 devnames[i] = cp; 333 } 334 } 335 336 pheader(); 337 aflag++; 338 /* 339 * Construct the list of mounted file systems. 340 */ 341 mntl = mkmntlist(); 342 343 /* 344 * Iterate through the argument list, reporting on each one. 345 */ 346 for (i = 0; i < argc; i++) { 347 register struct mntlist *mlp; 348 int isblk; 349 350 /* 351 * Skip if we've already determined that we can't 352 * process it. 353 */ 354 if (argv[i] == NULL) 355 continue; 356 357 /* 358 * If the argument names a device, report on the file 359 * system associated with the device rather than on 360 * the one containing the device's directory entry 361 */ 362 cp = devnames[i]; 363 if ((isblk = (argstat[i].st_mode&S_IFMT) == S_IFBLK) || 364 (argstat[i].st_mode & S_IFMT) == S_IFCHR) { 365 if (isblk && strcmp(mpath(cp), "") != 0) { 366 struct mnttab *mp = mdev(cp); 367 dfreemnt(mp->mnt_mountp, mp); 368 } else { 369 dfreedev(cp); 370 } 371 free(cp); 372 devnames[i] = NULL; 373 continue; 374 } 375 376 /* 377 * Get this argument's corresponding mount table 378 * entry. 379 */ 380 mlp = findmntent(cp, &argstat[i], mntl); 381 free(cp); 382 devnames[i] = NULL; 383 384 if (mlp == NULL) { 385 (void) fprintf(stderr, 386 gettext("Could not find mount point for %s\n"), 387 argv[i]); 388 continue; 389 } 390 391 dfreemnt(mlp->mntl_mnt->mnt_mountp, mlp->mntl_mnt); 392 } 393 free(devnames); 394 free(argstat); 395 } 396 exit(0); 397 /*NOTREACHED*/ 398 } 399 400 void 401 pheader() 402 { 403 if (hflag) 404 return; 405 if (nflag) 406 (void) printf(gettext("VFStype name - ufs\n")); 407 if (iflag) { 408 if (eflag) 409 /* 410 * TRANSLATION_NOTE 411 * Following string is used as a table header. 412 * Translated items should start at the same 413 * columns as the original items. 414 */ 415 (void) printf(gettext( 416 "Filesystem ifree\n")); 417 else { 418 /* 419 * TRANSLATION_NOTE 420 * Following string is used as a table header. 421 * Translated items should start at the same 422 * columns as the original items. 423 */ 424 (void) printf(gettext( 425 "Filesystem iused ifree %%iused Mounted on\n")); 426 } 427 } else { 428 if (gflag) 429 /* 430 * TRANSLATION_NOTE 431 * Following string is used as a table header. 432 * Translated items should start at the same 433 * columns as the original items. 434 */ 435 (void) printf(gettext( 436 "Filesystem f_type f_fsize f_bfree f_bavail f_files f_ffree " 437 "f_fsid f_flag f_fstr\n")); 438 else 439 if (bflag) 440 /* 441 * TRANSLATION_NOTE 442 * Following string is used as a table header. 443 * Translated items should start at the same 444 * columns as the original items. 445 */ 446 (void) printf(gettext( 447 "Filesystem avail\n")); 448 else { 449 /* 450 * TRANSLATION_NOTE 451 * Following string is used as a table header. 452 * Translated items should start at the same 453 * columns as the original items. 454 */ 455 (void) printf(gettext( 456 "Filesystem kbytes used avail capacity Mounted on\n")); 457 } 458 } 459 } 460 461 /* 462 * Report on a block or character special device. Assumed not to be 463 * mounted. N.B. checks for a valid UFS superblock. 464 */ 465 void 466 dfreedev(file) 467 char *file; 468 { 469 fsblkcnt64_t totalblks, availblks, avail, free, used; 470 int fi; 471 472 fi = open64(file, 0); 473 if (fi < 0) { 474 (void) fprintf(stderr, "df: "); 475 perror(file); 476 return; 477 } 478 if (bread(file, fi, SBLOCK, (char *)&sblock, SBSIZE) == 0) { 479 (void) close(fi); 480 return; 481 } 482 if ((sblock.fs_magic != FS_MAGIC) && 483 (sblock.fs_magic != MTB_UFS_MAGIC)) { 484 (void) fprintf(stderr, gettext( 485 "df: %s: not a ufs file system\n"), 486 file); 487 (void) close(fi); 488 return; 489 } 490 if (sblock.fs_magic == FS_MAGIC && 491 (sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 && 492 sblock.fs_version != UFS_VERSION_MIN)) { 493 (void) fprintf(stderr, gettext( 494 "df: %s: unrecognized version of UFS: %d\n"), 495 file, sblock.fs_version); 496 (void) close(fi); 497 return; 498 } 499 if (sblock.fs_magic == MTB_UFS_MAGIC && 500 (sblock.fs_version > MTB_UFS_VERSION_1 || 501 sblock.fs_version < MTB_UFS_VERSION_MIN)) { 502 (void) fprintf(stderr, gettext( 503 "df: %s: unrecognized version of UFS: %d\n"), 504 file, sblock.fs_version); 505 (void) close(fi); 506 return; 507 } 508 (void) printf("%-20.20s", file); 509 if (iflag) { 510 if (eflag) { 511 (void) printf("%8ld", sblock.fs_cstotal.cs_nifree); 512 } else { 513 show_inode_usage( 514 (fsfilcnt64_t)sblock.fs_ncg * (fsfilcnt64_t)sblock.fs_ipg, 515 (fsfilcnt64_t)sblock.fs_cstotal.cs_nifree); 516 } 517 } else { 518 totalblks = (fsblkcnt64_t)sblock.fs_dsize; 519 free = 520 (fsblkcnt64_t)sblock.fs_cstotal.cs_nbfree * 521 (fsblkcnt64_t)sblock.fs_frag + 522 (fsblkcnt64_t)sblock.fs_cstotal.cs_nffree; 523 used = totalblks - free; 524 availblks = totalblks / (fsblkcnt64_t)100 * 525 ((fsblkcnt64_t)100 - (fsblkcnt64_t)sblock.fs_minfree); 526 avail = availblks > used ? availblks - used : (fsblkcnt64_t)0; 527 if (bflag) { 528 (void) printf("%8lld\n", dbtok(avail, 529 (fsblkcnt64_t)sblock.fs_fsize)); 530 } else { 531 (void) printf(" %7lld %7lld %7lld", 532 dbtok(totalblks, (fsblkcnt64_t)sblock.fs_fsize), 533 dbtok(used, (fsblkcnt64_t)sblock.fs_fsize), 534 dbtok(avail, (fsblkcnt64_t)sblock.fs_fsize)); 535 (void) printf("%6.0f%%", 536 availblks == 0 ? 0.0 : 537 (double)used / (double)availblks * 100.0); 538 (void) printf(" "); 539 } 540 if (tflag) { 541 t_totalblks += dbtok(totalblks, 542 (fsblkcnt64_t)sblock.fs_fsize); 543 t_used += dbtok(used, (fsblkcnt64_t)sblock.fs_fsize); 544 t_avail += dbtok(avail, (fsblkcnt64_t)sblock.fs_fsize); 545 t_free += free; 546 } 547 } 548 if ((!bflag) && (!eflag)) 549 (void) printf(" %s\n", mpath(file)); 550 else if (eflag) 551 (void) printf("\n"); 552 (void) close(fi); 553 } 554 555 void 556 dfreemnt(file, mnt) 557 char *file; 558 struct mnttab *mnt; 559 { 560 struct statvfs64 fs; 561 562 if (statvfs64(file, &fs) < 0 && 563 chroot_stat(file, statvfs64, (char *)&fs, &file) < 0) { 564 (void) fprintf(stderr, "df: "); 565 perror(file); 566 return; 567 } 568 569 if (!aflag && fs.f_blocks == 0) { 570 return; 571 } 572 if (!isatty(fileno(stdout))) { 573 (void) printf("%s", mnt->mnt_special); 574 } else { 575 if (strlen(mnt->mnt_special) > (size_t)20) { 576 (void) printf("%s\n", mnt->mnt_special); 577 (void) printf(" "); 578 } else { 579 (void) printf("%-20.20s", mnt->mnt_special); 580 } 581 } 582 if (iflag) { 583 if (eflag) { 584 (void) printf("%8lld", fs.f_ffree); 585 } else { 586 show_inode_usage(fs.f_files, fs.f_ffree); 587 } 588 } else { 589 if (gflag) { 590 print_statvfs(&fs); 591 } else { 592 fsblkcnt64_t totalblks, avail, free, used, reserved; 593 594 totalblks = fs.f_blocks; 595 free = fs.f_bfree; 596 used = totalblks - free; 597 avail = fs.f_bavail; 598 reserved = free - avail; 599 if ((long long)avail < 0) 600 avail = 0; 601 if (bflag) { 602 (void) printf("%8lld\n", dbtok(avail, 603 (fsblkcnt64_t)fs.f_frsize)); 604 } else { 605 (void) printf(" %7lld %7lld %7lld", 606 dbtok(totalblks, 607 (fsblkcnt64_t)fs.f_frsize), 608 dbtok(used, (fsblkcnt64_t)fs.f_frsize), 609 dbtok(avail, (fsblkcnt64_t)fs.f_frsize)); 610 totalblks -= reserved; 611 (void) printf("%6.0f%%", 612 totalblks == 0 ? 0.0 : 613 (double)used / (double)totalblks * 100.0); 614 (void) printf(" "); 615 if (tflag) { 616 t_totalblks += dbtok(totalblks + reserved, 617 (fsblkcnt64_t)fs.f_bsize); 618 t_reserved += reserved; 619 t_used += dbtok(used, 620 (fsblkcnt64_t)fs.f_frsize); 621 t_avail += dbtok(avail, 622 (fsblkcnt64_t)fs.f_frsize); 623 t_free += free; 624 } 625 } 626 } 627 } 628 if ((!bflag) && (!eflag) && (!gflag)) 629 (void) printf(" %s\n", mnt->mnt_mountp); 630 else if (eflag) 631 (void) printf("\n"); 632 } 633 634 static void 635 show_inode_usage(fsfilcnt64_t total, fsfilcnt64_t free) 636 { 637 fsfilcnt64_t used = total - free; 638 int missing_info = ((long long)total == (long long)-1 || 639 (long long)free == (long long)-1); 640 641 if (missing_info) 642 (void) printf("%8s", "*"); 643 else 644 (void) printf("%8lld", used); 645 if ((long long)free == (long long)-1) 646 (void) printf("%8s", "*"); 647 else 648 (void) printf(" %7lld", free); 649 if (missing_info) 650 (void) printf("%6s ", "*"); 651 else 652 (void) printf("%6.0f%% ", (double)used / (double)total * 100.0); 653 } 654 655 /* 656 * Return the suffix of path obtained by stripping off the prefix 657 * that is the value of the CHROOT environment variable. If this 658 * value isn't obtainable or if it's not a prefix of path, return NULL. 659 */ 660 static char * 661 zap_chroot(path) 662 char *path; 663 { 664 return (pathsuffix(path, chrootpath)); 665 } 666 667 /* 668 * Stat/statfs a file after stripping off leading directory to which we are 669 * chroot'd. Used to find the TFS mount that applies to the current 670 * activated NSE environment. 671 */ 672 static int 673 chroot_stat(dir, statfunc, statp, dirp) 674 char *dir; 675 int (*statfunc)(); 676 char *statp; 677 char **dirp; 678 { 679 if ((dir = zap_chroot(dir)) == NULL) 680 return (-1); 681 if (dirp) 682 *dirp = dir; 683 return (*statfunc)(dir, statp); 684 } 685 686 /* 687 * Given a name like /dev/dsk/c1d0s2, returns the mounted path, like /usr. 688 */ 689 char * 690 mpath(char *file) 691 { 692 struct mnttab mnt; 693 FILE *mnttab; 694 struct stat64 device_stat, mount_stat; 695 char *mname; 696 697 mnttab = fopen(MNTTAB, "r"); 698 if (mnttab == NULL) { 699 return (""); 700 } 701 mname = ""; 702 while ((getmntent(mnttab, &mnt)) == 0) { 703 if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0) { 704 continue; 705 } 706 if (strcmp(file, mnt.mnt_special) == 0) { 707 if (stat64(mnt.mnt_mountp, &mount_stat) != 0) 708 continue; 709 if (stat64(mnt.mnt_special, &device_stat) != 0) 710 continue; 711 712 if (device_stat.st_rdev == mount_stat.st_dev) { 713 mname = mnt.mnt_mountp; 714 break; 715 } 716 } 717 } 718 fclose(mnttab); 719 return (mname); 720 } 721 722 /* 723 * Given a special device, return mnttab entry 724 */ 725 726 struct mnttab * 727 mdev(char *spec) 728 { 729 FILE *mntp; 730 struct mnttab mnt; 731 732 if ((mntp = fopen(MNTTAB, "r")) == 0) { 733 (void) fprintf(stderr, "df: "); 734 perror(MNTTAB); 735 exit(1); 736 } 737 738 while (getmntent(mntp, &mnt) == 0) { 739 if (strcmp(spec, mnt.mnt_special) == 0) { 740 (void) fclose(mntp); 741 return (mntdup(&mnt)); 742 } 743 } 744 (void) fclose(mntp); 745 (void) fprintf(stderr, "df : couldn't find mnttab entry for %s", spec); 746 exit(1); 747 } 748 749 /* 750 * Find the entry in mlist that corresponds to the file named by path 751 * (i.e., that names a mount table entry for the file system in which 752 * path lies). The pstat argument must point to stat information for 753 * path. 754 * 755 * Return the entry or NULL if there's no match. 756 * 757 * As it becomes necessary to obtain stat information about previously 758 * unexamined mlist entries, gather the information and cache it with the 759 * entries. 760 * 761 * The routine's strategy is to convert path into its canonical, symlink-free 762 * representation canon (which will require accessing the file systems on the 763 * branch from the root to path and thus may cause the routine to hang if any 764 * of them are inaccessible) and to use it to search for a mount point whose 765 * name is a substring of canon and whose corresponding device matches that of 766 * canon. This technique avoids accessing unnecessary file system resources 767 * and thus prevents the program from hanging on inaccessible resources unless 768 * those resources are necessary for accessing path. 769 */ 770 static struct mntlist * 771 findmntent(path, pstat, mlist) 772 char *path; 773 struct stat64 *pstat; 774 struct mntlist *mlist; 775 { 776 static char cwd[MAXPATHLEN]; 777 char canon[MAXPATHLEN]; 778 char scratch[MAXPATHLEN]; 779 register struct mntlist *mlp; 780 781 /* 782 * If path is relative and we haven't already determined the current 783 * working directory, do so now. Calculating the working directory 784 * here lets us do the work once, instead of (potentially) repeatedly 785 * in realpath(). 786 */ 787 if (*path != '/' && cwd[0] == '\0') { 788 if (getcwd(cwd, MAXPATHLEN) == NULL) { 789 cwd[0] = '\0'; 790 return (NULL); 791 } 792 } 793 794 /* 795 * Find an absolute pathname in the native file system name space that 796 * corresponds to path, stuffing it into canon. 797 * 798 * If CHROOT is set in the environment, assume that chroot($CHROOT) 799 * (or an equivalent series of calls) was executed and convert the 800 * path to the equivalent name in the native file system's name space. 801 * Doing so allows direct comparison with the names in mtab entires, 802 * which are assumed to be recorded relative to the native name space. 803 */ 804 if (abspath(cwd, path, scratch) < 0) 805 return (NULL); 806 if (strcmp(scratch, "/") == 0 && chrootpath != NULL) { 807 /* 808 * Force canon to be in canonical form; if the result from 809 * abspath was "/" and chrootpath isn't the null string, we 810 * must strip off a trailing slash. 811 */ 812 scratch[0] = '\0'; 813 } 814 (void) sprintf(canon, "%s%s", chrootpath ? chrootpath : "", scratch); 815 816 again: 817 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 818 struct mnttab *mnt = mlp->mntl_mnt; 819 820 /* 821 * Ignore uninteresting mounts. 822 */ 823 if (strcmp(mnt->mnt_fstype, typestr) != 0) 824 continue; 825 826 /* 827 * The mount entry covers some prefix of the file. 828 * See whether it's the entry for the file system 829 * containing the file by comparing device ids. 830 */ 831 if (mlp->mntl_dev == NODEV) { 832 struct stat64 fs_sb; 833 834 if (stat64(mnt->mnt_mountp, &fs_sb) < 0 && 835 chroot_stat(mnt->mnt_mountp, stat64, (char *)&fs_sb, 836 (char **)NULL) < 0) { 837 continue; 838 } 839 mlp->mntl_dev = fs_sb.st_dev; 840 } 841 842 if (pstat->st_dev == mlp->mntl_dev) 843 return (mlp); 844 } 845 846 return (NULL); 847 } 848 849 /* 850 * Convert the path given in raw to canonical, absolute, symlink-free 851 * form, storing the result in the buffer named by canon, which must be 852 * at least MAXPATHLEN bytes long. "wd" contains the current working 853 * directory; accepting this value as an argument lets our caller cache 854 * the value, so that realpath (called from this routine) doesn't have 855 * to recalculate it each time it's given a relative pathname. 856 * 857 * Return 0 on success, -1 on failure. 858 */ 859 static int 860 abspath(wd, raw, canon) 861 char *wd; 862 register char *raw; 863 char *canon; 864 { 865 char absbuf[MAXPATHLEN]; 866 867 /* 868 * Preliminary sanity check. 869 */ 870 if (wd == NULL || raw == NULL || canon == NULL) 871 return (-1); 872 873 /* 874 * If the path is relative, convert it to absolute form, 875 * using wd if it's been supplied. 876 */ 877 if (raw[0] != '/') { 878 register char *limit = absbuf + sizeof (absbuf); 879 register char *d; 880 881 /* Fill in working directory. */ 882 if (strlcpy(absbuf, wd, sizeof (absbuf)) >= sizeof (absbuf)) 883 return (-1); 884 885 /* Add separating slash. */ 886 d = absbuf + strlen(absbuf); 887 if (d < limit) 888 *d++ = '/'; 889 890 /* Glue on the relative part of the path. */ 891 while (d < limit && (*d++ = *raw++)) 892 continue; 893 894 raw = absbuf; 895 } 896 897 /* 898 * Call realpath to canonicalize and resolve symlinks. 899 */ 900 return (realpath(raw, canon) == NULL ? -1 : 0); 901 } 902 903 /* 904 * Return a pointer to the trailing suffix of full that follows the prefix 905 * given by pref. If pref isn't a prefix of full, return NULL. Apply 906 * pathname semantics to the prefix test, so that pref must match at a 907 * component boundary. 908 */ 909 static char * 910 pathsuffix(full, pref) 911 register char *full; 912 register char *pref; 913 { 914 register int preflen; 915 916 if (full == NULL || pref == NULL) 917 return (NULL); 918 919 preflen = strlen(pref); 920 if (strncmp(pref, full, preflen) != 0) 921 return (NULL); 922 923 /* 924 * pref is a substring of full. To be a subpath, it cannot cover a 925 * partial component of full. The last clause of the test handles the 926 * special case of the root. 927 */ 928 if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1) 929 return (NULL); 930 931 if (preflen == 1 && full[0] == '/') 932 return (full); 933 else 934 return (full + preflen); 935 } 936 937 /* 938 * Return zero iff the path named by sub is a leading subpath 939 * of the path named by full. 940 * 941 * Treat null paths as matching nothing. 942 */ 943 static int 944 subpath(full, sub) 945 register char *full; 946 register char *sub; 947 { 948 return (pathsuffix(full, sub) == NULL); 949 } 950 951 offset_t llseek(); 952 953 int 954 bread(file, fi, bno, buf, cnt) 955 char *file; 956 int fi; 957 daddr_t bno; 958 char *buf; 959 int cnt; 960 { 961 register int n; 962 963 (void) llseek(fi, (offset_t)bno * DEV_BSIZE, 0); 964 if ((n = read(fi, buf, cnt)) < 0) { 965 /* probably a dismounted disk if errno == EIO */ 966 if (errno != EIO) { 967 (void) fprintf(stderr, gettext("df: read error on ")); 968 perror(file); 969 (void) fprintf(stderr, "bno = %ld\n", bno); 970 } else { 971 (void) fprintf(stderr, gettext( 972 "df: premature EOF on %s\n"), file); 973 (void) fprintf(stderr, 974 "bno = %ld expected = %d count = %d\n", bno, cnt, n); 975 } 976 return (0); 977 } 978 return (1); 979 } 980 981 char * 982 xmalloc(size) 983 unsigned int size; 984 { 985 register char *ret; 986 char *malloc(); 987 988 if ((ret = (char *)malloc(size)) == NULL) { 989 (void) fprintf(stderr, gettext("umount: ran out of memory!\n")); 990 exit(1); 991 } 992 return (ret); 993 } 994 995 struct mnttab * 996 mntdup(mnt) 997 register struct mnttab *mnt; 998 { 999 register struct mnttab *new; 1000 1001 new = (struct mnttab *)xmalloc(sizeof (*new)); 1002 1003 new->mnt_special = 1004 (char *)xmalloc((unsigned)(strlen(mnt->mnt_special) + 1)); 1005 (void) strcpy(new->mnt_special, mnt->mnt_special); 1006 1007 new->mnt_mountp = 1008 (char *)xmalloc((unsigned)(strlen(mnt->mnt_mountp) + 1)); 1009 (void) strcpy(new->mnt_mountp, mnt->mnt_mountp); 1010 1011 new->mnt_fstype = 1012 (char *)xmalloc((unsigned)(strlen(mnt->mnt_fstype) + 1)); 1013 (void) strcpy(new->mnt_fstype, mnt->mnt_fstype); 1014 1015 if (mnt->mnt_mntopts != NULL) { 1016 new->mnt_mntopts = 1017 (char *)xmalloc((unsigned)(strlen(mnt->mnt_mntopts) + 1)); 1018 (void) strcpy(new->mnt_mntopts, mnt->mnt_mntopts); 1019 } else { 1020 new->mnt_mntopts = NULL; 1021 } 1022 1023 #ifdef never 1024 new->mnt_freq = mnt->mnt_freq; 1025 new->mnt_passno = mnt->mnt_passno; 1026 #endif /* never */ 1027 1028 return (new); 1029 } 1030 1031 void 1032 usage() 1033 { 1034 1035 (void) fprintf(stderr, gettext( 1036 "ufs usage: df [generic options] [-o i] [directory | special]\n")); 1037 exit(1); 1038 } 1039 1040 struct mntlist * 1041 mkmntlist() 1042 { 1043 FILE *mounted; 1044 struct mntlist *mntl; 1045 struct mntlist *mntst = NULL; 1046 struct extmnttab mnt; 1047 1048 if ((mounted = fopen(MNTTAB, "r")) == NULL) { 1049 (void) fprintf(stderr, "df : "); 1050 perror(MNTTAB); 1051 exit(1); 1052 } 1053 resetmnttab(mounted); 1054 while (getextmntent(mounted, &mnt, sizeof (struct extmnttab)) == NULL) { 1055 mntl = (struct mntlist *)xmalloc(sizeof (*mntl)); 1056 mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt)); 1057 mntl->mntl_next = mntst; 1058 mntl->mntl_devvalid = 1; 1059 mntl->mntl_dev = makedev(mnt.mnt_major, mnt.mnt_minor); 1060 mntst = mntl; 1061 } 1062 (void) fclose(mounted); 1063 return (mntst); 1064 } 1065 1066 void 1067 print_statvfs(fs) 1068 struct statvfs64 *fs; 1069 { 1070 int i; 1071 1072 for (i = 0; i < FSTYPSZ; i++) 1073 (void) printf("%c", fs->f_basetype[i]); 1074 (void) printf(" %7d %7lld %7lld", 1075 fs->f_frsize, 1076 fs->f_blocks, 1077 fs->f_bavail); 1078 (void) printf(" %7lld %7lld %7d", 1079 fs->f_files, 1080 fs->f_ffree, 1081 fs->f_fsid); 1082 (void) printf(" 0x%x ", 1083 fs->f_flag); 1084 for (i = 0; i < 14; i++) 1085 (void) printf("%c", 1086 (fs->f_fstr[i] == '\0') ? ' ' : fs->f_fstr[i]); 1087 printf("\n"); 1088 } 1089 1090 void 1091 print_totals() 1092 { 1093 /* 1094 * TRANSLATION_NOTE 1095 * Following string is used as a table header. 1096 * Translated items should start at the same 1097 * columns as the original items. 1098 */ 1099 (void) printf(gettext("Totals %8lld %7lld %7lld"), 1100 t_totalblks, t_used, t_avail); 1101 (void) printf("%6.0f%%\n", 1102 (t_totalblks - t_reserved) == (fsblkcnt64_t)0 ? 1103 0.0 : 1104 (double)t_used / (double)(t_totalblks - t_reserved) * 100.0); 1105 } 1106 1107 void 1108 print_itotals() 1109 { 1110 /* 1111 * TRANSLATION_NOTE 1112 * Following string is used as a table header. 1113 * Translated items should start at the same 1114 * columns as the original items. 1115 */ 1116 (void) printf(gettext("Totals %8d %7d%6.0f%%\n"), 1117 t_iused, 1118 t_ifree, 1119 t_inodes == 0 ? 0.0 : (double)t_iused / (double)t_inodes * 100.0); 1120 } 1121