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