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