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