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