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