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