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