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 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #include <stdio.h> 35 #include <stdarg.h> 36 #include <string.h> 37 #include <locale.h> 38 #include <libintl.h> 39 #include <stdlib.h> 40 #include <ftw.h> 41 #include <errno.h> 42 #include <sys/types.h> 43 #include <unistd.h> 44 #include <sys/statvfs.h> 45 #include <sys/stat.h> 46 #include <sys/param.h> 47 #include <sys/mnttab.h> 48 #include <sys/mntent.h> 49 #include <sys/vfstab.h> 50 #include <sys/wait.h> 51 #include <sys/mkdev.h> 52 #include <sys/int_limits.h> 53 #include <sys/zone.h> 54 55 #include "fslib.h" 56 57 extern char *default_fstype(char *); 58 59 /* 60 * General notice: 61 * String pointers in this code may point to statically allocated memory 62 * or dynamically allocated memory. Furthermore, a dynamically allocated 63 * string may be pointed to by more than one pointer. This does not pose 64 * a problem because malloc'ed memory is never free'd (so we don't need 65 * to remember which pointers point to malloc'ed memory). 66 */ 67 68 /* 69 * TRANSLATION_NOTE 70 * Only strings passed as arguments to the TRANSLATE macro need to 71 * be translated. 72 */ 73 74 #ifndef MNTTYPE_LOFS 75 #define MNTTYPE_LOFS "lofs" 76 #endif 77 78 #define EQ(s1, s2) (strcmp(s1, s2) == 0) 79 #define NEW(type) xmalloc(sizeof (type)) 80 #define CLEAR(var) (void) memset(&(var), 0, sizeof (var)) 81 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 82 #define MAX3(a, b, c) MAX(a, MAX(b, c)) 83 #define TRANSLATE(s) new_string(gettext(s)) 84 85 #define MAX_OPTIONS 36 86 #define N_FSTYPES 20 87 #define MOUNT_TABLE_ENTRIES 40 /* initial allocation */ 88 #define MSGBUF_SIZE 1024 89 #define LINEBUF_SIZE 256 /* either input or output lines */ 90 91 #define BLOCK_SIZE 512 /* when reporting in terms of blocks */ 92 93 #define DEVNM_CMD "devnm" 94 #define FS_LIBPATH "/usr/lib/fs/" 95 #define MOUNT_TAB "/etc/mnttab" 96 #define VFS_TAB "/etc/vfstab" 97 #define REMOTE_FS "/etc/dfs/fstypes" 98 99 #define NUL '\0' 100 #define FALSE 0 101 #define TRUE 1 102 103 /* 104 * Formatting constants 105 */ 106 #define IBCS2_FILESYSTEM_WIDTH 15 /* Truncate to match ISC/SCO */ 107 #define IBCS2_MOUNT_POINT_WIDTH 10 /* Truncate to match ISC/SCO */ 108 #define FILESYSTEM_WIDTH 20 109 #define MOUNT_POINT_WIDTH 19 110 #define SPECIAL_DEVICE_WIDTH 18 111 #define FSTYPE_WIDTH 8 112 #define BLOCK_WIDTH 8 113 #define NFILES_WIDTH 8 114 #ifdef XPG4 115 #define KBYTE_WIDTH 11 116 #define AVAILABLE_WIDTH 10 117 #else 118 #define KBYTE_WIDTH 7 119 #define AVAILABLE_WIDTH 6 120 #endif 121 #define SCALED_WIDTH 6 122 #define CAPACITY_WIDTH 9 123 #define BSIZE_WIDTH 6 124 #define FRAGSIZE_WIDTH 7 125 #define FSID_WIDTH 7 126 #define FLAG_WIDTH 8 127 #define NAMELEN_WIDTH 7 128 #define MNT_SPEC_WIDTH MOUNT_POINT_WIDTH + SPECIAL_DEVICE_WIDTH + 2 129 130 /* 131 * Flags for the errmsg() function 132 */ 133 #define ERR_NOFLAGS 0x0 134 #define ERR_NONAME 0x1 /* don't include the program name */ 135 /* as a prefix */ 136 #define ERR_FATAL 0x2 /* call exit after printing the */ 137 /* message */ 138 #define ERR_PERROR 0x4 /* append an errno explanation to */ 139 /* the message */ 140 #define ERR_USAGE 0x8 /* print the usage line after the */ 141 /* message */ 142 143 #define NUMBER_WIDTH 40 144 145 /* 146 * A numbuf_t is used when converting a number to a string representation 147 */ 148 typedef char numbuf_t[ NUMBER_WIDTH ]; 149 150 /* 151 * We use bool_int instead of int to make clear which variables are 152 * supposed to be boolean 153 */ 154 typedef int bool_int; 155 156 struct mtab_entry { 157 bool_int mte_dev_is_valid; 158 dev_t mte_dev; 159 bool_int mte_ignore; /* the "ignore" option was set */ 160 struct extmnttab *mte_mount; 161 }; 162 163 164 struct df_request { 165 bool_int dfr_valid; 166 char *dfr_cmd_arg; /* what the user specified */ 167 struct mtab_entry *dfr_mte; 168 char *dfr_fstype; 169 int dfr_index; /* to make qsort stable */ 170 }; 171 172 #define DFR_MOUNT_POINT(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_mountp 173 #define DFR_SPECIAL(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_special 174 #define DFR_ISMOUNTEDFS(dfrp) ((dfrp)->dfr_mte != NULL) 175 176 #define DFRP(p) ((struct df_request *)(p)) 177 178 typedef void (*output_func)(struct df_request *, struct statvfs64 *); 179 180 struct df_output { 181 output_func dfo_func; /* function that will do the output */ 182 int dfo_flags; 183 }; 184 185 /* 186 * Output flags 187 */ 188 #define DFO_NOFLAGS 0x0 189 #define DFO_HEADER 0x1 /* output preceded by header */ 190 #define DFO_STATVFS 0x2 /* must do a statvfs64(2) */ 191 192 193 static char *program_name; 194 static char df_options[MAX_OPTIONS] = "-"; 195 static size_t df_options_len = 1; 196 static char *o_option_arg; /* arg to the -o option */ 197 static char *FSType; 198 static char *remote_fstypes[N_FSTYPES+1]; /* allocate an extra one */ 199 /* to use as a terminator */ 200 201 /* 202 * The following three variables support an in-memory copy of the mount table 203 * to speedup searches. 204 */ 205 static struct mtab_entry *mount_table; /* array of mtab_entry's */ 206 static size_t mount_table_entries; 207 static size_t mount_table_allocated_entries; 208 209 static bool_int F_option; 210 static bool_int V_option; 211 static bool_int P_option; /* Added for XCU4 compliance */ 212 static bool_int Z_option; 213 static bool_int v_option; 214 #ifdef _iBCS2 215 char *sysv3_set; 216 #endif /* _iBCS2 */ 217 static bool_int a_option; 218 static bool_int b_option; 219 static bool_int e_option; 220 static bool_int g_option; 221 static bool_int h_option; 222 static bool_int k_option; 223 static bool_int l_option; 224 static bool_int n_option; 225 static bool_int t_option; 226 static bool_int o_option; 227 228 static bool_int tty_output; 229 static bool_int use_scaling; 230 static int scale; 231 232 static void usage(void); 233 static void do_devnm(int, char **); 234 static void do_df(int, char **); 235 static void parse_options(int, char **); 236 static char *basename(char *); 237 238 239 void 240 main(int argc, char *argv[]) 241 { 242 (void) setlocale(LC_ALL, ""); 243 244 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 245 #define TEXT_DOMAIN "SYS_TEST" 246 #endif 247 (void) textdomain(TEXT_DOMAIN); 248 249 program_name = basename(argv[0]); 250 251 #ifdef _iBCS2 252 sysv3_set = getenv("SYSV3"); 253 #endif /* _iBCS2 */ 254 255 if (EQ(program_name, DEVNM_CMD)) 256 do_devnm(argc, argv); 257 258 parse_options(argc, argv); 259 260 /* 261 * The k_option implies SunOS 4.x compatibility: when the special 262 * device name is too long the line will be split except when the 263 * output has been redirected. 264 * This is also valid for the -h option. 265 */ 266 267 if (use_scaling || k_option || P_option || v_option) 268 tty_output = isatty(1); 269 270 do_df(argc - optind, &argv[optind]); 271 /* NOTREACHED */ 272 } 273 274 275 /* 276 * Prints an error message to stderr. 277 */ 278 /* VARARGS2 */ 279 static void 280 errmsg(int flags, char *fmt, ...) 281 { 282 char buf[MSGBUF_SIZE]; 283 va_list ap; 284 int cc; 285 int offset; 286 287 if (flags & ERR_NONAME) 288 offset = 0; 289 else 290 offset = sprintf(buf, "%s: ", program_name); 291 292 va_start(ap, fmt); 293 cc = vsprintf(&buf[offset], gettext(fmt), ap); 294 offset += cc; 295 va_end(ap); 296 297 if (flags & ERR_PERROR) { 298 if (buf[offset-1] != ' ') 299 (void) strcat(buf, " "); 300 (void) strcat(buf, strerror(errno)); 301 } 302 (void) fprintf(stderr, "%s\n", buf); 303 if (flags & ERR_USAGE) 304 usage(); 305 if (flags & ERR_FATAL) 306 exit(1); 307 } 308 309 310 static void 311 usage() 312 { 313 #ifdef XPG4 314 errmsg(ERR_NONAME, 315 "Usage: %s [-F FSType] [-abeghklntPVZ] [-o FSType-specific_options]" 316 " [directory | block_device | resource]", program_name); 317 #else 318 errmsg(ERR_NONAME, 319 "Usage: %s [-F FSType] [-abeghklntVvZ] [-o FSType-specific_options]" 320 " [directory | block_device | resource]", program_name); 321 #endif 322 exit(1); 323 /* NOTREACHED */ 324 } 325 326 327 static char * 328 new_string(char *s) 329 { 330 char *p = NULL; 331 332 if (s) { 333 p = strdup(s); 334 if (p) 335 return (p); 336 errmsg(ERR_FATAL, "out of memory"); 337 /* NOTREACHED */ 338 } 339 return (p); 340 } 341 342 343 /* 344 * Allocate memory using malloc but terminate if the allocation fails 345 */ 346 static void * 347 xmalloc(size_t size) 348 { 349 void *p = malloc(size); 350 351 if (p) 352 return (p); 353 errmsg(ERR_FATAL, "out of memory"); 354 /* NOTREACHED */ 355 } 356 357 358 /* 359 * Allocate memory using realloc but terminate if the allocation fails 360 */ 361 static void * 362 xrealloc(void *ptr, size_t size) 363 { 364 void *p = realloc(ptr, size); 365 366 if (p) 367 return (p); 368 errmsg(ERR_FATAL, "out of memory"); 369 /* NOTREACHED */ 370 } 371 372 373 /* 374 * fopen the specified file for reading but terminate if the fopen fails 375 */ 376 static FILE * 377 xfopen(char *file) 378 { 379 FILE *fp = fopen(file, "r"); 380 381 if (fp == NULL) 382 errmsg(ERR_FATAL + ERR_PERROR, "failed to open %s:", file); 383 return (fp); 384 } 385 386 387 /* 388 * Read remote file system types from REMOTE_FS into the 389 * remote_fstypes array. 390 */ 391 static void 392 init_remote_fs() 393 { 394 FILE *fp; 395 char line_buf[LINEBUF_SIZE]; 396 size_t fstype_index = 0; 397 398 if ((fp = fopen(REMOTE_FS, "r")) == NULL) { 399 errmsg(ERR_NOFLAGS, 400 "Warning: can't open %s, ignored", REMOTE_FS); 401 return; 402 } 403 404 while (fgets(line_buf, sizeof (line_buf), fp) != NULL) { 405 char buf[LINEBUF_SIZE]; 406 407 (void) sscanf(line_buf, "%s", buf); 408 remote_fstypes[fstype_index++] = new_string(buf); 409 410 if (fstype_index == N_FSTYPES) 411 break; 412 } 413 (void) fclose(fp); 414 } 415 416 417 /* 418 * Returns TRUE if fstype is a remote file system type; 419 * otherwise, returns FALSE. 420 */ 421 static int 422 is_remote_fs(char *fstype) 423 { 424 char **p; 425 static bool_int remote_fs_initialized; 426 427 if (! remote_fs_initialized) { 428 init_remote_fs(); 429 remote_fs_initialized = TRUE; 430 } 431 432 for (p = remote_fstypes; *p; p++) 433 if (EQ(fstype, *p)) 434 return (TRUE); 435 return (FALSE); 436 } 437 438 439 static char * 440 basename(char *s) 441 { 442 char *p = strrchr(s, '/'); 443 444 return (p ? p+1 : s); 445 } 446 447 448 /* 449 * Create a new "struct extmnttab" and make sure that its fields point 450 * to malloc'ed memory 451 */ 452 static struct extmnttab * 453 mntdup(struct extmnttab *old) 454 { 455 struct extmnttab *new = NEW(struct extmnttab); 456 457 new->mnt_special = new_string(old->mnt_special); 458 new->mnt_mountp = new_string(old->mnt_mountp); 459 new->mnt_fstype = new_string(old->mnt_fstype); 460 new->mnt_mntopts = new_string(old->mnt_mntopts); 461 new->mnt_time = new_string(old->mnt_time); 462 new->mnt_major = old->mnt_major; 463 new->mnt_minor = old->mnt_minor; 464 return (new); 465 } 466 467 468 static void 469 mtab_error(char *mtab_file, int status) 470 { 471 if (status == MNT_TOOLONG) 472 errmsg(ERR_NOFLAGS, "a line in %s exceeds %d characters", 473 mtab_file, MNT_LINE_MAX); 474 else if (status == MNT_TOOMANY) 475 errmsg(ERR_NOFLAGS, 476 "a line in %s has too many fields", mtab_file); 477 else if (status == MNT_TOOFEW) 478 errmsg(ERR_NOFLAGS, 479 "a line in %s has too few fields", mtab_file); 480 else 481 errmsg(ERR_NOFLAGS, 482 "error while reading %s: %d", mtab_file, status); 483 exit(1); 484 /* NOTREACHED */ 485 } 486 487 488 /* 489 * Read the mount table from the specified file. 490 * We keep the table in memory for faster lookups. 491 */ 492 static void 493 mtab_read_file() 494 { 495 char *mtab_file = MOUNT_TAB; 496 FILE *fp; 497 struct extmnttab mtab; 498 int status; 499 500 fp = xfopen(mtab_file); 501 502 resetmnttab(fp); 503 mount_table_allocated_entries = MOUNT_TABLE_ENTRIES; 504 mount_table_entries = 0; 505 mount_table = xmalloc( 506 mount_table_allocated_entries * sizeof (struct mtab_entry)); 507 508 while ((status = getextmntent(fp, &mtab, sizeof (struct extmnttab))) 509 == 0) { 510 struct mtab_entry *mtep; 511 512 if (mount_table_entries == mount_table_allocated_entries) { 513 mount_table_allocated_entries += MOUNT_TABLE_ENTRIES; 514 mount_table = xrealloc(mount_table, 515 mount_table_allocated_entries * 516 sizeof (struct mtab_entry)); 517 } 518 mtep = &mount_table[mount_table_entries++]; 519 mtep->mte_mount = mntdup(&mtab); 520 mtep->mte_dev_is_valid = FALSE; 521 mtep->mte_ignore = (hasmntopt((struct mnttab *)&mtab, 522 MNTOPT_IGNORE) != NULL); 523 } 524 525 (void) fclose(fp); 526 527 if (status == -1) /* reached EOF */ 528 return; 529 mtab_error(mtab_file, status); 530 /* NOTREACHED */ 531 } 532 533 534 /* 535 * We use this macro when we want to record the option for the purpose of 536 * passing it to the FS-specific df 537 */ 538 #define SET_OPTION(opt) opt##_option = TRUE, \ 539 df_options[df_options_len++] = arg 540 541 static void 542 parse_options(int argc, char *argv[]) 543 { 544 int arg; 545 546 opterr = 0; /* getopt shouldn't complain about unknown options */ 547 548 #ifdef XPG4 549 while ((arg = getopt(argc, argv, "F:o:abehkVtgnlPZ")) != EOF) { 550 #else 551 while ((arg = getopt(argc, argv, "F:o:abehkVtgnlvZ")) != EOF) { 552 #endif 553 if (arg == 'F') { 554 if (F_option) 555 errmsg(ERR_FATAL + ERR_USAGE, 556 "more than one FSType specified"); 557 F_option = 1; 558 FSType = optarg; 559 } else if (arg == 'V' && ! V_option) { 560 V_option = TRUE; 561 } else if (arg == 'v' && ! v_option) { 562 v_option = TRUE; 563 #ifdef XPG4 564 } else if (arg == 'P' && ! P_option) { 565 SET_OPTION(P); 566 #endif 567 } else if (arg == 'a' && ! a_option) { 568 SET_OPTION(a); 569 } else if (arg == 'b' && ! b_option) { 570 SET_OPTION(b); 571 } else if (arg == 'e' && ! e_option) { 572 SET_OPTION(e); 573 } else if (arg == 'g' && ! g_option) { 574 SET_OPTION(g); 575 } else if (arg == 'h') { 576 use_scaling = TRUE; 577 scale = 1024; 578 } else if (arg == 'k' && ! k_option) { 579 SET_OPTION(k); 580 } else if (arg == 'l' && ! l_option) { 581 SET_OPTION(l); 582 } else if (arg == 'n' && ! n_option) { 583 SET_OPTION(n); 584 } else if (arg == 't' && ! t_option) { 585 SET_OPTION(t); 586 } else if (arg == 'o') { 587 if (o_option) 588 errmsg(ERR_FATAL + ERR_USAGE, 589 "the -o option can only be specified once"); 590 o_option = TRUE; 591 o_option_arg = optarg; 592 } else if (arg == 'Z') { 593 SET_OPTION(Z); 594 } else if (arg == '?') { 595 errmsg(ERR_USAGE, "unknown option: %c", optopt); 596 } 597 } 598 599 /* 600 * Option sanity checks 601 */ 602 if (g_option && o_option) 603 errmsg(ERR_FATAL, "-o and -g options are incompatible"); 604 if (l_option && o_option) 605 errmsg(ERR_FATAL, "-o and -l options are incompatible"); 606 if (n_option && o_option) 607 errmsg(ERR_FATAL, "-o and -n options are incompatible"); 608 if (use_scaling && o_option) 609 errmsg(ERR_FATAL, "-o and -h options are incompatible"); 610 } 611 612 613 614 /* 615 * Check if the user-specified argument is a resource name. 616 * A resource name is whatever is placed in the mnt_special field of 617 * struct mnttab. In the case of NFS, a resource name has the form 618 * hostname:pathname 619 * We try to find an exact match between the user-specified argument 620 * and the mnt_special field of a mount table entry. 621 * We also use the heuristic of removing the basename from the user-specified 622 * argument and repeating the test until we get a match. This works 623 * fine for NFS but may fail for other remote file system types. However, 624 * it is guaranteed that the function will not fail if the user specifies 625 * the exact resource name. 626 * If successful, this function sets the 'dfr_mte' field of '*dfrp' 627 */ 628 static void 629 resource_mount_entry(struct df_request *dfrp) 630 { 631 char *name; 632 633 /* 634 * We need our own copy since we will modify the string 635 */ 636 name = new_string(dfrp->dfr_cmd_arg); 637 638 for (;;) { 639 char *p; 640 int i; 641 642 /* 643 * Compare against all known mount points. 644 * We start from the most recent mount, which is at the 645 * end of the array. 646 */ 647 for (i = mount_table_entries - 1; i >= 0; i--) { 648 struct mtab_entry *mtep = &mount_table[i]; 649 650 if (EQ(name, mtep->mte_mount->mnt_special)) { 651 dfrp->dfr_mte = mtep; 652 break; 653 } 654 } 655 656 /* 657 * Remove the last component of the pathname. 658 * If there is no such component, this is not a resource name. 659 */ 660 p = strrchr(name, '/'); 661 if (p == NULL) 662 break; 663 *p = NUL; 664 } 665 } 666 667 668 669 /* 670 * Try to match the command line argument which is a block special device 671 * with the special device of one of the mounted file systems. 672 * If one is found, set the appropriate field of 'dfrp' to the mount 673 * table entry. 674 */ 675 static void 676 bdev_mount_entry(struct df_request *dfrp) 677 { 678 int i; 679 char *special = dfrp->dfr_cmd_arg; 680 681 /* 682 * Compare against all known mount points. 683 * We start from the most recent mount, which is at the 684 * end of the array. 685 */ 686 for (i = mount_table_entries - 1; i >= 0; i--) { 687 struct mtab_entry *mtep = &mount_table[i]; 688 689 if (EQ(special, mtep->mte_mount->mnt_special)) { 690 dfrp->dfr_mte = mtep; 691 break; 692 } 693 } 694 } 695 696 static struct mtab_entry * 697 devid_matches(int i, dev_t devno) 698 { 699 struct mtab_entry *mtep = &mount_table[i]; 700 struct extmnttab *mtp = mtep->mte_mount; 701 /* int len = strlen(mtp->mnt_mountp); */ 702 703 if (EQ(mtp->mnt_fstype, MNTTYPE_SWAP)) 704 return (NULL); 705 /* 706 * check if device numbers match. If there is a cached device number 707 * in the mtab_entry, use it, otherwise get the device number 708 * either from the mnttab entry or by stat'ing the mount point. 709 */ 710 if (! mtep->mte_dev_is_valid) { 711 struct stat64 st; 712 dev_t dev = NODEV; 713 714 dev = makedev(mtp->mnt_major, mtp->mnt_minor); 715 if (dev == 0) 716 dev = NODEV; 717 if (dev == NODEV) { 718 if (stat64(mtp->mnt_mountp, &st) == -1) { 719 return (NULL); 720 } else { 721 dev = st.st_dev; 722 } 723 } 724 mtep->mte_dev = dev; 725 mtep->mte_dev_is_valid = TRUE; 726 } 727 if (mtep->mte_dev == devno) { 728 return (mtep); 729 } 730 return (NULL); 731 } 732 733 /* 734 * Find the mount point under which the user-specified path resides 735 * and set the 'dfr_mte' field of '*dfrp' to point to the mount table entry. 736 */ 737 static void 738 path_mount_entry(struct df_request *dfrp, dev_t devno) 739 { 740 char dirpath[MAXPATHLEN]; 741 char *dir = dfrp->dfr_cmd_arg; 742 struct mtab_entry *match, *tmatch; 743 int i; 744 745 /* 746 * Expand the given path to get a canonical version (i.e. an absolute 747 * path without symbolic links). 748 */ 749 if (realpath(dir, dirpath) == NULL) { 750 errmsg(ERR_PERROR, "cannot canonicalize %s:", dir); 751 return; 752 } 753 /* 754 * If the mnt point is lofs, search from the top of entries from 755 * /etc/mnttab and return the first entry that matches the devid 756 * For non-lofs mount points, return the first entry from the bottom 757 * of the entries in /etc/mnttab that matches on the devid field 758 */ 759 match = NULL; 760 if (dfrp->dfr_fstype && EQ(dfrp->dfr_fstype, MNTTYPE_LOFS)) { 761 for (i = 0; i < mount_table_entries; i++) { 762 if (match = devid_matches(i, devno)) 763 break; 764 } 765 } else { 766 for (i = mount_table_entries - 1; i >= 0; i--) { 767 if (tmatch = devid_matches(i, devno)) { 768 /* 769 * If executing in a zone, there might be lofs 770 * mounts for which the real mount point is 771 * invisible; accept the "best fit" for this 772 * devid. 773 */ 774 match = tmatch; 775 if (!EQ(match->mte_mount->mnt_fstype, 776 MNTTYPE_LOFS)) { 777 break; 778 } 779 } 780 } 781 } 782 if (! match) { 783 errmsg(ERR_NOFLAGS, 784 "Could not find mount point for %s", dir); 785 return; 786 } 787 dfrp->dfr_mte = match; 788 } 789 790 /* 791 * Execute a single FS-specific df command for all given requests 792 * Return 0 if successful, 1 otherwise. 793 */ 794 static int 795 run_fs_specific_df(struct df_request request_list[], int entries) 796 { 797 int i; 798 int argv_index; 799 char **argv; 800 size_t size; 801 pid_t pid; 802 int status; 803 char cmd_path[MAXPATHLEN]; 804 char *fstype; 805 806 if (entries == 0) 807 return (0); 808 809 fstype = request_list[0].dfr_fstype; 810 811 if (F_option && ! EQ(FSType, fstype)) 812 return (0); 813 814 (void) sprintf(cmd_path, "%s%s/df", FS_LIBPATH, fstype); 815 /* 816 * Argv entries: 817 * 1 for the path 818 * 2 for -o <options> 819 * 1 for the generic options that we propagate 820 * 1 for the terminating NULL pointer 821 * n for the number of user-specified arguments 822 */ 823 size = (5 + entries) * sizeof (char *); 824 argv = xmalloc(size); 825 (void) memset(argv, 0, size); 826 827 argv[0] = cmd_path; 828 argv_index = 1; 829 if (o_option) { 830 argv[argv_index++] = "-o"; 831 argv[argv_index++] = o_option_arg; 832 } 833 834 /* 835 * Check if we need to propagate any generic options 836 */ 837 if (df_options_len > 1) 838 argv[argv_index++] = df_options; 839 840 /* 841 * If there is a user-specified path, we pass that to the 842 * FS-specific df. Otherwise, we are guaranteed to have a mount 843 * point, since a request without a user path implies that 844 * we are reporting only on mounted file systems. 845 */ 846 for (i = 0; i < entries; i++) { 847 struct df_request *dfrp = &request_list[i]; 848 849 argv[argv_index++] = (dfrp->dfr_cmd_arg == NULL) 850 ? DFR_MOUNT_POINT(dfrp) 851 : dfrp->dfr_cmd_arg; 852 } 853 854 if (V_option) { 855 for (i = 0; i < argv_index-1; i++) 856 (void) printf("%s ", argv[i]); 857 (void) printf("%s\n", argv[i]); 858 return (0); 859 } 860 861 pid = fork(); 862 863 if (pid == -1) { 864 errmsg(ERR_PERROR, "cannot fork process:"); 865 return (1); 866 } else if (pid == 0) { 867 (void) execv(cmd_path, argv); 868 if (errno == ENOENT) 869 errmsg(ERR_NOFLAGS, 870 "operation not applicable for FSType %s", 871 fstype); 872 else 873 errmsg(ERR_PERROR, "cannot execute %s:", cmd_path); 874 exit(2); 875 } 876 877 /* 878 * Reap the child 879 */ 880 for (;;) { 881 pid_t wpid = waitpid(pid, &status, 0); 882 883 if (wpid == -1) 884 if (errno == EINTR) 885 continue; 886 else { 887 errmsg(ERR_PERROR, "waitpid error:"); 888 return (1); 889 } 890 else 891 break; 892 } 893 894 return ((WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : 1); 895 } 896 897 898 899 /* 900 * Remove from the request list all requests that do not apply. 901 * Notice that the subsequent processing of the requests depends on 902 * the sanity checking performed by this function. 903 */ 904 static int 905 prune_list(struct df_request request_list[], 906 size_t n_requests, 907 size_t *valid_requests) 908 { 909 size_t i; 910 size_t n_valid = 0; 911 int errors = 0; 912 913 for (i = 0; i < n_requests; i++) { 914 struct df_request *dfrp = &request_list[i]; 915 916 /* 917 * Skip file systems that are not mounted if either the 918 * -l or -n options were specified. If none of these options 919 * are present, the appropriate FS-specific df will be invoked. 920 */ 921 if (! DFR_ISMOUNTEDFS(dfrp)) { 922 if (l_option || n_option) { 923 errmsg(ERR_NOFLAGS, 924 "%s option incompatible with unmounted special device (%s)", 925 l_option ? "-l" : "-n", dfrp->dfr_cmd_arg); 926 dfrp->dfr_valid = FALSE; 927 errors++; 928 } 929 else 930 n_valid++; 931 continue; 932 } 933 934 /* 935 * Check for inconsistency between the argument of -F and 936 * the actual file system type. 937 * If there is an inconsistency and the user specified a 938 * path, this is an error since we are asked to interpret 939 * the path using the wrong file system type. If there is 940 * no path associated with this request, we quietly ignore it. 941 */ 942 if (F_option && ! EQ(dfrp->dfr_fstype, FSType)) { 943 dfrp->dfr_valid = FALSE; 944 if (dfrp->dfr_cmd_arg != NULL) { 945 errmsg(ERR_NOFLAGS, 946 "Warning: %s mounted as a %s file system", 947 dfrp->dfr_cmd_arg, dfrp->dfr_fstype); 948 errors++; 949 } 950 continue; 951 } 952 953 /* 954 * Skip remote file systems if the -l option is present 955 */ 956 if (l_option && is_remote_fs(dfrp->dfr_fstype)) { 957 if (dfrp->dfr_cmd_arg != NULL) { 958 errmsg(ERR_NOFLAGS, 959 "Warning: %s is not a local file system", 960 dfrp->dfr_cmd_arg); 961 errors++; 962 } 963 dfrp->dfr_valid = FALSE; 964 continue; 965 } 966 967 /* 968 * Skip file systems mounted as "ignore" unless the -a option 969 * is present, or the user explicitly specified them on 970 * the command line. 971 */ 972 if (dfrp->dfr_mte->mte_ignore && 973 ! (a_option || dfrp->dfr_cmd_arg)) { 974 dfrp->dfr_valid = FALSE; 975 continue; 976 } 977 978 n_valid++; 979 } 980 *valid_requests = n_valid; 981 return (errors); 982 } 983 984 985 /* 986 * Print the appropriate header for the requested output format. 987 * Options are checked in order of their precedence. 988 */ 989 static void 990 print_header() 991 { 992 if (use_scaling) { /* this comes from the -h option */ 993 int arg = 'h'; 994 995 (void) printf("%-*s %*s %*s %*s %-*s %s\n", 996 FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 997 #ifdef XPG4 998 SCALED_WIDTH, TRANSLATE("Size"), 999 SCALED_WIDTH, TRANSLATE("Used"), 1000 AVAILABLE_WIDTH, TRANSLATE("Available"), 1001 CAPACITY_WIDTH, TRANSLATE("Capacity"), 1002 #else 1003 SCALED_WIDTH, TRANSLATE("size"), 1004 SCALED_WIDTH, TRANSLATE("used"), 1005 AVAILABLE_WIDTH, TRANSLATE("avail"), 1006 CAPACITY_WIDTH, TRANSLATE("capacity"), 1007 #endif 1008 TRANSLATE("Mounted on")); 1009 SET_OPTION(h); 1010 return; 1011 } 1012 if (k_option) { 1013 int arg = 'h'; 1014 1015 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"), 1016 FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 1017 #ifdef XPG4 1018 KBYTE_WIDTH, TRANSLATE("1024-blocks"), 1019 KBYTE_WIDTH, TRANSLATE("Used"), 1020 KBYTE_WIDTH, TRANSLATE("Available"), 1021 CAPACITY_WIDTH, TRANSLATE("Capacity"), 1022 #else 1023 KBYTE_WIDTH, TRANSLATE("kbytes"), 1024 KBYTE_WIDTH, TRANSLATE("used"), 1025 KBYTE_WIDTH, TRANSLATE("avail"), 1026 CAPACITY_WIDTH, TRANSLATE("capacity"), 1027 #endif 1028 TRANSLATE("Mounted on")); 1029 SET_OPTION(h); 1030 return; 1031 } 1032 /* Added for XCU4 compliance */ 1033 if (P_option) { 1034 int arg = 'h'; 1035 1036 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"), 1037 FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 1038 KBYTE_WIDTH, TRANSLATE("512-blocks"), 1039 KBYTE_WIDTH, TRANSLATE("Used"), 1040 KBYTE_WIDTH, TRANSLATE("Available"), 1041 CAPACITY_WIDTH, TRANSLATE("Capacity"), 1042 TRANSLATE("Mounted on")); 1043 1044 SET_OPTION(h); 1045 return; 1046 } 1047 /* End XCU4 */ 1048 if (v_option) { 1049 (void) printf("%-*s %-*s %*s %*s %*s %-*s\n", 1050 IBCS2_MOUNT_POINT_WIDTH, TRANSLATE("Mount Dir"), 1051 IBCS2_FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 1052 BLOCK_WIDTH, TRANSLATE("blocks"), 1053 BLOCK_WIDTH, TRANSLATE("used"), 1054 BLOCK_WIDTH, TRANSLATE("free"), 1055 CAPACITY_WIDTH, TRANSLATE(" %used")); 1056 return; 1057 } 1058 if (e_option) { 1059 (void) printf(gettext("%-*s %*s\n"), 1060 FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 1061 BLOCK_WIDTH, TRANSLATE("ifree")); 1062 return; 1063 } 1064 if (b_option) { 1065 (void) printf(gettext("%-*s %*s\n"), 1066 FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 1067 BLOCK_WIDTH, TRANSLATE("avail")); 1068 return; 1069 } 1070 } 1071 1072 1073 /* 1074 * Convert an unsigned long long to a string representation and place the 1075 * result in the caller-supplied buffer. 1076 * The given number is in units of "unit_from" size, but the 1077 * converted number will be in units of "unit_to" size. The unit sizes 1078 * must be powers of 2. 1079 * The value "(unsigned long long)-1" is a special case and is always 1080 * converted to "-1". 1081 * Returns a pointer to the caller-supplied buffer. 1082 */ 1083 static char * 1084 number_to_string( 1085 char *buf, /* put the result here */ 1086 unsigned long long number, /* convert this number */ 1087 int unit_from, /* from units of this size */ 1088 int unit_to) /* to units of this size */ 1089 { 1090 if ((long long)number == (long long)-1) 1091 (void) strcpy(buf, "-1"); 1092 else { 1093 if (unit_from == unit_to) 1094 (void) sprintf(buf, "%llu", number); 1095 else if (unit_from < unit_to) 1096 (void) sprintf(buf, "%llu", 1097 number / (unsigned long long)(unit_to / unit_from)); 1098 else 1099 (void) sprintf(buf, "%llu", 1100 number * (unsigned long long)(unit_from / unit_to)); 1101 } 1102 return (buf); 1103 } 1104 1105 /* 1106 * Convert an unsigned long long to a string representation and place the 1107 * result in the caller-supplied buffer. 1108 * The given number is in units of "unit_from" size, 1109 * this will first be converted to a number in 1024 or 1000 byte size, 1110 * depending on the scaling factor. 1111 * Then the number is scaled down until it is small enough to be in a good 1112 * human readable format i.e. in the range 0 thru scale-1. 1113 * If it's smaller than 10 there's room enough to provide one decimal place. 1114 * The value "(unsigned long long)-1" is a special case and is always 1115 * converted to "-1". 1116 * Returns a pointer to the caller-supplied buffer. 1117 */ 1118 static char * 1119 number_to_scaled_string( 1120 numbuf_t buf, /* put the result here */ 1121 unsigned long long number, /* convert this number */ 1122 int unit_from, 1123 int scale) 1124 { 1125 unsigned long long save = 0; 1126 char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */ 1127 char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */ 1128 1129 if ((long long)number == (long long)-1) { 1130 (void) strcpy(buf, "-1"); 1131 return (buf); 1132 } 1133 1134 /* 1135 * Convert number from unit_from to given scale (1024 or 1000). 1136 * This means multiply number by unit_from and divide by scale. 1137 * 1138 * Would like to multiply by unit_from and then divide by scale, 1139 * but if the first multiplication would overflow, then need to 1140 * divide by scale and then multiply by unit_from. 1141 */ 1142 if (number > (UINT64_MAX / (unsigned long long)unit_from)) { 1143 number = (number / (unsigned long long)scale) * 1144 (unsigned long long)unit_from; 1145 } else { 1146 number = (number * (unsigned long long)unit_from) / 1147 (unsigned long long)scale; 1148 } 1149 1150 /* 1151 * Now we have number as a count of scale units. 1152 * Stop scaling when we reached exa bytes, then something is 1153 * probably wrong with our number. 1154 */ 1155 1156 while ((number >= scale) && (*uom != 'E')) { 1157 uom++; /* next unit of measurement */ 1158 save = number; 1159 number = (number + (scale / 2)) / scale; 1160 } 1161 /* check if we should output a decimal place after the point */ 1162 if (save && ((save / scale) < 10)) { 1163 /* sprintf() will round for us */ 1164 float fnum = (float)save / scale; 1165 (void) sprintf(buf, "%2.1f%c", fnum, *uom); 1166 } else { 1167 (void) sprintf(buf, "%4llu%c", number, *uom); 1168 } 1169 return (buf); 1170 } 1171 1172 1173 /* 1174 * The output will appear properly columnized regardless of the names of 1175 * the various fields 1176 */ 1177 static void 1178 g_output(struct df_request *dfrp, struct statvfs64 *fsp) 1179 { 1180 fsblkcnt64_t available_blocks = fsp->f_bavail; 1181 numbuf_t total_blocks_buf; 1182 numbuf_t total_files_buf; 1183 numbuf_t free_blocks_buf; 1184 numbuf_t available_blocks_buf; 1185 numbuf_t free_files_buf; 1186 numbuf_t fname_buf; 1187 char *temp_buf; 1188 1189 #define DEFINE_STR_LEN(var) \ 1190 static char *var##_str; \ 1191 static size_t var##_len 1192 1193 #define SET_STR_LEN(name, var)\ 1194 if (! var##_str) {\ 1195 var##_str = TRANSLATE(name); \ 1196 var##_len = strlen(var##_str); \ 1197 } 1198 1199 DEFINE_STR_LEN(block_size); 1200 DEFINE_STR_LEN(frag_size); 1201 DEFINE_STR_LEN(total_blocks); 1202 DEFINE_STR_LEN(free_blocks); 1203 DEFINE_STR_LEN(available); 1204 DEFINE_STR_LEN(total_files); 1205 DEFINE_STR_LEN(free_files); 1206 DEFINE_STR_LEN(fstype); 1207 DEFINE_STR_LEN(fsys_id); 1208 DEFINE_STR_LEN(fname); 1209 DEFINE_STR_LEN(flag); 1210 1211 /* 1212 * TRANSLATION_NOTE 1213 * The first argument of each of the following macro invocations is a 1214 * string that needs to be translated. 1215 */ 1216 SET_STR_LEN("block size", block_size); 1217 SET_STR_LEN("frag size", frag_size); 1218 SET_STR_LEN("total blocks", total_blocks); 1219 SET_STR_LEN("free blocks", free_blocks); 1220 SET_STR_LEN("available", available); 1221 SET_STR_LEN("total files", total_files); 1222 SET_STR_LEN("free files", free_files); 1223 SET_STR_LEN("fstype", fstype); 1224 SET_STR_LEN("filesys id", fsys_id); 1225 SET_STR_LEN("filename length", fname); 1226 SET_STR_LEN("flag", flag); 1227 1228 #define NCOL1_WIDTH (int)MAX3(BLOCK_WIDTH, NFILES_WIDTH, FSTYPE_WIDTH) 1229 #define NCOL2_WIDTH (int)MAX3(BLOCK_WIDTH, FSID_WIDTH, FLAG_WIDTH) + 2 1230 #define NCOL3_WIDTH (int)MAX3(BSIZE_WIDTH, BLOCK_WIDTH, NAMELEN_WIDTH) 1231 #define NCOL4_WIDTH (int)MAX(FRAGSIZE_WIDTH, NFILES_WIDTH) 1232 1233 #define SCOL1_WIDTH (int)MAX3(total_blocks_len, free_files_len, fstype_len) 1234 #define SCOL2_WIDTH (int)MAX3(free_blocks_len, fsys_id_len, flag_len) 1235 #define SCOL3_WIDTH (int)MAX3(block_size_len, available_len, fname_len) 1236 #define SCOL4_WIDTH (int)MAX(frag_size_len, total_files_len) 1237 1238 temp_buf = xmalloc( 1239 MAX(MOUNT_POINT_WIDTH, strlen(DFR_MOUNT_POINT(dfrp))) 1240 + MAX(SPECIAL_DEVICE_WIDTH, strlen(DFR_SPECIAL(dfrp))) 1241 + 20); /* plus slop - nulls & formatting */ 1242 (void) sprintf(temp_buf, "%-*s(%-*s):", 1243 MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 1244 SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp)); 1245 1246 (void) printf("%-*s %*lu %-*s %*lu %-*s\n", 1247 NCOL1_WIDTH + 1 + SCOL1_WIDTH + 1 + NCOL2_WIDTH + 1 + SCOL2_WIDTH, 1248 temp_buf, 1249 NCOL3_WIDTH, fsp->f_bsize, SCOL3_WIDTH, block_size_str, 1250 NCOL4_WIDTH, fsp->f_frsize, SCOL4_WIDTH, frag_size_str); 1251 free(temp_buf); 1252 1253 /* 1254 * Adjust available_blocks value - it can be less than 0 on 1255 * a 4.x file system. Reset it to 0 in order to avoid printing 1256 * negative numbers. 1257 */ 1258 if ((long long)available_blocks < (long long)0) 1259 available_blocks = (fsblkcnt64_t)0; 1260 1261 (void) printf("%*s %-*s %*s %-*s %*s %-*s %*s %-*s\n", 1262 NCOL1_WIDTH, number_to_string(total_blocks_buf, 1263 fsp->f_blocks, fsp->f_frsize, 512), 1264 SCOL1_WIDTH, total_blocks_str, 1265 NCOL2_WIDTH, number_to_string(free_blocks_buf, 1266 fsp->f_bfree, fsp->f_frsize, 512), 1267 SCOL2_WIDTH, free_blocks_str, 1268 NCOL3_WIDTH, number_to_string(available_blocks_buf, 1269 available_blocks, fsp->f_frsize, 512), 1270 SCOL3_WIDTH, available_str, 1271 NCOL4_WIDTH, number_to_string(total_files_buf, 1272 fsp->f_files, 1, 1), 1273 SCOL4_WIDTH, total_files_str); 1274 1275 (void) printf("%*s %-*s %*lu %-*s %s\n", 1276 NCOL1_WIDTH, number_to_string(free_files_buf, 1277 fsp->f_ffree, 1, 1), 1278 SCOL1_WIDTH, free_files_str, 1279 NCOL2_WIDTH, fsp->f_fsid, SCOL2_WIDTH, fsys_id_str, 1280 fsp->f_fstr); 1281 1282 (void) printf("%*s %-*s %#*.*lx %-*s %*s %-*s\n\n", 1283 NCOL1_WIDTH, fsp->f_basetype, SCOL1_WIDTH, fstype_str, 1284 NCOL2_WIDTH, NCOL2_WIDTH-2, fsp->f_flag, SCOL2_WIDTH, flag_str, 1285 NCOL3_WIDTH, number_to_string(fname_buf, 1286 (unsigned long long)fsp->f_namemax, 1, 1), 1287 SCOL3_WIDTH, fname_str); 1288 } 1289 1290 1291 static void 1292 k_output(struct df_request *dfrp, struct statvfs64 *fsp) 1293 { 1294 fsblkcnt64_t total_blocks = fsp->f_blocks; 1295 fsblkcnt64_t free_blocks = fsp->f_bfree; 1296 fsblkcnt64_t available_blocks = fsp->f_bavail; 1297 fsblkcnt64_t used_blocks; 1298 char *file_system = DFR_SPECIAL(dfrp); 1299 numbuf_t total_blocks_buf; 1300 numbuf_t used_blocks_buf; 1301 numbuf_t available_blocks_buf; 1302 char capacity_buf[LINEBUF_SIZE]; 1303 1304 /* 1305 * If the free block count is -1, don't trust anything but the total 1306 * number of blocks. 1307 */ 1308 if (free_blocks == (fsblkcnt64_t)-1) { 1309 used_blocks = (fsblkcnt64_t)-1; 1310 (void) strcpy(capacity_buf, " 100%"); 1311 } else { 1312 fsblkcnt64_t reserved_blocks = free_blocks - available_blocks; 1313 1314 used_blocks = total_blocks - free_blocks; 1315 1316 /* 1317 * The capacity estimation is bogus when available_blocks is 0 1318 * and the super-user has allocated more space. The reason 1319 * is that reserved_blocks is inaccurate in that case, because 1320 * when the super-user allocates space, free_blocks is updated 1321 * but available_blocks is not (since it can't drop below 0). 1322 * 1323 * XCU4 and POSIX.2 require that any fractional result of the 1324 * capacity estimation be rounded to the next highest integer, 1325 * hence the addition of 0.5. 1326 */ 1327 (void) sprintf(capacity_buf, "%5.0f%%", 1328 (total_blocks == 0) ? 0.0 : 1329 ((double)used_blocks / 1330 (double)(total_blocks - reserved_blocks)) 1331 * 100.0 + 0.5); 1332 } 1333 1334 /* 1335 * The available_blocks can be less than 0 on a 4.x file system. 1336 * Reset it to 0 in order to avoid printing negative numbers. 1337 */ 1338 if ((long long)available_blocks < (long long)0) 1339 available_blocks = (fsblkcnt64_t)0; 1340 /* 1341 * Print long special device names (usually NFS mounts) in a line 1342 * by themselves when the output is directed to a terminal. 1343 */ 1344 if (tty_output && strlen(file_system) > (size_t)FILESYSTEM_WIDTH) { 1345 (void) printf("%s\n", file_system); 1346 file_system = ""; 1347 } 1348 1349 if (use_scaling) { /* comes from the -h option */ 1350 (void) printf("%-*s %*s %*s %*s %-*s %-s\n", 1351 FILESYSTEM_WIDTH, file_system, 1352 SCALED_WIDTH, number_to_scaled_string(total_blocks_buf, 1353 total_blocks, fsp->f_frsize, scale), 1354 SCALED_WIDTH, number_to_scaled_string(used_blocks_buf, 1355 used_blocks, fsp->f_frsize, scale), 1356 AVAILABLE_WIDTH, number_to_scaled_string(available_blocks_buf, 1357 available_blocks, fsp->f_frsize, scale), 1358 CAPACITY_WIDTH, capacity_buf, 1359 DFR_MOUNT_POINT(dfrp)); 1360 return; 1361 } 1362 1363 if (v_option) { 1364 (void) printf("%-*.*s %-*.*s %*lld %*lld %*lld %-.*s\n", 1365 IBCS2_MOUNT_POINT_WIDTH, IBCS2_MOUNT_POINT_WIDTH, 1366 DFR_MOUNT_POINT(dfrp), 1367 IBCS2_FILESYSTEM_WIDTH, IBCS2_FILESYSTEM_WIDTH, file_system, 1368 BLOCK_WIDTH, total_blocks, 1369 BLOCK_WIDTH, used_blocks, 1370 BLOCK_WIDTH, available_blocks, 1371 CAPACITY_WIDTH, capacity_buf); 1372 return; 1373 } 1374 1375 if (P_option && !k_option) { 1376 (void) printf("%-*s %*s %*s %*s %-*s %-s\n", 1377 FILESYSTEM_WIDTH, file_system, 1378 KBYTE_WIDTH, number_to_string(total_blocks_buf, 1379 total_blocks, fsp->f_frsize, 512), 1380 KBYTE_WIDTH, number_to_string(used_blocks_buf, 1381 used_blocks, fsp->f_frsize, 512), 1382 KBYTE_WIDTH, number_to_string(available_blocks_buf, 1383 available_blocks, fsp->f_frsize, 512), 1384 CAPACITY_WIDTH, capacity_buf, 1385 DFR_MOUNT_POINT(dfrp)); 1386 } else { 1387 (void) printf("%-*s %*s %*s %*s %-*s %-s\n", 1388 FILESYSTEM_WIDTH, file_system, 1389 KBYTE_WIDTH, number_to_string(total_blocks_buf, 1390 total_blocks, fsp->f_frsize, 1024), 1391 KBYTE_WIDTH, number_to_string(used_blocks_buf, 1392 used_blocks, fsp->f_frsize, 1024), 1393 KBYTE_WIDTH, number_to_string(available_blocks_buf, 1394 available_blocks, fsp->f_frsize, 1024), 1395 CAPACITY_WIDTH, capacity_buf, 1396 DFR_MOUNT_POINT(dfrp)); 1397 } 1398 } 1399 1400 /* 1401 * The following is for internationalization support. 1402 */ 1403 static bool_int strings_initialized; 1404 static char *files_str; 1405 static char *blocks_str; 1406 static char *total_str; 1407 static char *kilobytes_str; 1408 1409 static void 1410 strings_init() 1411 { 1412 total_str = TRANSLATE("total"); 1413 #ifdef _iBCS2 1414 /* ISC/SCO print i-nodes instead of files */ 1415 if (sysv3_set) 1416 files_str = TRANSLATE("i-nodes"); 1417 else 1418 #endif /* _iBCS2 */ 1419 files_str = TRANSLATE("files"); 1420 blocks_str = TRANSLATE("blocks"); 1421 kilobytes_str = TRANSLATE("kilobytes"); 1422 strings_initialized = TRUE; 1423 } 1424 1425 #define STRINGS_INIT() if (!strings_initialized) strings_init() 1426 1427 1428 static void 1429 t_output(struct df_request *dfrp, struct statvfs64 *fsp) 1430 { 1431 numbuf_t total_blocks_buf; 1432 numbuf_t total_files_buf; 1433 numbuf_t free_blocks_buf; 1434 numbuf_t free_files_buf; 1435 1436 STRINGS_INIT(); 1437 1438 (void) printf("%-*s(%-*s): %*s %s %*s %s\n", 1439 MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 1440 SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 1441 BLOCK_WIDTH, number_to_string(free_blocks_buf, 1442 fsp->f_bfree, fsp->f_frsize, 512), 1443 blocks_str, 1444 NFILES_WIDTH, number_to_string(free_files_buf, 1445 fsp->f_ffree, 1, 1), 1446 files_str); 1447 /* 1448 * The total column used to use the same space as the mnt pt & special 1449 * dev fields. However, this doesn't work with massive special dev 1450 * fields * (eg > 500 chars) causing an enormous amount of white space 1451 * before the total column (see bug 4100411). So the code was 1452 * simplified to set the total column at the usual gap. 1453 * This had the side effect of fixing a bug where the previously 1454 * used static buffer was overflowed by the same massive special dev. 1455 */ 1456 (void) printf("%*s: %*s %s %*s %s\n", 1457 MNT_SPEC_WIDTH, total_str, 1458 BLOCK_WIDTH, number_to_string(total_blocks_buf, 1459 fsp->f_blocks, fsp->f_frsize, 512), 1460 blocks_str, 1461 NFILES_WIDTH, number_to_string(total_files_buf, 1462 fsp->f_files, 1, 1), 1463 files_str); 1464 } 1465 1466 1467 static void 1468 eb_output(struct df_request *dfrp, struct statvfs64 *fsp) 1469 { 1470 numbuf_t free_files_buf; 1471 numbuf_t free_kbytes_buf; 1472 1473 STRINGS_INIT(); 1474 1475 (void) printf("%-*s(%-*s): %*s %s\n", 1476 MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 1477 SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 1478 MAX(KBYTE_WIDTH, NFILES_WIDTH), 1479 number_to_string(free_kbytes_buf, 1480 fsp->f_bfree, fsp->f_frsize, 1024), 1481 kilobytes_str); 1482 (void) printf("%-*s(%-*s): %*s %s\n", 1483 MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 1484 SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 1485 MAX(NFILES_WIDTH, NFILES_WIDTH), 1486 number_to_string(free_files_buf, fsp->f_ffree, 1, 1), 1487 files_str); 1488 } 1489 1490 1491 static void 1492 e_output(struct df_request *dfrp, struct statvfs64 *fsp) 1493 { 1494 numbuf_t free_files_buf; 1495 1496 (void) printf("%-*s %*s\n", 1497 FILESYSTEM_WIDTH, DFR_SPECIAL(dfrp), 1498 NFILES_WIDTH, 1499 number_to_string(free_files_buf, fsp->f_ffree, 1, 1)); 1500 } 1501 1502 1503 static void 1504 b_output(struct df_request *dfrp, struct statvfs64 *fsp) 1505 { 1506 numbuf_t free_blocks_buf; 1507 1508 (void) printf("%-*s %*s\n", 1509 FILESYSTEM_WIDTH, DFR_SPECIAL(dfrp), 1510 BLOCK_WIDTH, number_to_string(free_blocks_buf, 1511 fsp->f_bfree, fsp->f_frsize, 1024)); 1512 } 1513 1514 1515 /* ARGSUSED */ 1516 static void 1517 n_output(struct df_request *dfrp, struct statvfs64 *fsp) 1518 { 1519 (void) printf("%-*s: %-*s\n", 1520 MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 1521 FSTYPE_WIDTH, dfrp->dfr_fstype); 1522 } 1523 1524 1525 static void 1526 default_output(struct df_request *dfrp, struct statvfs64 *fsp) 1527 { 1528 numbuf_t free_blocks_buf; 1529 numbuf_t free_files_buf; 1530 1531 STRINGS_INIT(); 1532 1533 (void) printf("%-*s(%-*s):%*s %s %*s %s\n", 1534 MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 1535 SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 1536 BLOCK_WIDTH, number_to_string(free_blocks_buf, 1537 fsp->f_bfree, fsp->f_frsize, 512), 1538 blocks_str, 1539 NFILES_WIDTH, number_to_string(free_files_buf, 1540 fsp->f_ffree, 1, 1), 1541 files_str); 1542 } 1543 1544 1545 /* ARGSUSED */ 1546 static void 1547 V_output(struct df_request *dfrp, struct statvfs64 *fsp) 1548 { 1549 char temp_buf[LINEBUF_SIZE]; 1550 1551 if (df_options_len > 1) 1552 (void) strcat(strcpy(temp_buf, df_options), " "); 1553 else 1554 temp_buf[0] = NUL; 1555 1556 (void) printf("%s -F %s %s%s\n", 1557 program_name, dfrp->dfr_fstype, temp_buf, 1558 dfrp->dfr_cmd_arg ? dfrp->dfr_cmd_arg: DFR_SPECIAL(dfrp)); 1559 } 1560 1561 1562 /* 1563 * This function is used to sort the array of df_requests according to fstype 1564 */ 1565 static int 1566 df_reqcomp(const void *p1, const void *p2) 1567 { 1568 int v = strcmp(DFRP(p1)->dfr_fstype, DFRP(p2)->dfr_fstype); 1569 1570 if (v != 0) 1571 return (v); 1572 else 1573 return (DFRP(p1)->dfr_index - DFRP(p2)->dfr_index); 1574 } 1575 1576 1577 static void 1578 vfs_error(char *file, int status) 1579 { 1580 if (status == VFS_TOOLONG) 1581 errmsg(ERR_NOFLAGS, "a line in %s exceeds %d characters", 1582 file, MNT_LINE_MAX); 1583 else if (status == VFS_TOOMANY) 1584 errmsg(ERR_NOFLAGS, "a line in %s has too many fields", file); 1585 else if (status == VFS_TOOFEW) 1586 errmsg(ERR_NOFLAGS, "a line in %s has too few fields", file); 1587 else 1588 errmsg(ERR_NOFLAGS, "error while reading %s: %d", file, status); 1589 } 1590 1591 1592 /* 1593 * Try to determine the fstype for the specified block device. 1594 * Return in order of decreasing preference: 1595 * file system type from vfstab 1596 * file system type as specified by -F option 1597 * default file system type 1598 */ 1599 static char * 1600 find_fstype(char *special) 1601 { 1602 struct vfstab vtab; 1603 FILE *fp; 1604 int status; 1605 char *vfstab_file = VFS_TAB; 1606 1607 fp = xfopen(vfstab_file); 1608 status = getvfsspec(fp, &vtab, special); 1609 (void) fclose(fp); 1610 if (status > 0) 1611 vfs_error(vfstab_file, status); 1612 1613 if (status == 0) { 1614 if (F_option && ! EQ(FSType, vtab.vfs_fstype)) 1615 errmsg(ERR_NOFLAGS, 1616 "warning: %s is of type %s", special, vtab.vfs_fstype); 1617 return (new_string(vtab.vfs_fstype)); 1618 } 1619 else 1620 return (F_option ? FSType : default_fstype(special)); 1621 } 1622 1623 /* 1624 * When this function returns, the following fields are filled for all 1625 * valid entries in the requests[] array: 1626 * dfr_mte (if the file system is mounted) 1627 * dfr_fstype 1628 * dfr_index 1629 * 1630 * The function returns the number of errors that occurred while building 1631 * the request list. 1632 */ 1633 static int 1634 create_request_list( 1635 int argc, 1636 char *argv[], 1637 struct df_request *requests_p[], 1638 size_t *request_count) 1639 { 1640 struct df_request *requests; 1641 struct df_request *dfrp; 1642 size_t size; 1643 size_t i; 1644 size_t request_index = 0; 1645 size_t max_requests; 1646 int errors = 0; 1647 1648 /* 1649 * If no args, use the mounted file systems, otherwise use the 1650 * user-specified arguments. 1651 */ 1652 if (argc == 0) { 1653 mtab_read_file(); 1654 max_requests = mount_table_entries; 1655 } else 1656 max_requests = argc; 1657 1658 size = max_requests * sizeof (struct df_request); 1659 requests = xmalloc(size); 1660 (void) memset(requests, 0, size); 1661 1662 if (argc == 0) { 1663 /* 1664 * If -Z wasn't specified, we skip mounts in other 1665 * zones. This obviously is a noop in a non-global 1666 * zone. 1667 */ 1668 boolean_t showall = (getzoneid() != GLOBAL_ZONEID) || Z_option; 1669 struct zone_summary *zsp; 1670 1671 if (!showall) { 1672 zsp = fs_get_zone_summaries(); 1673 if (zsp == NULL) 1674 errmsg(ERR_FATAL, 1675 "unable to retrieve list of zones"); 1676 } 1677 1678 for (i = 0; i < mount_table_entries; i++) { 1679 struct extmnttab *mtp = mount_table[i].mte_mount; 1680 1681 if (EQ(mtp->mnt_fstype, MNTTYPE_SWAP)) 1682 continue; 1683 1684 if (!showall) { 1685 if (fs_mount_in_other_zone(zsp, 1686 mtp->mnt_mountp)) 1687 continue; 1688 } 1689 dfrp = &requests[request_index++]; 1690 dfrp->dfr_mte = &mount_table[i]; 1691 dfrp->dfr_fstype = mtp->mnt_fstype; 1692 dfrp->dfr_index = i; 1693 dfrp->dfr_valid = TRUE; 1694 } 1695 } else { 1696 struct stat64 *arg_stat; /* array of stat structures */ 1697 bool_int *valid_stat; /* which structures are valid */ 1698 1699 arg_stat = xmalloc(argc * sizeof (struct stat64)); 1700 valid_stat = xmalloc(argc * sizeof (bool_int)); 1701 1702 /* 1703 * Obtain stat64 information for each argument before 1704 * constructing the list of mounted file systems. By 1705 * touching all these places we force the automounter 1706 * to establish any mounts required to access the arguments, 1707 * so that the corresponding mount table entries will exist 1708 * when we look for them. 1709 * It is still possible that the automounter may timeout 1710 * mounts between the time we read the mount table and the 1711 * time we process the request. Even in that case, when 1712 * we issue the statvfs64(2) for the mount point, the file 1713 * system will be mounted again. The only problem will 1714 * occur if the automounter maps change in the meantime 1715 * and the mount point is eliminated. 1716 */ 1717 for (i = 0; i < argc; i++) 1718 valid_stat[i] = (stat64(argv[i], &arg_stat[i]) == 0); 1719 1720 mtab_read_file(); 1721 1722 for (i = 0; i < argc; i++) { 1723 char *arg = argv[i]; 1724 1725 dfrp = &requests[request_index]; 1726 1727 dfrp->dfr_index = request_index; 1728 dfrp->dfr_cmd_arg = arg; 1729 1730 if (valid_stat[i]) { 1731 if (S_ISBLK(arg_stat[i].st_mode)) { 1732 bdev_mount_entry(dfrp); 1733 dfrp->dfr_valid = TRUE; 1734 } else if (S_ISDIR(arg_stat[i].st_mode) || 1735 S_ISREG(arg_stat[i].st_mode) || 1736 S_ISFIFO(arg_stat[i].st_mode)) { 1737 path_mount_entry(dfrp, 1738 arg_stat[i].st_dev); 1739 if (! DFR_ISMOUNTEDFS(dfrp)) { 1740 errors++; 1741 continue; 1742 } 1743 dfrp->dfr_valid = TRUE; 1744 } 1745 } else { 1746 resource_mount_entry(dfrp); 1747 dfrp->dfr_valid = DFR_ISMOUNTEDFS(dfrp); 1748 } 1749 1750 /* 1751 * If we haven't managed to verify that the request 1752 * is valid, we must have gotten a bad argument. 1753 */ 1754 if (!dfrp->dfr_valid) { 1755 errmsg(ERR_NOFLAGS, 1756 "(%-10s) not a block device, directory or mounted resource", 1757 arg); 1758 errors++; 1759 continue; 1760 } 1761 1762 /* 1763 * Determine the file system type. 1764 */ 1765 if (DFR_ISMOUNTEDFS(dfrp)) 1766 dfrp->dfr_fstype = 1767 dfrp->dfr_mte->mte_mount->mnt_fstype; 1768 else 1769 dfrp->dfr_fstype = 1770 find_fstype(dfrp->dfr_cmd_arg); 1771 1772 request_index++; 1773 } 1774 } 1775 *requests_p = requests; 1776 *request_count = request_index; 1777 return (errors); 1778 } 1779 1780 1781 /* 1782 * Select the appropriate function and flags to use for output. 1783 * Notice that using both -e and -b options produces a different form of 1784 * output than either of those two options alone; this is the behavior of 1785 * the SVR4 df. 1786 */ 1787 static struct df_output * 1788 select_output() 1789 { 1790 static struct df_output dfo; 1791 1792 /* 1793 * The order of checking options follows the option precedence 1794 * rules as they are listed in the man page. 1795 */ 1796 if (use_scaling) { /* comes from the -h option */ 1797 dfo.dfo_func = k_output; 1798 dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 1799 } else if (V_option) { 1800 dfo.dfo_func = V_output; 1801 dfo.dfo_flags = DFO_NOFLAGS; 1802 } else if (g_option) { 1803 dfo.dfo_func = g_output; 1804 dfo.dfo_flags = DFO_STATVFS; 1805 } else if (k_option || P_option || v_option) { 1806 dfo.dfo_func = k_output; 1807 dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 1808 } else if (t_option) { 1809 dfo.dfo_func = t_output; 1810 dfo.dfo_flags = DFO_STATVFS; 1811 } else if (b_option && e_option) { 1812 dfo.dfo_func = eb_output; 1813 dfo.dfo_flags = DFO_STATVFS; 1814 } else if (b_option) { 1815 dfo.dfo_func = b_output; 1816 dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 1817 } else if (e_option) { 1818 dfo.dfo_func = e_output; 1819 dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 1820 } else if (n_option) { 1821 dfo.dfo_func = n_output; 1822 dfo.dfo_flags = DFO_NOFLAGS; 1823 } else { 1824 dfo.dfo_func = default_output; 1825 dfo.dfo_flags = DFO_STATVFS; 1826 } 1827 return (&dfo); 1828 } 1829 1830 1831 /* 1832 * The (argc,argv) pair contains all the non-option arguments 1833 */ 1834 static void 1835 do_df(int argc, char *argv[]) 1836 { 1837 size_t i; 1838 struct df_request *requests; /* array of requests */ 1839 size_t n_requests; 1840 struct df_request *dfrp; 1841 int errors; 1842 1843 errors = create_request_list(argc, argv, &requests, &n_requests); 1844 1845 if (n_requests == 0) 1846 exit(errors); 1847 1848 /* 1849 * If we are going to run the FSType-specific df command, 1850 * rearrange the requests so that we can issue a single command 1851 * per file system type. 1852 */ 1853 if (o_option) { 1854 size_t j; 1855 1856 /* 1857 * qsort is not a stable sorting method (i.e. requests of 1858 * the same file system type may be swapped, and hence appear 1859 * in the output in a different order from the one in which 1860 * they were listed in the command line). In order to force 1861 * stability, we use the dfr_index field which is unique 1862 * for each request. 1863 */ 1864 qsort(requests, 1865 n_requests, sizeof (struct df_request), df_reqcomp); 1866 for (i = 0; i < n_requests; i = j) { 1867 char *fstype = requests[i].dfr_fstype; 1868 1869 for (j = i+1; j < n_requests; j++) 1870 if (! EQ(fstype, requests[j].dfr_fstype)) 1871 break; 1872 1873 /* 1874 * At this point, requests in the range [i,j) are 1875 * of the same type. 1876 * 1877 * If the -F option was used, and the user specified 1878 * arguments, the filesystem types must match 1879 * 1880 * XXX: the alternative of doing this check here is to 1881 * invoke prune_list, but then we have to 1882 * modify this code to ignore invalid requests. 1883 */ 1884 if (F_option && ! EQ(fstype, FSType)) { 1885 size_t k; 1886 1887 for (k = i; k < j; k++) { 1888 dfrp = &requests[k]; 1889 if (dfrp->dfr_cmd_arg != NULL) { 1890 errmsg(ERR_NOFLAGS, 1891 "Warning: %s mounted as a %s file system", 1892 dfrp->dfr_cmd_arg, dfrp->dfr_fstype); 1893 errors++; 1894 } 1895 } 1896 } else 1897 errors += run_fs_specific_df(&requests[i], j-i); 1898 } 1899 } else { 1900 size_t valid_requests; 1901 1902 /* 1903 * We have to prune the request list to avoid printing a header 1904 * if there are no valid requests 1905 */ 1906 errors += prune_list(requests, n_requests, &valid_requests); 1907 1908 if (valid_requests) { 1909 struct df_output *dfop = select_output(); 1910 1911 /* indicates if we already printed out a header line */ 1912 int printed_header = 0; 1913 1914 for (i = 0; i < n_requests; i++) { 1915 dfrp = &requests[i]; 1916 if (! dfrp->dfr_valid) 1917 continue; 1918 1919 /* 1920 * If we don't have a mount point, 1921 * this must be a block device. 1922 */ 1923 if (DFR_ISMOUNTEDFS(dfrp)) { 1924 struct statvfs64 stvfs; 1925 1926 if ((dfop->dfo_flags & DFO_STATVFS) && 1927 statvfs64(DFR_MOUNT_POINT(dfrp), 1928 &stvfs) == -1) { 1929 errmsg(ERR_PERROR, 1930 "cannot statvfs %s:", 1931 DFR_MOUNT_POINT(dfrp)); 1932 errors++; 1933 continue; 1934 } 1935 if ((!printed_header) && 1936 (dfop->dfo_flags & DFO_HEADER)) { 1937 print_header(); 1938 printed_header = 1; 1939 } 1940 1941 (*dfop->dfo_func)(dfrp, &stvfs); 1942 } else { 1943 /* 1944 * -h option only works for 1945 * mounted filesystems 1946 */ 1947 if (use_scaling) { 1948 errmsg(ERR_NOFLAGS, 1949 "-h option incompatible with unmounted special device (%s)", 1950 dfrp->dfr_cmd_arg); 1951 errors++; 1952 continue; 1953 } 1954 errors += run_fs_specific_df(dfrp, 1); 1955 } 1956 } 1957 } 1958 } 1959 exit(errors); 1960 } 1961 1962 1963 /* 1964 * The rest of this file implements the devnm command 1965 */ 1966 1967 static char * 1968 find_dev_name(char *file, dev_t dev) 1969 { 1970 struct df_request dfreq; 1971 1972 dfreq.dfr_cmd_arg = file; 1973 dfreq.dfr_fstype = 0; 1974 dfreq.dfr_mte = NULL; 1975 path_mount_entry(&dfreq, dev); 1976 return (DFR_ISMOUNTEDFS(&dfreq) ? DFR_SPECIAL(&dfreq) : NULL); 1977 } 1978 1979 1980 static void 1981 do_devnm(int argc, char *argv[]) 1982 { 1983 int arg; 1984 int errors = 0; 1985 char *dev_name; 1986 1987 if (argc == 1) 1988 errmsg(ERR_NONAME, "Usage: %s name ...", DEVNM_CMD); 1989 1990 mtab_read_file(); 1991 1992 for (arg = 1; arg < argc; arg++) { 1993 char *file = argv[arg]; 1994 struct stat64 st; 1995 1996 if (stat64(file, &st) == -1) { 1997 errmsg(ERR_PERROR, "%s: ", file); 1998 errors++; 1999 continue; 2000 } 2001 2002 if (! is_remote_fs(st.st_fstype) && 2003 ! EQ(st.st_fstype, MNTTYPE_TMPFS) && 2004 (dev_name = find_dev_name(file, st.st_dev))) 2005 (void) printf("%s %s\n", dev_name, file); 2006 else 2007 errmsg(ERR_NOFLAGS, 2008 "%s not found", file); 2009 } 2010 exit(errors); 2011 /* NOTREACHED */ 2012 } 2013