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