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