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