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