10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 220Sstevel@tonic-gate /* All Rights Reserved */ 230Sstevel@tonic-gate 240Sstevel@tonic-gate 250Sstevel@tonic-gate /* 2612420SJim.Rice@Sun.COM * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 270Sstevel@tonic-gate */ 280Sstevel@tonic-gate 290Sstevel@tonic-gate 30789Sahrens #include <dlfcn.h> 310Sstevel@tonic-gate #include <stdio.h> 320Sstevel@tonic-gate #include <stdarg.h> 330Sstevel@tonic-gate #include <string.h> 340Sstevel@tonic-gate #include <locale.h> 350Sstevel@tonic-gate #include <libintl.h> 360Sstevel@tonic-gate #include <stdlib.h> 370Sstevel@tonic-gate #include <ftw.h> 380Sstevel@tonic-gate #include <errno.h> 390Sstevel@tonic-gate #include <sys/types.h> 400Sstevel@tonic-gate #include <unistd.h> 410Sstevel@tonic-gate #include <sys/statvfs.h> 420Sstevel@tonic-gate #include <sys/stat.h> 430Sstevel@tonic-gate #include <sys/param.h> 440Sstevel@tonic-gate #include <sys/mnttab.h> 450Sstevel@tonic-gate #include <sys/mntent.h> 460Sstevel@tonic-gate #include <sys/vfstab.h> 470Sstevel@tonic-gate #include <sys/wait.h> 480Sstevel@tonic-gate #include <sys/mkdev.h> 490Sstevel@tonic-gate #include <sys/int_limits.h> 500Sstevel@tonic-gate #include <sys/zone.h> 51789Sahrens #include <libzfs.h> 520Sstevel@tonic-gate 530Sstevel@tonic-gate #include "fslib.h" 540Sstevel@tonic-gate 550Sstevel@tonic-gate extern char *default_fstype(char *); 560Sstevel@tonic-gate 570Sstevel@tonic-gate /* 580Sstevel@tonic-gate * General notice: 590Sstevel@tonic-gate * String pointers in this code may point to statically allocated memory 600Sstevel@tonic-gate * or dynamically allocated memory. Furthermore, a dynamically allocated 610Sstevel@tonic-gate * string may be pointed to by more than one pointer. This does not pose 620Sstevel@tonic-gate * a problem because malloc'ed memory is never free'd (so we don't need 630Sstevel@tonic-gate * to remember which pointers point to malloc'ed memory). 640Sstevel@tonic-gate */ 650Sstevel@tonic-gate 660Sstevel@tonic-gate /* 670Sstevel@tonic-gate * TRANSLATION_NOTE 680Sstevel@tonic-gate * Only strings passed as arguments to the TRANSLATE macro need to 690Sstevel@tonic-gate * be translated. 700Sstevel@tonic-gate */ 710Sstevel@tonic-gate 720Sstevel@tonic-gate #ifndef MNTTYPE_LOFS 730Sstevel@tonic-gate #define MNTTYPE_LOFS "lofs" 740Sstevel@tonic-gate #endif 750Sstevel@tonic-gate 760Sstevel@tonic-gate #define EQ(s1, s2) (strcmp(s1, s2) == 0) 770Sstevel@tonic-gate #define NEW(type) xmalloc(sizeof (type)) 780Sstevel@tonic-gate #define CLEAR(var) (void) memset(&(var), 0, sizeof (var)) 790Sstevel@tonic-gate #define MAX(a, b) ((a) > (b) ? (a) : (b)) 800Sstevel@tonic-gate #define MAX3(a, b, c) MAX(a, MAX(b, c)) 810Sstevel@tonic-gate #define TRANSLATE(s) new_string(gettext(s)) 820Sstevel@tonic-gate 830Sstevel@tonic-gate #define MAX_OPTIONS 36 840Sstevel@tonic-gate #define N_FSTYPES 20 850Sstevel@tonic-gate #define MOUNT_TABLE_ENTRIES 40 /* initial allocation */ 860Sstevel@tonic-gate #define MSGBUF_SIZE 1024 870Sstevel@tonic-gate #define LINEBUF_SIZE 256 /* either input or output lines */ 880Sstevel@tonic-gate 890Sstevel@tonic-gate #define BLOCK_SIZE 512 /* when reporting in terms of blocks */ 900Sstevel@tonic-gate 910Sstevel@tonic-gate #define DEVNM_CMD "devnm" 920Sstevel@tonic-gate #define FS_LIBPATH "/usr/lib/fs/" 930Sstevel@tonic-gate #define MOUNT_TAB "/etc/mnttab" 940Sstevel@tonic-gate #define VFS_TAB "/etc/vfstab" 950Sstevel@tonic-gate #define REMOTE_FS "/etc/dfs/fstypes" 960Sstevel@tonic-gate 970Sstevel@tonic-gate #define NUL '\0' 980Sstevel@tonic-gate #define FALSE 0 990Sstevel@tonic-gate #define TRUE 1 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate /* 1020Sstevel@tonic-gate * Formatting constants 1030Sstevel@tonic-gate */ 1040Sstevel@tonic-gate #define IBCS2_FILESYSTEM_WIDTH 15 /* Truncate to match ISC/SCO */ 1050Sstevel@tonic-gate #define IBCS2_MOUNT_POINT_WIDTH 10 /* Truncate to match ISC/SCO */ 1060Sstevel@tonic-gate #define FILESYSTEM_WIDTH 20 1070Sstevel@tonic-gate #define MOUNT_POINT_WIDTH 19 1080Sstevel@tonic-gate #define SPECIAL_DEVICE_WIDTH 18 1090Sstevel@tonic-gate #define FSTYPE_WIDTH 8 1100Sstevel@tonic-gate #define BLOCK_WIDTH 8 1110Sstevel@tonic-gate #define NFILES_WIDTH 8 1120Sstevel@tonic-gate #ifdef XPG4 1130Sstevel@tonic-gate #define KBYTE_WIDTH 11 1140Sstevel@tonic-gate #define AVAILABLE_WIDTH 10 1150Sstevel@tonic-gate #else 1160Sstevel@tonic-gate #define KBYTE_WIDTH 7 1170Sstevel@tonic-gate #define AVAILABLE_WIDTH 6 1180Sstevel@tonic-gate #endif 1190Sstevel@tonic-gate #define SCALED_WIDTH 6 1200Sstevel@tonic-gate #define CAPACITY_WIDTH 9 1210Sstevel@tonic-gate #define BSIZE_WIDTH 6 1220Sstevel@tonic-gate #define FRAGSIZE_WIDTH 7 1230Sstevel@tonic-gate #define FSID_WIDTH 7 1240Sstevel@tonic-gate #define FLAG_WIDTH 8 1250Sstevel@tonic-gate #define NAMELEN_WIDTH 7 1260Sstevel@tonic-gate #define MNT_SPEC_WIDTH MOUNT_POINT_WIDTH + SPECIAL_DEVICE_WIDTH + 2 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate /* 1290Sstevel@tonic-gate * Flags for the errmsg() function 1300Sstevel@tonic-gate */ 1310Sstevel@tonic-gate #define ERR_NOFLAGS 0x0 1320Sstevel@tonic-gate #define ERR_NONAME 0x1 /* don't include the program name */ 1330Sstevel@tonic-gate /* as a prefix */ 1340Sstevel@tonic-gate #define ERR_FATAL 0x2 /* call exit after printing the */ 1350Sstevel@tonic-gate /* message */ 1360Sstevel@tonic-gate #define ERR_PERROR 0x4 /* append an errno explanation to */ 1370Sstevel@tonic-gate /* the message */ 1380Sstevel@tonic-gate #define ERR_USAGE 0x8 /* print the usage line after the */ 1390Sstevel@tonic-gate /* message */ 1400Sstevel@tonic-gate 1410Sstevel@tonic-gate #define NUMBER_WIDTH 40 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate /* 1440Sstevel@tonic-gate * A numbuf_t is used when converting a number to a string representation 1450Sstevel@tonic-gate */ 1460Sstevel@tonic-gate typedef char numbuf_t[ NUMBER_WIDTH ]; 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate /* 1490Sstevel@tonic-gate * We use bool_int instead of int to make clear which variables are 1500Sstevel@tonic-gate * supposed to be boolean 1510Sstevel@tonic-gate */ 1520Sstevel@tonic-gate typedef int bool_int; 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate struct mtab_entry { 1550Sstevel@tonic-gate bool_int mte_dev_is_valid; 1560Sstevel@tonic-gate dev_t mte_dev; 1570Sstevel@tonic-gate bool_int mte_ignore; /* the "ignore" option was set */ 1580Sstevel@tonic-gate struct extmnttab *mte_mount; 1590Sstevel@tonic-gate }; 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate struct df_request { 1630Sstevel@tonic-gate bool_int dfr_valid; 1640Sstevel@tonic-gate char *dfr_cmd_arg; /* what the user specified */ 1650Sstevel@tonic-gate struct mtab_entry *dfr_mte; 1660Sstevel@tonic-gate char *dfr_fstype; 1670Sstevel@tonic-gate int dfr_index; /* to make qsort stable */ 1680Sstevel@tonic-gate }; 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate #define DFR_MOUNT_POINT(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_mountp 1710Sstevel@tonic-gate #define DFR_SPECIAL(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_special 172789Sahrens #define DFR_FSTYPE(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_fstype 1730Sstevel@tonic-gate #define DFR_ISMOUNTEDFS(dfrp) ((dfrp)->dfr_mte != NULL) 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate #define DFRP(p) ((struct df_request *)(p)) 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate typedef void (*output_func)(struct df_request *, struct statvfs64 *); 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate struct df_output { 1800Sstevel@tonic-gate output_func dfo_func; /* function that will do the output */ 1810Sstevel@tonic-gate int dfo_flags; 1820Sstevel@tonic-gate }; 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate /* 1850Sstevel@tonic-gate * Output flags 1860Sstevel@tonic-gate */ 1870Sstevel@tonic-gate #define DFO_NOFLAGS 0x0 1880Sstevel@tonic-gate #define DFO_HEADER 0x1 /* output preceded by header */ 1890Sstevel@tonic-gate #define DFO_STATVFS 0x2 /* must do a statvfs64(2) */ 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate static char *program_name; 1930Sstevel@tonic-gate static char df_options[MAX_OPTIONS] = "-"; 1940Sstevel@tonic-gate static size_t df_options_len = 1; 1950Sstevel@tonic-gate static char *o_option_arg; /* arg to the -o option */ 1960Sstevel@tonic-gate static char *FSType; 1970Sstevel@tonic-gate static char *remote_fstypes[N_FSTYPES+1]; /* allocate an extra one */ 1980Sstevel@tonic-gate /* to use as a terminator */ 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate /* 2010Sstevel@tonic-gate * The following three variables support an in-memory copy of the mount table 2020Sstevel@tonic-gate * to speedup searches. 2030Sstevel@tonic-gate */ 2040Sstevel@tonic-gate static struct mtab_entry *mount_table; /* array of mtab_entry's */ 2050Sstevel@tonic-gate static size_t mount_table_entries; 2060Sstevel@tonic-gate static size_t mount_table_allocated_entries; 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate static bool_int F_option; 2090Sstevel@tonic-gate static bool_int V_option; 2100Sstevel@tonic-gate static bool_int P_option; /* Added for XCU4 compliance */ 2110Sstevel@tonic-gate static bool_int Z_option; 2120Sstevel@tonic-gate static bool_int v_option; 2130Sstevel@tonic-gate static bool_int a_option; 2140Sstevel@tonic-gate static bool_int b_option; 2150Sstevel@tonic-gate static bool_int e_option; 2160Sstevel@tonic-gate static bool_int g_option; 2170Sstevel@tonic-gate static bool_int h_option; 2180Sstevel@tonic-gate static bool_int k_option; 2190Sstevel@tonic-gate static bool_int l_option; 2200Sstevel@tonic-gate static bool_int n_option; 2210Sstevel@tonic-gate static bool_int t_option; 2220Sstevel@tonic-gate static bool_int o_option; 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate static bool_int tty_output; 2250Sstevel@tonic-gate static bool_int use_scaling; 2260Sstevel@tonic-gate static int scale; 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate static void usage(void); 2290Sstevel@tonic-gate static void do_devnm(int, char **); 230821Sdh145677 static void do_df(int, char **) __NORETURN; 2310Sstevel@tonic-gate static void parse_options(int, char **); 2320Sstevel@tonic-gate static char *basename(char *); 2330Sstevel@tonic-gate 23411814SChris.Kirby@sun.com static libzfs_handle_t *(*_libzfs_init)(void); 2352082Seschrock static zfs_handle_t *(*_zfs_open)(libzfs_handle_t *, const char *, int); 236789Sahrens static void (*_zfs_close)(zfs_handle_t *); 237789Sahrens static uint64_t (*_zfs_prop_get_int)(zfs_handle_t *, zfs_prop_t); 2382082Seschrock static libzfs_handle_t *g_zfs; 239789Sahrens 2401544Seschrock /* 2411544Seschrock * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs 2421544Seschrock * packages. A basic utility such as df shouldn't depend on optional 2431544Seschrock * filesystems. 2441544Seschrock */ 2452082Seschrock static boolean_t 2461544Seschrock load_libzfs(void) 2470Sstevel@tonic-gate { 248789Sahrens void *hdl; 249789Sahrens 2502082Seschrock if (_libzfs_init != NULL) 2512082Seschrock return (g_zfs != NULL); 2520Sstevel@tonic-gate 253789Sahrens if ((hdl = dlopen("libzfs.so", RTLD_LAZY)) != NULL) { 25411814SChris.Kirby@sun.com _libzfs_init = (libzfs_handle_t *(*)(void))dlsym(hdl, 2552082Seschrock "libzfs_init"); 256789Sahrens _zfs_open = (zfs_handle_t *(*)())dlsym(hdl, "zfs_open"); 257789Sahrens _zfs_close = (void (*)())dlsym(hdl, "zfs_close"); 258789Sahrens _zfs_prop_get_int = (uint64_t (*)()) 259789Sahrens dlsym(hdl, "zfs_prop_get_int"); 260789Sahrens 2612082Seschrock if (_libzfs_init != NULL) { 262789Sahrens assert(_zfs_open != NULL); 263789Sahrens assert(_zfs_close != NULL); 264789Sahrens assert(_zfs_prop_get_int != NULL); 265789Sahrens 26611814SChris.Kirby@sun.com g_zfs = _libzfs_init(); 267789Sahrens } 268789Sahrens } 269789Sahrens 2702082Seschrock return (g_zfs != NULL); 2711544Seschrock } 2721544Seschrock 2731544Seschrock int 2741544Seschrock main(int argc, char *argv[]) 2751544Seschrock { 2761544Seschrock (void) setlocale(LC_ALL, ""); 2771544Seschrock 2781544Seschrock #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 2791544Seschrock #define TEXT_DOMAIN "SYS_TEST" 2801544Seschrock #endif 2811544Seschrock (void) textdomain(TEXT_DOMAIN); 2821544Seschrock 2831544Seschrock program_name = basename(argv[0]); 2841544Seschrock 2850Sstevel@tonic-gate if (EQ(program_name, DEVNM_CMD)) 2860Sstevel@tonic-gate do_devnm(argc, argv); 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate parse_options(argc, argv); 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate /* 2910Sstevel@tonic-gate * The k_option implies SunOS 4.x compatibility: when the special 2920Sstevel@tonic-gate * device name is too long the line will be split except when the 2930Sstevel@tonic-gate * output has been redirected. 2940Sstevel@tonic-gate * This is also valid for the -h option. 2950Sstevel@tonic-gate */ 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate if (use_scaling || k_option || P_option || v_option) 2980Sstevel@tonic-gate tty_output = isatty(1); 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate do_df(argc - optind, &argv[optind]); 3010Sstevel@tonic-gate /* NOTREACHED */ 3020Sstevel@tonic-gate } 3030Sstevel@tonic-gate 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /* 3060Sstevel@tonic-gate * Prints an error message to stderr. 3070Sstevel@tonic-gate */ 3080Sstevel@tonic-gate /* VARARGS2 */ 3090Sstevel@tonic-gate static void 3100Sstevel@tonic-gate errmsg(int flags, char *fmt, ...) 3110Sstevel@tonic-gate { 3120Sstevel@tonic-gate char buf[MSGBUF_SIZE]; 3130Sstevel@tonic-gate va_list ap; 3140Sstevel@tonic-gate int cc; 3150Sstevel@tonic-gate int offset; 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate if (flags & ERR_NONAME) 3180Sstevel@tonic-gate offset = 0; 3190Sstevel@tonic-gate else 3200Sstevel@tonic-gate offset = sprintf(buf, "%s: ", program_name); 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate va_start(ap, fmt); 3230Sstevel@tonic-gate cc = vsprintf(&buf[offset], gettext(fmt), ap); 3240Sstevel@tonic-gate offset += cc; 3250Sstevel@tonic-gate va_end(ap); 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate if (flags & ERR_PERROR) { 3280Sstevel@tonic-gate if (buf[offset-1] != ' ') 3290Sstevel@tonic-gate (void) strcat(buf, " "); 3300Sstevel@tonic-gate (void) strcat(buf, strerror(errno)); 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate (void) fprintf(stderr, "%s\n", buf); 3330Sstevel@tonic-gate if (flags & ERR_USAGE) 3340Sstevel@tonic-gate usage(); 3350Sstevel@tonic-gate if (flags & ERR_FATAL) 3360Sstevel@tonic-gate exit(1); 3370Sstevel@tonic-gate } 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate static void 341821Sdh145677 usage(void) 3420Sstevel@tonic-gate { 3430Sstevel@tonic-gate #ifdef XPG4 3440Sstevel@tonic-gate errmsg(ERR_NONAME, 3455094Slling "Usage: %s [-F FSType] [-abeghklntPVZ] [-o FSType-specific_options]" 3465094Slling " [directory | block_device | resource]", program_name); 3470Sstevel@tonic-gate #else 3480Sstevel@tonic-gate errmsg(ERR_NONAME, 3495094Slling "Usage: %s [-F FSType] [-abeghklntVvZ] [-o FSType-specific_options]" 3505094Slling " [directory | block_device | resource]", program_name); 3510Sstevel@tonic-gate #endif 3520Sstevel@tonic-gate exit(1); 3530Sstevel@tonic-gate /* NOTREACHED */ 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate static char * 3580Sstevel@tonic-gate new_string(char *s) 3590Sstevel@tonic-gate { 3600Sstevel@tonic-gate char *p = NULL; 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate if (s) { 3630Sstevel@tonic-gate p = strdup(s); 3640Sstevel@tonic-gate if (p) 3650Sstevel@tonic-gate return (p); 3660Sstevel@tonic-gate errmsg(ERR_FATAL, "out of memory"); 3670Sstevel@tonic-gate /* NOTREACHED */ 3680Sstevel@tonic-gate } 3690Sstevel@tonic-gate return (p); 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate /* 3740Sstevel@tonic-gate * Allocate memory using malloc but terminate if the allocation fails 3750Sstevel@tonic-gate */ 3760Sstevel@tonic-gate static void * 3770Sstevel@tonic-gate xmalloc(size_t size) 3780Sstevel@tonic-gate { 3790Sstevel@tonic-gate void *p = malloc(size); 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate if (p) 3820Sstevel@tonic-gate return (p); 3830Sstevel@tonic-gate errmsg(ERR_FATAL, "out of memory"); 3840Sstevel@tonic-gate /* NOTREACHED */ 385821Sdh145677 return (NULL); 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate /* 3900Sstevel@tonic-gate * Allocate memory using realloc but terminate if the allocation fails 3910Sstevel@tonic-gate */ 3920Sstevel@tonic-gate static void * 3930Sstevel@tonic-gate xrealloc(void *ptr, size_t size) 3940Sstevel@tonic-gate { 3950Sstevel@tonic-gate void *p = realloc(ptr, size); 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate if (p) 3980Sstevel@tonic-gate return (p); 3990Sstevel@tonic-gate errmsg(ERR_FATAL, "out of memory"); 4000Sstevel@tonic-gate /* NOTREACHED */ 401821Sdh145677 return (NULL); 4020Sstevel@tonic-gate } 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate /* 4060Sstevel@tonic-gate * fopen the specified file for reading but terminate if the fopen fails 4070Sstevel@tonic-gate */ 4080Sstevel@tonic-gate static FILE * 4090Sstevel@tonic-gate xfopen(char *file) 4100Sstevel@tonic-gate { 4110Sstevel@tonic-gate FILE *fp = fopen(file, "r"); 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate if (fp == NULL) 4140Sstevel@tonic-gate errmsg(ERR_FATAL + ERR_PERROR, "failed to open %s:", file); 4150Sstevel@tonic-gate return (fp); 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate /* 4200Sstevel@tonic-gate * Read remote file system types from REMOTE_FS into the 4210Sstevel@tonic-gate * remote_fstypes array. 4220Sstevel@tonic-gate */ 4230Sstevel@tonic-gate static void 424821Sdh145677 init_remote_fs(void) 4250Sstevel@tonic-gate { 4260Sstevel@tonic-gate FILE *fp; 4270Sstevel@tonic-gate char line_buf[LINEBUF_SIZE]; 4280Sstevel@tonic-gate size_t fstype_index = 0; 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate if ((fp = fopen(REMOTE_FS, "r")) == NULL) { 4310Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 4325094Slling "Warning: can't open %s, ignored", REMOTE_FS); 4330Sstevel@tonic-gate return; 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate while (fgets(line_buf, sizeof (line_buf), fp) != NULL) { 4370Sstevel@tonic-gate char buf[LINEBUF_SIZE]; 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate (void) sscanf(line_buf, "%s", buf); 4400Sstevel@tonic-gate remote_fstypes[fstype_index++] = new_string(buf); 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate if (fstype_index == N_FSTYPES) 4430Sstevel@tonic-gate break; 4440Sstevel@tonic-gate } 4450Sstevel@tonic-gate (void) fclose(fp); 4460Sstevel@tonic-gate } 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate /* 4500Sstevel@tonic-gate * Returns TRUE if fstype is a remote file system type; 4510Sstevel@tonic-gate * otherwise, returns FALSE. 4520Sstevel@tonic-gate */ 4530Sstevel@tonic-gate static int 4540Sstevel@tonic-gate is_remote_fs(char *fstype) 4550Sstevel@tonic-gate { 4560Sstevel@tonic-gate char **p; 4570Sstevel@tonic-gate static bool_int remote_fs_initialized; 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate if (! remote_fs_initialized) { 4600Sstevel@tonic-gate init_remote_fs(); 4610Sstevel@tonic-gate remote_fs_initialized = TRUE; 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate for (p = remote_fstypes; *p; p++) 4650Sstevel@tonic-gate if (EQ(fstype, *p)) 4660Sstevel@tonic-gate return (TRUE); 4670Sstevel@tonic-gate return (FALSE); 4680Sstevel@tonic-gate } 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate static char * 4720Sstevel@tonic-gate basename(char *s) 4730Sstevel@tonic-gate { 4740Sstevel@tonic-gate char *p = strrchr(s, '/'); 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate return (p ? p+1 : s); 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate 4800Sstevel@tonic-gate /* 4810Sstevel@tonic-gate * Create a new "struct extmnttab" and make sure that its fields point 4820Sstevel@tonic-gate * to malloc'ed memory 4830Sstevel@tonic-gate */ 4840Sstevel@tonic-gate static struct extmnttab * 4850Sstevel@tonic-gate mntdup(struct extmnttab *old) 4860Sstevel@tonic-gate { 4870Sstevel@tonic-gate struct extmnttab *new = NEW(struct extmnttab); 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate new->mnt_special = new_string(old->mnt_special); 4900Sstevel@tonic-gate new->mnt_mountp = new_string(old->mnt_mountp); 4910Sstevel@tonic-gate new->mnt_fstype = new_string(old->mnt_fstype); 4920Sstevel@tonic-gate new->mnt_mntopts = new_string(old->mnt_mntopts); 4930Sstevel@tonic-gate new->mnt_time = new_string(old->mnt_time); 4940Sstevel@tonic-gate new->mnt_major = old->mnt_major; 4950Sstevel@tonic-gate new->mnt_minor = old->mnt_minor; 4960Sstevel@tonic-gate return (new); 4970Sstevel@tonic-gate } 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate 5000Sstevel@tonic-gate static void 5010Sstevel@tonic-gate mtab_error(char *mtab_file, int status) 5020Sstevel@tonic-gate { 5030Sstevel@tonic-gate if (status == MNT_TOOLONG) 5040Sstevel@tonic-gate errmsg(ERR_NOFLAGS, "a line in %s exceeds %d characters", 5055094Slling mtab_file, MNT_LINE_MAX); 5060Sstevel@tonic-gate else if (status == MNT_TOOMANY) 5070Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 5085094Slling "a line in %s has too many fields", mtab_file); 5090Sstevel@tonic-gate else if (status == MNT_TOOFEW) 5100Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 5115094Slling "a line in %s has too few fields", mtab_file); 5120Sstevel@tonic-gate else 5130Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 5145094Slling "error while reading %s: %d", mtab_file, status); 5150Sstevel@tonic-gate exit(1); 5160Sstevel@tonic-gate /* NOTREACHED */ 5170Sstevel@tonic-gate } 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate /* 5210Sstevel@tonic-gate * Read the mount table from the specified file. 5220Sstevel@tonic-gate * We keep the table in memory for faster lookups. 5230Sstevel@tonic-gate */ 5240Sstevel@tonic-gate static void 525821Sdh145677 mtab_read_file(void) 5260Sstevel@tonic-gate { 5270Sstevel@tonic-gate char *mtab_file = MOUNT_TAB; 5280Sstevel@tonic-gate FILE *fp; 5290Sstevel@tonic-gate struct extmnttab mtab; 5300Sstevel@tonic-gate int status; 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate fp = xfopen(mtab_file); 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate resetmnttab(fp); 5350Sstevel@tonic-gate mount_table_allocated_entries = MOUNT_TABLE_ENTRIES; 5360Sstevel@tonic-gate mount_table_entries = 0; 5370Sstevel@tonic-gate mount_table = xmalloc( 5385094Slling mount_table_allocated_entries * sizeof (struct mtab_entry)); 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate while ((status = getextmntent(fp, &mtab, sizeof (struct extmnttab))) 5415094Slling == 0) { 5420Sstevel@tonic-gate struct mtab_entry *mtep; 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate if (mount_table_entries == mount_table_allocated_entries) { 5450Sstevel@tonic-gate mount_table_allocated_entries += MOUNT_TABLE_ENTRIES; 5460Sstevel@tonic-gate mount_table = xrealloc(mount_table, 5475094Slling mount_table_allocated_entries * 5485094Slling sizeof (struct mtab_entry)); 5490Sstevel@tonic-gate } 5500Sstevel@tonic-gate mtep = &mount_table[mount_table_entries++]; 5510Sstevel@tonic-gate mtep->mte_mount = mntdup(&mtab); 5520Sstevel@tonic-gate mtep->mte_dev_is_valid = FALSE; 5530Sstevel@tonic-gate mtep->mte_ignore = (hasmntopt((struct mnttab *)&mtab, 5545094Slling MNTOPT_IGNORE) != NULL); 5550Sstevel@tonic-gate } 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate (void) fclose(fp); 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate if (status == -1) /* reached EOF */ 5600Sstevel@tonic-gate return; 5610Sstevel@tonic-gate mtab_error(mtab_file, status); 5620Sstevel@tonic-gate /* NOTREACHED */ 5630Sstevel@tonic-gate } 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate /* 5670Sstevel@tonic-gate * We use this macro when we want to record the option for the purpose of 5680Sstevel@tonic-gate * passing it to the FS-specific df 5690Sstevel@tonic-gate */ 5700Sstevel@tonic-gate #define SET_OPTION(opt) opt##_option = TRUE, \ 5710Sstevel@tonic-gate df_options[df_options_len++] = arg 5720Sstevel@tonic-gate 5730Sstevel@tonic-gate static void 5740Sstevel@tonic-gate parse_options(int argc, char *argv[]) 5750Sstevel@tonic-gate { 5760Sstevel@tonic-gate int arg; 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate opterr = 0; /* getopt shouldn't complain about unknown options */ 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate #ifdef XPG4 5810Sstevel@tonic-gate while ((arg = getopt(argc, argv, "F:o:abehkVtgnlPZ")) != EOF) { 5820Sstevel@tonic-gate #else 5830Sstevel@tonic-gate while ((arg = getopt(argc, argv, "F:o:abehkVtgnlvZ")) != EOF) { 5840Sstevel@tonic-gate #endif 5850Sstevel@tonic-gate if (arg == 'F') { 5860Sstevel@tonic-gate if (F_option) 5870Sstevel@tonic-gate errmsg(ERR_FATAL + ERR_USAGE, 5885094Slling "more than one FSType specified"); 5890Sstevel@tonic-gate F_option = 1; 5900Sstevel@tonic-gate FSType = optarg; 5910Sstevel@tonic-gate } else if (arg == 'V' && ! V_option) { 5920Sstevel@tonic-gate V_option = TRUE; 5930Sstevel@tonic-gate } else if (arg == 'v' && ! v_option) { 5940Sstevel@tonic-gate v_option = TRUE; 5950Sstevel@tonic-gate #ifdef XPG4 5960Sstevel@tonic-gate } else if (arg == 'P' && ! P_option) { 5970Sstevel@tonic-gate SET_OPTION(P); 5980Sstevel@tonic-gate #endif 5990Sstevel@tonic-gate } else if (arg == 'a' && ! a_option) { 6000Sstevel@tonic-gate SET_OPTION(a); 6010Sstevel@tonic-gate } else if (arg == 'b' && ! b_option) { 6020Sstevel@tonic-gate SET_OPTION(b); 6030Sstevel@tonic-gate } else if (arg == 'e' && ! e_option) { 6040Sstevel@tonic-gate SET_OPTION(e); 6050Sstevel@tonic-gate } else if (arg == 'g' && ! g_option) { 6060Sstevel@tonic-gate SET_OPTION(g); 6070Sstevel@tonic-gate } else if (arg == 'h') { 6080Sstevel@tonic-gate use_scaling = TRUE; 6090Sstevel@tonic-gate scale = 1024; 6100Sstevel@tonic-gate } else if (arg == 'k' && ! k_option) { 6110Sstevel@tonic-gate SET_OPTION(k); 6120Sstevel@tonic-gate } else if (arg == 'l' && ! l_option) { 6130Sstevel@tonic-gate SET_OPTION(l); 6140Sstevel@tonic-gate } else if (arg == 'n' && ! n_option) { 6150Sstevel@tonic-gate SET_OPTION(n); 6160Sstevel@tonic-gate } else if (arg == 't' && ! t_option) { 6170Sstevel@tonic-gate SET_OPTION(t); 6180Sstevel@tonic-gate } else if (arg == 'o') { 6190Sstevel@tonic-gate if (o_option) 6200Sstevel@tonic-gate errmsg(ERR_FATAL + ERR_USAGE, 6210Sstevel@tonic-gate "the -o option can only be specified once"); 6220Sstevel@tonic-gate o_option = TRUE; 6230Sstevel@tonic-gate o_option_arg = optarg; 6240Sstevel@tonic-gate } else if (arg == 'Z') { 6250Sstevel@tonic-gate SET_OPTION(Z); 6260Sstevel@tonic-gate } else if (arg == '?') { 6270Sstevel@tonic-gate errmsg(ERR_USAGE, "unknown option: %c", optopt); 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate } 6300Sstevel@tonic-gate 6310Sstevel@tonic-gate /* 6320Sstevel@tonic-gate * Option sanity checks 6330Sstevel@tonic-gate */ 6340Sstevel@tonic-gate if (g_option && o_option) 6350Sstevel@tonic-gate errmsg(ERR_FATAL, "-o and -g options are incompatible"); 6360Sstevel@tonic-gate if (l_option && o_option) 6370Sstevel@tonic-gate errmsg(ERR_FATAL, "-o and -l options are incompatible"); 6380Sstevel@tonic-gate if (n_option && o_option) 6390Sstevel@tonic-gate errmsg(ERR_FATAL, "-o and -n options are incompatible"); 6400Sstevel@tonic-gate if (use_scaling && o_option) 6410Sstevel@tonic-gate errmsg(ERR_FATAL, "-o and -h options are incompatible"); 6420Sstevel@tonic-gate } 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate /* 6470Sstevel@tonic-gate * Check if the user-specified argument is a resource name. 6480Sstevel@tonic-gate * A resource name is whatever is placed in the mnt_special field of 6490Sstevel@tonic-gate * struct mnttab. In the case of NFS, a resource name has the form 6500Sstevel@tonic-gate * hostname:pathname 6510Sstevel@tonic-gate * We try to find an exact match between the user-specified argument 6520Sstevel@tonic-gate * and the mnt_special field of a mount table entry. 6530Sstevel@tonic-gate * We also use the heuristic of removing the basename from the user-specified 6540Sstevel@tonic-gate * argument and repeating the test until we get a match. This works 6550Sstevel@tonic-gate * fine for NFS but may fail for other remote file system types. However, 6560Sstevel@tonic-gate * it is guaranteed that the function will not fail if the user specifies 6570Sstevel@tonic-gate * the exact resource name. 6580Sstevel@tonic-gate * If successful, this function sets the 'dfr_mte' field of '*dfrp' 6590Sstevel@tonic-gate */ 6600Sstevel@tonic-gate static void 6610Sstevel@tonic-gate resource_mount_entry(struct df_request *dfrp) 6620Sstevel@tonic-gate { 6630Sstevel@tonic-gate char *name; 6640Sstevel@tonic-gate 6650Sstevel@tonic-gate /* 6660Sstevel@tonic-gate * We need our own copy since we will modify the string 6670Sstevel@tonic-gate */ 6680Sstevel@tonic-gate name = new_string(dfrp->dfr_cmd_arg); 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate for (;;) { 6710Sstevel@tonic-gate char *p; 6720Sstevel@tonic-gate int i; 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate /* 6750Sstevel@tonic-gate * Compare against all known mount points. 6760Sstevel@tonic-gate * We start from the most recent mount, which is at the 6770Sstevel@tonic-gate * end of the array. 6780Sstevel@tonic-gate */ 6790Sstevel@tonic-gate for (i = mount_table_entries - 1; i >= 0; i--) { 6800Sstevel@tonic-gate struct mtab_entry *mtep = &mount_table[i]; 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate if (EQ(name, mtep->mte_mount->mnt_special)) { 6830Sstevel@tonic-gate dfrp->dfr_mte = mtep; 6840Sstevel@tonic-gate break; 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate } 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate /* 6890Sstevel@tonic-gate * Remove the last component of the pathname. 6900Sstevel@tonic-gate * If there is no such component, this is not a resource name. 6910Sstevel@tonic-gate */ 6920Sstevel@tonic-gate p = strrchr(name, '/'); 6930Sstevel@tonic-gate if (p == NULL) 6940Sstevel@tonic-gate break; 6950Sstevel@tonic-gate *p = NUL; 6960Sstevel@tonic-gate } 6970Sstevel@tonic-gate } 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate /* 7020Sstevel@tonic-gate * Try to match the command line argument which is a block special device 7030Sstevel@tonic-gate * with the special device of one of the mounted file systems. 7040Sstevel@tonic-gate * If one is found, set the appropriate field of 'dfrp' to the mount 7050Sstevel@tonic-gate * table entry. 7060Sstevel@tonic-gate */ 7070Sstevel@tonic-gate static void 7080Sstevel@tonic-gate bdev_mount_entry(struct df_request *dfrp) 7090Sstevel@tonic-gate { 7100Sstevel@tonic-gate int i; 7110Sstevel@tonic-gate char *special = dfrp->dfr_cmd_arg; 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate /* 7140Sstevel@tonic-gate * Compare against all known mount points. 7150Sstevel@tonic-gate * We start from the most recent mount, which is at the 7160Sstevel@tonic-gate * end of the array. 7170Sstevel@tonic-gate */ 7180Sstevel@tonic-gate for (i = mount_table_entries - 1; i >= 0; i--) { 7190Sstevel@tonic-gate struct mtab_entry *mtep = &mount_table[i]; 7200Sstevel@tonic-gate 7210Sstevel@tonic-gate if (EQ(special, mtep->mte_mount->mnt_special)) { 7220Sstevel@tonic-gate dfrp->dfr_mte = mtep; 7230Sstevel@tonic-gate break; 7240Sstevel@tonic-gate } 7250Sstevel@tonic-gate } 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate static struct mtab_entry * 7290Sstevel@tonic-gate devid_matches(int i, dev_t devno) 7300Sstevel@tonic-gate { 7310Sstevel@tonic-gate struct mtab_entry *mtep = &mount_table[i]; 7320Sstevel@tonic-gate struct extmnttab *mtp = mtep->mte_mount; 7330Sstevel@tonic-gate /* int len = strlen(mtp->mnt_mountp); */ 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate if (EQ(mtp->mnt_fstype, MNTTYPE_SWAP)) 7360Sstevel@tonic-gate return (NULL); 7370Sstevel@tonic-gate /* 7380Sstevel@tonic-gate * check if device numbers match. If there is a cached device number 7390Sstevel@tonic-gate * in the mtab_entry, use it, otherwise get the device number 7400Sstevel@tonic-gate * either from the mnttab entry or by stat'ing the mount point. 7410Sstevel@tonic-gate */ 7420Sstevel@tonic-gate if (! mtep->mte_dev_is_valid) { 7430Sstevel@tonic-gate struct stat64 st; 7440Sstevel@tonic-gate dev_t dev = NODEV; 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate dev = makedev(mtp->mnt_major, mtp->mnt_minor); 7470Sstevel@tonic-gate if (dev == 0) 7480Sstevel@tonic-gate dev = NODEV; 7490Sstevel@tonic-gate if (dev == NODEV) { 7500Sstevel@tonic-gate if (stat64(mtp->mnt_mountp, &st) == -1) { 7510Sstevel@tonic-gate return (NULL); 7520Sstevel@tonic-gate } else { 7530Sstevel@tonic-gate dev = st.st_dev; 7540Sstevel@tonic-gate } 7550Sstevel@tonic-gate } 7560Sstevel@tonic-gate mtep->mte_dev = dev; 7570Sstevel@tonic-gate mtep->mte_dev_is_valid = TRUE; 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate if (mtep->mte_dev == devno) { 7600Sstevel@tonic-gate return (mtep); 7610Sstevel@tonic-gate } 7620Sstevel@tonic-gate return (NULL); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate 7650Sstevel@tonic-gate /* 7660Sstevel@tonic-gate * Find the mount point under which the user-specified path resides 7670Sstevel@tonic-gate * and set the 'dfr_mte' field of '*dfrp' to point to the mount table entry. 7680Sstevel@tonic-gate */ 7690Sstevel@tonic-gate static void 7700Sstevel@tonic-gate path_mount_entry(struct df_request *dfrp, dev_t devno) 7710Sstevel@tonic-gate { 7720Sstevel@tonic-gate char dirpath[MAXPATHLEN]; 7730Sstevel@tonic-gate char *dir = dfrp->dfr_cmd_arg; 7740Sstevel@tonic-gate struct mtab_entry *match, *tmatch; 7750Sstevel@tonic-gate int i; 7760Sstevel@tonic-gate 7770Sstevel@tonic-gate /* 7780Sstevel@tonic-gate * Expand the given path to get a canonical version (i.e. an absolute 7790Sstevel@tonic-gate * path without symbolic links). 7800Sstevel@tonic-gate */ 7810Sstevel@tonic-gate if (realpath(dir, dirpath) == NULL) { 7820Sstevel@tonic-gate errmsg(ERR_PERROR, "cannot canonicalize %s:", dir); 7830Sstevel@tonic-gate return; 7840Sstevel@tonic-gate } 7850Sstevel@tonic-gate /* 7860Sstevel@tonic-gate * If the mnt point is lofs, search from the top of entries from 78712420SJim.Rice@Sun.COM * /etc/mnttab and return the entry that best matches the pathname. 7880Sstevel@tonic-gate * For non-lofs mount points, return the first entry from the bottom 7890Sstevel@tonic-gate * of the entries in /etc/mnttab that matches on the devid field 7900Sstevel@tonic-gate */ 7910Sstevel@tonic-gate match = NULL; 7920Sstevel@tonic-gate if (dfrp->dfr_fstype && EQ(dfrp->dfr_fstype, MNTTYPE_LOFS)) { 79312420SJim.Rice@Sun.COM struct extmnttab *entryp; 79412420SJim.Rice@Sun.COM char *path, *mountp; 79512420SJim.Rice@Sun.COM char p, m; 79612420SJim.Rice@Sun.COM int score; 79712420SJim.Rice@Sun.COM int best_score = 0; 79812420SJim.Rice@Sun.COM int best_index = -1; 79912420SJim.Rice@Sun.COM 8000Sstevel@tonic-gate for (i = 0; i < mount_table_entries; i++) { 80112420SJim.Rice@Sun.COM entryp = mount_table[i].mte_mount; 80212420SJim.Rice@Sun.COM 80312420SJim.Rice@Sun.COM if (!EQ(entryp->mnt_fstype, MNTTYPE_LOFS)) 80412420SJim.Rice@Sun.COM continue; 80512420SJim.Rice@Sun.COM 80612420SJim.Rice@Sun.COM path = dirpath; 80712420SJim.Rice@Sun.COM mountp = entryp->mnt_mountp; 80812420SJim.Rice@Sun.COM score = 0; 80912420SJim.Rice@Sun.COM /* 81012420SJim.Rice@Sun.COM * Count the number of matching characters 81112420SJim.Rice@Sun.COM * until either path or mountpoint is exhausted 81212420SJim.Rice@Sun.COM */ 81312420SJim.Rice@Sun.COM while ((p = *path++) == (m = *mountp++)) { 81412420SJim.Rice@Sun.COM score++; 81512420SJim.Rice@Sun.COM 81612420SJim.Rice@Sun.COM if (p == '\0' || m == '\0') 81712420SJim.Rice@Sun.COM break; 81812420SJim.Rice@Sun.COM } 81912420SJim.Rice@Sun.COM 82012420SJim.Rice@Sun.COM /* Both exhausted so we have a match */ 82112420SJim.Rice@Sun.COM if (p == '\0' && m == '\0') { 82212420SJim.Rice@Sun.COM best_index = i; 8230Sstevel@tonic-gate break; 82412420SJim.Rice@Sun.COM } 82512420SJim.Rice@Sun.COM 82612420SJim.Rice@Sun.COM /* 82712420SJim.Rice@Sun.COM * We have exhausted the mountpoint and the current 82812420SJim.Rice@Sun.COM * character in the path is a '/' hence the full path 82912420SJim.Rice@Sun.COM * traverses this mountpoint. 83012420SJim.Rice@Sun.COM * Record this as the best candidate so far. 83112420SJim.Rice@Sun.COM */ 83212420SJim.Rice@Sun.COM if (p == '/' && m == '\0') { 83312420SJim.Rice@Sun.COM if (score > best_score) { 83412420SJim.Rice@Sun.COM best_index = i; 83512420SJim.Rice@Sun.COM best_score = score; 83612420SJim.Rice@Sun.COM } 83712420SJim.Rice@Sun.COM } 8380Sstevel@tonic-gate } 83912420SJim.Rice@Sun.COM 84012420SJim.Rice@Sun.COM if (best_index > -1) 84112420SJim.Rice@Sun.COM match = &mount_table[best_index]; 8420Sstevel@tonic-gate } else { 8430Sstevel@tonic-gate for (i = mount_table_entries - 1; i >= 0; i--) { 8440Sstevel@tonic-gate if (tmatch = devid_matches(i, devno)) { 8450Sstevel@tonic-gate /* 8460Sstevel@tonic-gate * If executing in a zone, there might be lofs 8470Sstevel@tonic-gate * mounts for which the real mount point is 8480Sstevel@tonic-gate * invisible; accept the "best fit" for this 8490Sstevel@tonic-gate * devid. 8500Sstevel@tonic-gate */ 8510Sstevel@tonic-gate match = tmatch; 8520Sstevel@tonic-gate if (!EQ(match->mte_mount->mnt_fstype, 8535094Slling MNTTYPE_LOFS)) { 8540Sstevel@tonic-gate break; 8550Sstevel@tonic-gate } 8560Sstevel@tonic-gate } 8570Sstevel@tonic-gate } 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate if (! match) { 8600Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 8615094Slling "Could not find mount point for %s", dir); 8620Sstevel@tonic-gate return; 8630Sstevel@tonic-gate } 8640Sstevel@tonic-gate dfrp->dfr_mte = match; 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate 8670Sstevel@tonic-gate /* 8680Sstevel@tonic-gate * Execute a single FS-specific df command for all given requests 8690Sstevel@tonic-gate * Return 0 if successful, 1 otherwise. 8700Sstevel@tonic-gate */ 8710Sstevel@tonic-gate static int 8720Sstevel@tonic-gate run_fs_specific_df(struct df_request request_list[], int entries) 8730Sstevel@tonic-gate { 8740Sstevel@tonic-gate int i; 8750Sstevel@tonic-gate int argv_index; 8760Sstevel@tonic-gate char **argv; 8770Sstevel@tonic-gate size_t size; 8780Sstevel@tonic-gate pid_t pid; 8790Sstevel@tonic-gate int status; 8800Sstevel@tonic-gate char cmd_path[MAXPATHLEN]; 8810Sstevel@tonic-gate char *fstype; 8820Sstevel@tonic-gate 8830Sstevel@tonic-gate if (entries == 0) 8840Sstevel@tonic-gate return (0); 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate fstype = request_list[0].dfr_fstype; 8870Sstevel@tonic-gate 8880Sstevel@tonic-gate if (F_option && ! EQ(FSType, fstype)) 8890Sstevel@tonic-gate return (0); 8900Sstevel@tonic-gate 8910Sstevel@tonic-gate (void) sprintf(cmd_path, "%s%s/df", FS_LIBPATH, fstype); 8920Sstevel@tonic-gate /* 8930Sstevel@tonic-gate * Argv entries: 8940Sstevel@tonic-gate * 1 for the path 8950Sstevel@tonic-gate * 2 for -o <options> 8960Sstevel@tonic-gate * 1 for the generic options that we propagate 8970Sstevel@tonic-gate * 1 for the terminating NULL pointer 8980Sstevel@tonic-gate * n for the number of user-specified arguments 8990Sstevel@tonic-gate */ 9000Sstevel@tonic-gate size = (5 + entries) * sizeof (char *); 9010Sstevel@tonic-gate argv = xmalloc(size); 9020Sstevel@tonic-gate (void) memset(argv, 0, size); 9030Sstevel@tonic-gate 9040Sstevel@tonic-gate argv[0] = cmd_path; 9050Sstevel@tonic-gate argv_index = 1; 9060Sstevel@tonic-gate if (o_option) { 9070Sstevel@tonic-gate argv[argv_index++] = "-o"; 9080Sstevel@tonic-gate argv[argv_index++] = o_option_arg; 9090Sstevel@tonic-gate } 9100Sstevel@tonic-gate 9110Sstevel@tonic-gate /* 9120Sstevel@tonic-gate * Check if we need to propagate any generic options 9130Sstevel@tonic-gate */ 9140Sstevel@tonic-gate if (df_options_len > 1) 9150Sstevel@tonic-gate argv[argv_index++] = df_options; 9160Sstevel@tonic-gate 9170Sstevel@tonic-gate /* 9180Sstevel@tonic-gate * If there is a user-specified path, we pass that to the 9190Sstevel@tonic-gate * FS-specific df. Otherwise, we are guaranteed to have a mount 9200Sstevel@tonic-gate * point, since a request without a user path implies that 9210Sstevel@tonic-gate * we are reporting only on mounted file systems. 9220Sstevel@tonic-gate */ 9230Sstevel@tonic-gate for (i = 0; i < entries; i++) { 9240Sstevel@tonic-gate struct df_request *dfrp = &request_list[i]; 9250Sstevel@tonic-gate 9260Sstevel@tonic-gate argv[argv_index++] = (dfrp->dfr_cmd_arg == NULL) 9275094Slling ? DFR_MOUNT_POINT(dfrp) 9285094Slling : dfrp->dfr_cmd_arg; 9290Sstevel@tonic-gate } 9300Sstevel@tonic-gate 9310Sstevel@tonic-gate if (V_option) { 9320Sstevel@tonic-gate for (i = 0; i < argv_index-1; i++) 9330Sstevel@tonic-gate (void) printf("%s ", argv[i]); 9340Sstevel@tonic-gate (void) printf("%s\n", argv[i]); 9350Sstevel@tonic-gate return (0); 9360Sstevel@tonic-gate } 9370Sstevel@tonic-gate 9380Sstevel@tonic-gate pid = fork(); 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate if (pid == -1) { 9410Sstevel@tonic-gate errmsg(ERR_PERROR, "cannot fork process:"); 9420Sstevel@tonic-gate return (1); 9430Sstevel@tonic-gate } else if (pid == 0) { 9440Sstevel@tonic-gate (void) execv(cmd_path, argv); 9450Sstevel@tonic-gate if (errno == ENOENT) 9460Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 9475094Slling "operation not applicable for FSType %s", 9485094Slling fstype); 9490Sstevel@tonic-gate else 9500Sstevel@tonic-gate errmsg(ERR_PERROR, "cannot execute %s:", cmd_path); 9510Sstevel@tonic-gate exit(2); 9520Sstevel@tonic-gate } 9530Sstevel@tonic-gate 9540Sstevel@tonic-gate /* 9550Sstevel@tonic-gate * Reap the child 9560Sstevel@tonic-gate */ 9570Sstevel@tonic-gate for (;;) { 9580Sstevel@tonic-gate pid_t wpid = waitpid(pid, &status, 0); 9590Sstevel@tonic-gate 9600Sstevel@tonic-gate if (wpid == -1) 9610Sstevel@tonic-gate if (errno == EINTR) 9620Sstevel@tonic-gate continue; 9630Sstevel@tonic-gate else { 9640Sstevel@tonic-gate errmsg(ERR_PERROR, "waitpid error:"); 9650Sstevel@tonic-gate return (1); 9660Sstevel@tonic-gate } 9670Sstevel@tonic-gate else 9680Sstevel@tonic-gate break; 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate return ((WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : 1); 9720Sstevel@tonic-gate } 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate 9750Sstevel@tonic-gate 9760Sstevel@tonic-gate /* 9770Sstevel@tonic-gate * Remove from the request list all requests that do not apply. 9780Sstevel@tonic-gate * Notice that the subsequent processing of the requests depends on 9790Sstevel@tonic-gate * the sanity checking performed by this function. 9800Sstevel@tonic-gate */ 9810Sstevel@tonic-gate static int 9820Sstevel@tonic-gate prune_list(struct df_request request_list[], 9830Sstevel@tonic-gate size_t n_requests, 9840Sstevel@tonic-gate size_t *valid_requests) 9850Sstevel@tonic-gate { 9860Sstevel@tonic-gate size_t i; 9870Sstevel@tonic-gate size_t n_valid = 0; 9880Sstevel@tonic-gate int errors = 0; 9890Sstevel@tonic-gate 9900Sstevel@tonic-gate for (i = 0; i < n_requests; i++) { 9910Sstevel@tonic-gate struct df_request *dfrp = &request_list[i]; 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate /* 9940Sstevel@tonic-gate * Skip file systems that are not mounted if either the 9950Sstevel@tonic-gate * -l or -n options were specified. If none of these options 9960Sstevel@tonic-gate * are present, the appropriate FS-specific df will be invoked. 9970Sstevel@tonic-gate */ 9980Sstevel@tonic-gate if (! DFR_ISMOUNTEDFS(dfrp)) { 9990Sstevel@tonic-gate if (l_option || n_option) { 10000Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 10015094Slling "%s option incompatible with unmounted " 10025094Slling "special device (%s)", 10035094Slling l_option ? "-l" : "-n", dfrp->dfr_cmd_arg); 10040Sstevel@tonic-gate dfrp->dfr_valid = FALSE; 10050Sstevel@tonic-gate errors++; 10060Sstevel@tonic-gate } 10070Sstevel@tonic-gate else 10080Sstevel@tonic-gate n_valid++; 10090Sstevel@tonic-gate continue; 10100Sstevel@tonic-gate } 10110Sstevel@tonic-gate 10120Sstevel@tonic-gate /* 10130Sstevel@tonic-gate * Check for inconsistency between the argument of -F and 10140Sstevel@tonic-gate * the actual file system type. 10150Sstevel@tonic-gate * If there is an inconsistency and the user specified a 10160Sstevel@tonic-gate * path, this is an error since we are asked to interpret 10170Sstevel@tonic-gate * the path using the wrong file system type. If there is 10180Sstevel@tonic-gate * no path associated with this request, we quietly ignore it. 10190Sstevel@tonic-gate */ 10200Sstevel@tonic-gate if (F_option && ! EQ(dfrp->dfr_fstype, FSType)) { 10210Sstevel@tonic-gate dfrp->dfr_valid = FALSE; 10220Sstevel@tonic-gate if (dfrp->dfr_cmd_arg != NULL) { 10230Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 10240Sstevel@tonic-gate "Warning: %s mounted as a %s file system", 10255094Slling dfrp->dfr_cmd_arg, dfrp->dfr_fstype); 10260Sstevel@tonic-gate errors++; 10270Sstevel@tonic-gate } 10280Sstevel@tonic-gate continue; 10290Sstevel@tonic-gate } 10300Sstevel@tonic-gate 10310Sstevel@tonic-gate /* 10320Sstevel@tonic-gate * Skip remote file systems if the -l option is present 10330Sstevel@tonic-gate */ 10340Sstevel@tonic-gate if (l_option && is_remote_fs(dfrp->dfr_fstype)) { 10350Sstevel@tonic-gate if (dfrp->dfr_cmd_arg != NULL) { 10360Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 10375094Slling "Warning: %s is not a local file system", 10385094Slling dfrp->dfr_cmd_arg); 10390Sstevel@tonic-gate errors++; 10400Sstevel@tonic-gate } 10410Sstevel@tonic-gate dfrp->dfr_valid = FALSE; 10420Sstevel@tonic-gate continue; 10430Sstevel@tonic-gate } 10440Sstevel@tonic-gate 10450Sstevel@tonic-gate /* 10460Sstevel@tonic-gate * Skip file systems mounted as "ignore" unless the -a option 10470Sstevel@tonic-gate * is present, or the user explicitly specified them on 10480Sstevel@tonic-gate * the command line. 10490Sstevel@tonic-gate */ 10500Sstevel@tonic-gate if (dfrp->dfr_mte->mte_ignore && 10515094Slling ! (a_option || dfrp->dfr_cmd_arg)) { 10520Sstevel@tonic-gate dfrp->dfr_valid = FALSE; 10530Sstevel@tonic-gate continue; 10540Sstevel@tonic-gate } 10550Sstevel@tonic-gate 10560Sstevel@tonic-gate n_valid++; 10570Sstevel@tonic-gate } 10580Sstevel@tonic-gate *valid_requests = n_valid; 10590Sstevel@tonic-gate return (errors); 10600Sstevel@tonic-gate } 10610Sstevel@tonic-gate 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate /* 10640Sstevel@tonic-gate * Print the appropriate header for the requested output format. 10650Sstevel@tonic-gate * Options are checked in order of their precedence. 10660Sstevel@tonic-gate */ 10670Sstevel@tonic-gate static void 1068821Sdh145677 print_header(void) 10690Sstevel@tonic-gate { 10700Sstevel@tonic-gate if (use_scaling) { /* this comes from the -h option */ 10710Sstevel@tonic-gate int arg = 'h'; 10720Sstevel@tonic-gate 10730Sstevel@tonic-gate (void) printf("%-*s %*s %*s %*s %-*s %s\n", 10745094Slling FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 10750Sstevel@tonic-gate #ifdef XPG4 10765094Slling SCALED_WIDTH, TRANSLATE("Size"), 10775094Slling SCALED_WIDTH, TRANSLATE("Used"), 10785094Slling AVAILABLE_WIDTH, TRANSLATE("Available"), 10795094Slling CAPACITY_WIDTH, TRANSLATE("Capacity"), 10800Sstevel@tonic-gate #else 10815094Slling SCALED_WIDTH, TRANSLATE("size"), 10825094Slling SCALED_WIDTH, TRANSLATE("used"), 10835094Slling AVAILABLE_WIDTH, TRANSLATE("avail"), 10845094Slling CAPACITY_WIDTH, TRANSLATE("capacity"), 10850Sstevel@tonic-gate #endif 10865094Slling TRANSLATE("Mounted on")); 10870Sstevel@tonic-gate SET_OPTION(h); 10880Sstevel@tonic-gate return; 10890Sstevel@tonic-gate } 10900Sstevel@tonic-gate if (k_option) { 10910Sstevel@tonic-gate int arg = 'h'; 10920Sstevel@tonic-gate 10930Sstevel@tonic-gate (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"), 10945094Slling FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 10950Sstevel@tonic-gate #ifdef XPG4 10965094Slling KBYTE_WIDTH, TRANSLATE("1024-blocks"), 10975094Slling KBYTE_WIDTH, TRANSLATE("Used"), 10985094Slling KBYTE_WIDTH, TRANSLATE("Available"), 10995094Slling CAPACITY_WIDTH, TRANSLATE("Capacity"), 11000Sstevel@tonic-gate #else 11015094Slling KBYTE_WIDTH, TRANSLATE("kbytes"), 11025094Slling KBYTE_WIDTH, TRANSLATE("used"), 11035094Slling KBYTE_WIDTH, TRANSLATE("avail"), 11045094Slling CAPACITY_WIDTH, TRANSLATE("capacity"), 11050Sstevel@tonic-gate #endif 11065094Slling TRANSLATE("Mounted on")); 11070Sstevel@tonic-gate SET_OPTION(h); 11080Sstevel@tonic-gate return; 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate /* Added for XCU4 compliance */ 11110Sstevel@tonic-gate if (P_option) { 11120Sstevel@tonic-gate int arg = 'h'; 11130Sstevel@tonic-gate 11140Sstevel@tonic-gate (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"), 11155094Slling FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 11165094Slling KBYTE_WIDTH, TRANSLATE("512-blocks"), 11175094Slling KBYTE_WIDTH, TRANSLATE("Used"), 11185094Slling KBYTE_WIDTH, TRANSLATE("Available"), 11195094Slling CAPACITY_WIDTH, TRANSLATE("Capacity"), 11205094Slling TRANSLATE("Mounted on")); 11210Sstevel@tonic-gate 11220Sstevel@tonic-gate SET_OPTION(h); 11230Sstevel@tonic-gate return; 11240Sstevel@tonic-gate } 11250Sstevel@tonic-gate /* End XCU4 */ 11260Sstevel@tonic-gate if (v_option) { 11270Sstevel@tonic-gate (void) printf("%-*s %-*s %*s %*s %*s %-*s\n", 11285094Slling IBCS2_MOUNT_POINT_WIDTH, TRANSLATE("Mount Dir"), 11295094Slling IBCS2_FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 11305094Slling BLOCK_WIDTH, TRANSLATE("blocks"), 11315094Slling BLOCK_WIDTH, TRANSLATE("used"), 11325094Slling BLOCK_WIDTH, TRANSLATE("free"), 11335094Slling CAPACITY_WIDTH, TRANSLATE(" %used")); 11340Sstevel@tonic-gate return; 11350Sstevel@tonic-gate } 11360Sstevel@tonic-gate if (e_option) { 11370Sstevel@tonic-gate (void) printf(gettext("%-*s %*s\n"), 11385094Slling FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 11395094Slling BLOCK_WIDTH, TRANSLATE("ifree")); 11400Sstevel@tonic-gate return; 11410Sstevel@tonic-gate } 11420Sstevel@tonic-gate if (b_option) { 11430Sstevel@tonic-gate (void) printf(gettext("%-*s %*s\n"), 11445094Slling FILESYSTEM_WIDTH, TRANSLATE("Filesystem"), 11455094Slling BLOCK_WIDTH, TRANSLATE("avail")); 11460Sstevel@tonic-gate return; 11470Sstevel@tonic-gate } 11480Sstevel@tonic-gate } 11490Sstevel@tonic-gate 11500Sstevel@tonic-gate 11510Sstevel@tonic-gate /* 11520Sstevel@tonic-gate * Convert an unsigned long long to a string representation and place the 11530Sstevel@tonic-gate * result in the caller-supplied buffer. 11540Sstevel@tonic-gate * The given number is in units of "unit_from" size, but the 11550Sstevel@tonic-gate * converted number will be in units of "unit_to" size. The unit sizes 11560Sstevel@tonic-gate * must be powers of 2. 11570Sstevel@tonic-gate * The value "(unsigned long long)-1" is a special case and is always 11580Sstevel@tonic-gate * converted to "-1". 11590Sstevel@tonic-gate * Returns a pointer to the caller-supplied buffer. 11600Sstevel@tonic-gate */ 11610Sstevel@tonic-gate static char * 11620Sstevel@tonic-gate number_to_string( 11630Sstevel@tonic-gate char *buf, /* put the result here */ 11640Sstevel@tonic-gate unsigned long long number, /* convert this number */ 11650Sstevel@tonic-gate int unit_from, /* from units of this size */ 11660Sstevel@tonic-gate int unit_to) /* to units of this size */ 11670Sstevel@tonic-gate { 11680Sstevel@tonic-gate if ((long long)number == (long long)-1) 11690Sstevel@tonic-gate (void) strcpy(buf, "-1"); 11700Sstevel@tonic-gate else { 11710Sstevel@tonic-gate if (unit_from == unit_to) 11720Sstevel@tonic-gate (void) sprintf(buf, "%llu", number); 11730Sstevel@tonic-gate else if (unit_from < unit_to) 11740Sstevel@tonic-gate (void) sprintf(buf, "%llu", 11750Sstevel@tonic-gate number / (unsigned long long)(unit_to / unit_from)); 11760Sstevel@tonic-gate else 11770Sstevel@tonic-gate (void) sprintf(buf, "%llu", 11780Sstevel@tonic-gate number * (unsigned long long)(unit_from / unit_to)); 11790Sstevel@tonic-gate } 11800Sstevel@tonic-gate return (buf); 11810Sstevel@tonic-gate } 11820Sstevel@tonic-gate 11830Sstevel@tonic-gate /* 11840Sstevel@tonic-gate * Convert an unsigned long long to a string representation and place the 11850Sstevel@tonic-gate * result in the caller-supplied buffer. 11860Sstevel@tonic-gate * The given number is in units of "unit_from" size, 11870Sstevel@tonic-gate * this will first be converted to a number in 1024 or 1000 byte size, 11880Sstevel@tonic-gate * depending on the scaling factor. 11890Sstevel@tonic-gate * Then the number is scaled down until it is small enough to be in a good 11900Sstevel@tonic-gate * human readable format i.e. in the range 0 thru scale-1. 11910Sstevel@tonic-gate * If it's smaller than 10 there's room enough to provide one decimal place. 11920Sstevel@tonic-gate * The value "(unsigned long long)-1" is a special case and is always 11930Sstevel@tonic-gate * converted to "-1". 11940Sstevel@tonic-gate * Returns a pointer to the caller-supplied buffer. 11950Sstevel@tonic-gate */ 11960Sstevel@tonic-gate static char * 11970Sstevel@tonic-gate number_to_scaled_string( 11980Sstevel@tonic-gate numbuf_t buf, /* put the result here */ 11990Sstevel@tonic-gate unsigned long long number, /* convert this number */ 12000Sstevel@tonic-gate int unit_from, 12010Sstevel@tonic-gate int scale) 12020Sstevel@tonic-gate { 12030Sstevel@tonic-gate unsigned long long save = 0; 12040Sstevel@tonic-gate char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */ 12050Sstevel@tonic-gate char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */ 12060Sstevel@tonic-gate 12070Sstevel@tonic-gate if ((long long)number == (long long)-1) { 12080Sstevel@tonic-gate (void) strcpy(buf, "-1"); 12090Sstevel@tonic-gate return (buf); 12100Sstevel@tonic-gate } 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate /* 12130Sstevel@tonic-gate * Convert number from unit_from to given scale (1024 or 1000). 12140Sstevel@tonic-gate * This means multiply number by unit_from and divide by scale. 12150Sstevel@tonic-gate * 12160Sstevel@tonic-gate * Would like to multiply by unit_from and then divide by scale, 12170Sstevel@tonic-gate * but if the first multiplication would overflow, then need to 12180Sstevel@tonic-gate * divide by scale and then multiply by unit_from. 12190Sstevel@tonic-gate */ 12200Sstevel@tonic-gate if (number > (UINT64_MAX / (unsigned long long)unit_from)) { 12210Sstevel@tonic-gate number = (number / (unsigned long long)scale) * 12220Sstevel@tonic-gate (unsigned long long)unit_from; 12230Sstevel@tonic-gate } else { 12240Sstevel@tonic-gate number = (number * (unsigned long long)unit_from) / 12250Sstevel@tonic-gate (unsigned long long)scale; 12260Sstevel@tonic-gate } 12270Sstevel@tonic-gate 12280Sstevel@tonic-gate /* 12290Sstevel@tonic-gate * Now we have number as a count of scale units. 12300Sstevel@tonic-gate * Stop scaling when we reached exa bytes, then something is 12310Sstevel@tonic-gate * probably wrong with our number. 12320Sstevel@tonic-gate */ 12330Sstevel@tonic-gate 12340Sstevel@tonic-gate while ((number >= scale) && (*uom != 'E')) { 12350Sstevel@tonic-gate uom++; /* next unit of measurement */ 12360Sstevel@tonic-gate save = number; 12370Sstevel@tonic-gate number = (number + (scale / 2)) / scale; 12380Sstevel@tonic-gate } 12390Sstevel@tonic-gate /* check if we should output a decimal place after the point */ 12400Sstevel@tonic-gate if (save && ((save / scale) < 10)) { 12410Sstevel@tonic-gate /* sprintf() will round for us */ 12420Sstevel@tonic-gate float fnum = (float)save / scale; 12430Sstevel@tonic-gate (void) sprintf(buf, "%2.1f%c", fnum, *uom); 12440Sstevel@tonic-gate } else { 12450Sstevel@tonic-gate (void) sprintf(buf, "%4llu%c", number, *uom); 12460Sstevel@tonic-gate } 12470Sstevel@tonic-gate return (buf); 12480Sstevel@tonic-gate } 12490Sstevel@tonic-gate 1250789Sahrens /* 1251789Sahrens * The statvfs() implementation allows us to return only two values, the total 1252789Sahrens * number of blocks and the number of blocks free. The equation 'used = total - 1253789Sahrens * free' will not work for ZFS filesystems, due to the nature of pooled storage. 1254789Sahrens * We choose to return values in the statvfs structure that will produce correct 1255789Sahrens * results for 'used' and 'available', but not 'total'. This function will open 1256789Sahrens * the underlying ZFS dataset if necessary and get the real value. 1257789Sahrens */ 1258789Sahrens static void 1259789Sahrens adjust_total_blocks(struct df_request *dfrp, fsblkcnt64_t *total, 1260789Sahrens uint64_t blocksize) 1261789Sahrens { 1262789Sahrens char *dataset, *slash; 12635378Sck153898 boolean_t first = TRUE; 12645378Sck153898 uint64_t quota = 0; 1265789Sahrens 12665378Sck153898 if (strcmp(DFR_FSTYPE(dfrp), MNTTYPE_ZFS) != 0 || !load_libzfs()) 1267789Sahrens return; 1268789Sahrens 1269789Sahrens /* 1270789Sahrens * We want to get the total size for this filesystem as bounded by any 1271789Sahrens * quotas. In order to do this, we start at the current filesystem and 12725378Sck153898 * work upwards looking for the smallest quota. When we reach the 12735378Sck153898 * pool itself, the quota is the amount used plus the amount 1274789Sahrens * available. 1275789Sahrens */ 1276789Sahrens if ((dataset = strdup(DFR_SPECIAL(dfrp))) == NULL) 1277789Sahrens return; 1278789Sahrens 1279789Sahrens slash = dataset + strlen(dataset); 12805378Sck153898 while (slash != NULL) { 12815378Sck153898 zfs_handle_t *zhp; 12825378Sck153898 uint64_t this_quota; 12835378Sck153898 1284789Sahrens *slash = '\0'; 1285789Sahrens 12865378Sck153898 zhp = _zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET); 12875378Sck153898 if (zhp == NULL) 12885378Sck153898 break; 12895378Sck153898 12905378Sck153898 /* true at first iteration of loop */ 12915378Sck153898 if (first) { 12925378Sck153898 quota = _zfs_prop_get_int(zhp, ZFS_PROP_REFQUOTA); 12935378Sck153898 if (quota == 0) 12945378Sck153898 quota = UINT64_MAX; 12955378Sck153898 first = FALSE; 1296789Sahrens } 1297789Sahrens 12985378Sck153898 this_quota = _zfs_prop_get_int(zhp, ZFS_PROP_QUOTA); 12995378Sck153898 if (this_quota && this_quota < quota) 13005378Sck153898 quota = this_quota; 13015378Sck153898 13025378Sck153898 /* true at last iteration of loop */ 13035378Sck153898 if ((slash = strrchr(dataset, '/')) == NULL) { 13045378Sck153898 uint64_t size; 13055378Sck153898 13065378Sck153898 size = _zfs_prop_get_int(zhp, ZFS_PROP_USED) + 13075378Sck153898 _zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 13085378Sck153898 if (size < quota) 13095378Sck153898 quota = size; 1310789Sahrens } 1311789Sahrens 1312789Sahrens _zfs_close(zhp); 1313789Sahrens } 1314789Sahrens 131511443SSanjeev.Bagewadi@Sun.COM /* 131611443SSanjeev.Bagewadi@Sun.COM * Modify total only if we managed to get some stats from libzfs. 131711443SSanjeev.Bagewadi@Sun.COM */ 131811443SSanjeev.Bagewadi@Sun.COM if (quota != 0) 131911443SSanjeev.Bagewadi@Sun.COM *total = quota / blocksize; 1320789Sahrens free(dataset); 1321789Sahrens } 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate /* 13240Sstevel@tonic-gate * The output will appear properly columnized regardless of the names of 13250Sstevel@tonic-gate * the various fields 13260Sstevel@tonic-gate */ 13270Sstevel@tonic-gate static void 13280Sstevel@tonic-gate g_output(struct df_request *dfrp, struct statvfs64 *fsp) 13290Sstevel@tonic-gate { 13300Sstevel@tonic-gate fsblkcnt64_t available_blocks = fsp->f_bavail; 1331789Sahrens fsblkcnt64_t total_blocks = fsp->f_blocks; 13320Sstevel@tonic-gate numbuf_t total_blocks_buf; 13330Sstevel@tonic-gate numbuf_t total_files_buf; 13340Sstevel@tonic-gate numbuf_t free_blocks_buf; 13350Sstevel@tonic-gate numbuf_t available_blocks_buf; 13360Sstevel@tonic-gate numbuf_t free_files_buf; 13370Sstevel@tonic-gate numbuf_t fname_buf; 13380Sstevel@tonic-gate char *temp_buf; 13390Sstevel@tonic-gate 13400Sstevel@tonic-gate #define DEFINE_STR_LEN(var) \ 13410Sstevel@tonic-gate static char *var##_str; \ 13420Sstevel@tonic-gate static size_t var##_len 13430Sstevel@tonic-gate 13440Sstevel@tonic-gate #define SET_STR_LEN(name, var)\ 13450Sstevel@tonic-gate if (! var##_str) {\ 13460Sstevel@tonic-gate var##_str = TRANSLATE(name); \ 13470Sstevel@tonic-gate var##_len = strlen(var##_str); \ 13480Sstevel@tonic-gate } 13490Sstevel@tonic-gate 13500Sstevel@tonic-gate DEFINE_STR_LEN(block_size); 13510Sstevel@tonic-gate DEFINE_STR_LEN(frag_size); 13520Sstevel@tonic-gate DEFINE_STR_LEN(total_blocks); 13530Sstevel@tonic-gate DEFINE_STR_LEN(free_blocks); 13540Sstevel@tonic-gate DEFINE_STR_LEN(available); 13550Sstevel@tonic-gate DEFINE_STR_LEN(total_files); 13560Sstevel@tonic-gate DEFINE_STR_LEN(free_files); 13570Sstevel@tonic-gate DEFINE_STR_LEN(fstype); 13580Sstevel@tonic-gate DEFINE_STR_LEN(fsys_id); 13590Sstevel@tonic-gate DEFINE_STR_LEN(fname); 13600Sstevel@tonic-gate DEFINE_STR_LEN(flag); 13610Sstevel@tonic-gate 13620Sstevel@tonic-gate /* 13630Sstevel@tonic-gate * TRANSLATION_NOTE 13640Sstevel@tonic-gate * The first argument of each of the following macro invocations is a 13650Sstevel@tonic-gate * string that needs to be translated. 13660Sstevel@tonic-gate */ 13670Sstevel@tonic-gate SET_STR_LEN("block size", block_size); 13680Sstevel@tonic-gate SET_STR_LEN("frag size", frag_size); 13690Sstevel@tonic-gate SET_STR_LEN("total blocks", total_blocks); 13700Sstevel@tonic-gate SET_STR_LEN("free blocks", free_blocks); 13710Sstevel@tonic-gate SET_STR_LEN("available", available); 13720Sstevel@tonic-gate SET_STR_LEN("total files", total_files); 13730Sstevel@tonic-gate SET_STR_LEN("free files", free_files); 13740Sstevel@tonic-gate SET_STR_LEN("fstype", fstype); 13750Sstevel@tonic-gate SET_STR_LEN("filesys id", fsys_id); 13760Sstevel@tonic-gate SET_STR_LEN("filename length", fname); 13770Sstevel@tonic-gate SET_STR_LEN("flag", flag); 13780Sstevel@tonic-gate 13790Sstevel@tonic-gate #define NCOL1_WIDTH (int)MAX3(BLOCK_WIDTH, NFILES_WIDTH, FSTYPE_WIDTH) 13800Sstevel@tonic-gate #define NCOL2_WIDTH (int)MAX3(BLOCK_WIDTH, FSID_WIDTH, FLAG_WIDTH) + 2 13810Sstevel@tonic-gate #define NCOL3_WIDTH (int)MAX3(BSIZE_WIDTH, BLOCK_WIDTH, NAMELEN_WIDTH) 13820Sstevel@tonic-gate #define NCOL4_WIDTH (int)MAX(FRAGSIZE_WIDTH, NFILES_WIDTH) 13830Sstevel@tonic-gate 13840Sstevel@tonic-gate #define SCOL1_WIDTH (int)MAX3(total_blocks_len, free_files_len, fstype_len) 13850Sstevel@tonic-gate #define SCOL2_WIDTH (int)MAX3(free_blocks_len, fsys_id_len, flag_len) 13860Sstevel@tonic-gate #define SCOL3_WIDTH (int)MAX3(block_size_len, available_len, fname_len) 13870Sstevel@tonic-gate #define SCOL4_WIDTH (int)MAX(frag_size_len, total_files_len) 13880Sstevel@tonic-gate 13890Sstevel@tonic-gate temp_buf = xmalloc( 13900Sstevel@tonic-gate MAX(MOUNT_POINT_WIDTH, strlen(DFR_MOUNT_POINT(dfrp))) 13910Sstevel@tonic-gate + MAX(SPECIAL_DEVICE_WIDTH, strlen(DFR_SPECIAL(dfrp))) 13920Sstevel@tonic-gate + 20); /* plus slop - nulls & formatting */ 13930Sstevel@tonic-gate (void) sprintf(temp_buf, "%-*s(%-*s):", 13945094Slling MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 13955094Slling SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp)); 13960Sstevel@tonic-gate 13970Sstevel@tonic-gate (void) printf("%-*s %*lu %-*s %*lu %-*s\n", 13985094Slling NCOL1_WIDTH + 1 + SCOL1_WIDTH + 1 + NCOL2_WIDTH + 1 + SCOL2_WIDTH, 13995094Slling temp_buf, 14005094Slling NCOL3_WIDTH, fsp->f_bsize, SCOL3_WIDTH, block_size_str, 14015094Slling NCOL4_WIDTH, fsp->f_frsize, SCOL4_WIDTH, frag_size_str); 14020Sstevel@tonic-gate free(temp_buf); 14030Sstevel@tonic-gate 14040Sstevel@tonic-gate /* 14050Sstevel@tonic-gate * Adjust available_blocks value - it can be less than 0 on 14060Sstevel@tonic-gate * a 4.x file system. Reset it to 0 in order to avoid printing 14070Sstevel@tonic-gate * negative numbers. 14080Sstevel@tonic-gate */ 14090Sstevel@tonic-gate if ((long long)available_blocks < (long long)0) 14100Sstevel@tonic-gate available_blocks = (fsblkcnt64_t)0; 14110Sstevel@tonic-gate 1412789Sahrens adjust_total_blocks(dfrp, &total_blocks, fsp->f_frsize); 1413789Sahrens 14140Sstevel@tonic-gate (void) printf("%*s %-*s %*s %-*s %*s %-*s %*s %-*s\n", 14155094Slling NCOL1_WIDTH, number_to_string(total_blocks_buf, 14165094Slling total_blocks, fsp->f_frsize, 512), 14175094Slling SCOL1_WIDTH, total_blocks_str, 14185094Slling NCOL2_WIDTH, number_to_string(free_blocks_buf, 14195094Slling fsp->f_bfree, fsp->f_frsize, 512), 14205094Slling SCOL2_WIDTH, free_blocks_str, 14215094Slling NCOL3_WIDTH, number_to_string(available_blocks_buf, 14225094Slling available_blocks, fsp->f_frsize, 512), 14235094Slling SCOL3_WIDTH, available_str, 14245094Slling NCOL4_WIDTH, number_to_string(total_files_buf, 14255094Slling fsp->f_files, 1, 1), 14265094Slling SCOL4_WIDTH, total_files_str); 14270Sstevel@tonic-gate 14280Sstevel@tonic-gate (void) printf("%*s %-*s %*lu %-*s %s\n", 14295094Slling NCOL1_WIDTH, number_to_string(free_files_buf, 14305094Slling fsp->f_ffree, 1, 1), 14315094Slling SCOL1_WIDTH, free_files_str, 14325094Slling NCOL2_WIDTH, fsp->f_fsid, SCOL2_WIDTH, fsys_id_str, 14335094Slling fsp->f_fstr); 14340Sstevel@tonic-gate 14350Sstevel@tonic-gate (void) printf("%*s %-*s %#*.*lx %-*s %*s %-*s\n\n", 14365094Slling NCOL1_WIDTH, fsp->f_basetype, SCOL1_WIDTH, fstype_str, 14375094Slling NCOL2_WIDTH, NCOL2_WIDTH-2, fsp->f_flag, SCOL2_WIDTH, flag_str, 14385094Slling NCOL3_WIDTH, number_to_string(fname_buf, 14395094Slling (unsigned long long)fsp->f_namemax, 1, 1), 14405094Slling SCOL3_WIDTH, fname_str); 14410Sstevel@tonic-gate } 14420Sstevel@tonic-gate 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate static void 14450Sstevel@tonic-gate k_output(struct df_request *dfrp, struct statvfs64 *fsp) 14460Sstevel@tonic-gate { 14470Sstevel@tonic-gate fsblkcnt64_t total_blocks = fsp->f_blocks; 14480Sstevel@tonic-gate fsblkcnt64_t free_blocks = fsp->f_bfree; 14490Sstevel@tonic-gate fsblkcnt64_t available_blocks = fsp->f_bavail; 14500Sstevel@tonic-gate fsblkcnt64_t used_blocks; 14510Sstevel@tonic-gate char *file_system = DFR_SPECIAL(dfrp); 14520Sstevel@tonic-gate numbuf_t total_blocks_buf; 14530Sstevel@tonic-gate numbuf_t used_blocks_buf; 14540Sstevel@tonic-gate numbuf_t available_blocks_buf; 14550Sstevel@tonic-gate char capacity_buf[LINEBUF_SIZE]; 14560Sstevel@tonic-gate 14570Sstevel@tonic-gate /* 14580Sstevel@tonic-gate * If the free block count is -1, don't trust anything but the total 14590Sstevel@tonic-gate * number of blocks. 14600Sstevel@tonic-gate */ 14610Sstevel@tonic-gate if (free_blocks == (fsblkcnt64_t)-1) { 14620Sstevel@tonic-gate used_blocks = (fsblkcnt64_t)-1; 14630Sstevel@tonic-gate (void) strcpy(capacity_buf, " 100%"); 14640Sstevel@tonic-gate } else { 14650Sstevel@tonic-gate fsblkcnt64_t reserved_blocks = free_blocks - available_blocks; 14660Sstevel@tonic-gate 14670Sstevel@tonic-gate used_blocks = total_blocks - free_blocks; 14680Sstevel@tonic-gate 14690Sstevel@tonic-gate /* 14700Sstevel@tonic-gate * The capacity estimation is bogus when available_blocks is 0 14710Sstevel@tonic-gate * and the super-user has allocated more space. The reason 14720Sstevel@tonic-gate * is that reserved_blocks is inaccurate in that case, because 14730Sstevel@tonic-gate * when the super-user allocates space, free_blocks is updated 14740Sstevel@tonic-gate * but available_blocks is not (since it can't drop below 0). 14750Sstevel@tonic-gate * 14760Sstevel@tonic-gate * XCU4 and POSIX.2 require that any fractional result of the 14770Sstevel@tonic-gate * capacity estimation be rounded to the next highest integer, 14780Sstevel@tonic-gate * hence the addition of 0.5. 14790Sstevel@tonic-gate */ 14800Sstevel@tonic-gate (void) sprintf(capacity_buf, "%5.0f%%", 14815094Slling (total_blocks == 0) ? 0.0 : 14825094Slling ((double)used_blocks / 14835094Slling (double)(total_blocks - reserved_blocks)) 14845094Slling * 100.0 + 0.5); 14850Sstevel@tonic-gate } 14860Sstevel@tonic-gate 14870Sstevel@tonic-gate /* 14880Sstevel@tonic-gate * The available_blocks can be less than 0 on a 4.x file system. 14890Sstevel@tonic-gate * Reset it to 0 in order to avoid printing negative numbers. 14900Sstevel@tonic-gate */ 14910Sstevel@tonic-gate if ((long long)available_blocks < (long long)0) 14920Sstevel@tonic-gate available_blocks = (fsblkcnt64_t)0; 14930Sstevel@tonic-gate /* 14940Sstevel@tonic-gate * Print long special device names (usually NFS mounts) in a line 14950Sstevel@tonic-gate * by themselves when the output is directed to a terminal. 14960Sstevel@tonic-gate */ 14970Sstevel@tonic-gate if (tty_output && strlen(file_system) > (size_t)FILESYSTEM_WIDTH) { 14980Sstevel@tonic-gate (void) printf("%s\n", file_system); 14990Sstevel@tonic-gate file_system = ""; 15000Sstevel@tonic-gate } 15010Sstevel@tonic-gate 1502789Sahrens adjust_total_blocks(dfrp, &total_blocks, fsp->f_frsize); 1503789Sahrens 15040Sstevel@tonic-gate if (use_scaling) { /* comes from the -h option */ 15050Sstevel@tonic-gate (void) printf("%-*s %*s %*s %*s %-*s %-s\n", 15065094Slling FILESYSTEM_WIDTH, file_system, 15075094Slling SCALED_WIDTH, number_to_scaled_string(total_blocks_buf, 15085094Slling total_blocks, fsp->f_frsize, scale), 15095094Slling SCALED_WIDTH, number_to_scaled_string(used_blocks_buf, 15105094Slling used_blocks, fsp->f_frsize, scale), 15115094Slling AVAILABLE_WIDTH, number_to_scaled_string(available_blocks_buf, 15125094Slling available_blocks, fsp->f_frsize, scale), 15135094Slling CAPACITY_WIDTH, capacity_buf, 15145094Slling DFR_MOUNT_POINT(dfrp)); 15150Sstevel@tonic-gate return; 15160Sstevel@tonic-gate } 15170Sstevel@tonic-gate 15180Sstevel@tonic-gate if (v_option) { 15190Sstevel@tonic-gate (void) printf("%-*.*s %-*.*s %*lld %*lld %*lld %-.*s\n", 15205094Slling IBCS2_MOUNT_POINT_WIDTH, IBCS2_MOUNT_POINT_WIDTH, 15215094Slling DFR_MOUNT_POINT(dfrp), 15225094Slling IBCS2_FILESYSTEM_WIDTH, IBCS2_FILESYSTEM_WIDTH, file_system, 15235094Slling BLOCK_WIDTH, total_blocks, 15245094Slling BLOCK_WIDTH, used_blocks, 15255094Slling BLOCK_WIDTH, available_blocks, 15265094Slling CAPACITY_WIDTH, capacity_buf); 15270Sstevel@tonic-gate return; 15280Sstevel@tonic-gate } 15290Sstevel@tonic-gate 15300Sstevel@tonic-gate if (P_option && !k_option) { 15310Sstevel@tonic-gate (void) printf("%-*s %*s %*s %*s %-*s %-s\n", 15325094Slling FILESYSTEM_WIDTH, file_system, 15335094Slling KBYTE_WIDTH, number_to_string(total_blocks_buf, 15345094Slling total_blocks, fsp->f_frsize, 512), 15355094Slling KBYTE_WIDTH, number_to_string(used_blocks_buf, 15365094Slling used_blocks, fsp->f_frsize, 512), 15375094Slling KBYTE_WIDTH, number_to_string(available_blocks_buf, 15385094Slling available_blocks, fsp->f_frsize, 512), 15395094Slling CAPACITY_WIDTH, capacity_buf, 15405094Slling DFR_MOUNT_POINT(dfrp)); 15410Sstevel@tonic-gate } else { 15420Sstevel@tonic-gate (void) printf("%-*s %*s %*s %*s %-*s %-s\n", 15435094Slling FILESYSTEM_WIDTH, file_system, 15445094Slling KBYTE_WIDTH, number_to_string(total_blocks_buf, 15455094Slling total_blocks, fsp->f_frsize, 1024), 15465094Slling KBYTE_WIDTH, number_to_string(used_blocks_buf, 15475094Slling used_blocks, fsp->f_frsize, 1024), 15485094Slling KBYTE_WIDTH, number_to_string(available_blocks_buf, 15495094Slling available_blocks, fsp->f_frsize, 1024), 15505094Slling CAPACITY_WIDTH, capacity_buf, 15515094Slling DFR_MOUNT_POINT(dfrp)); 15520Sstevel@tonic-gate } 15530Sstevel@tonic-gate } 15540Sstevel@tonic-gate 15550Sstevel@tonic-gate /* 15560Sstevel@tonic-gate * The following is for internationalization support. 15570Sstevel@tonic-gate */ 15580Sstevel@tonic-gate static bool_int strings_initialized; 15590Sstevel@tonic-gate static char *files_str; 15600Sstevel@tonic-gate static char *blocks_str; 15610Sstevel@tonic-gate static char *total_str; 15620Sstevel@tonic-gate static char *kilobytes_str; 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate static void 1565821Sdh145677 strings_init(void) 15660Sstevel@tonic-gate { 15670Sstevel@tonic-gate total_str = TRANSLATE("total"); 1568*12836Srich.burridge@oracle.com files_str = TRANSLATE("files"); 15690Sstevel@tonic-gate blocks_str = TRANSLATE("blocks"); 15700Sstevel@tonic-gate kilobytes_str = TRANSLATE("kilobytes"); 15710Sstevel@tonic-gate strings_initialized = TRUE; 15720Sstevel@tonic-gate } 15730Sstevel@tonic-gate 15740Sstevel@tonic-gate #define STRINGS_INIT() if (!strings_initialized) strings_init() 15750Sstevel@tonic-gate 15760Sstevel@tonic-gate 15770Sstevel@tonic-gate static void 15780Sstevel@tonic-gate t_output(struct df_request *dfrp, struct statvfs64 *fsp) 15790Sstevel@tonic-gate { 1580789Sahrens fsblkcnt64_t total_blocks = fsp->f_blocks; 15810Sstevel@tonic-gate numbuf_t total_blocks_buf; 15820Sstevel@tonic-gate numbuf_t total_files_buf; 15830Sstevel@tonic-gate numbuf_t free_blocks_buf; 15840Sstevel@tonic-gate numbuf_t free_files_buf; 15850Sstevel@tonic-gate 15860Sstevel@tonic-gate STRINGS_INIT(); 15870Sstevel@tonic-gate 1588789Sahrens adjust_total_blocks(dfrp, &total_blocks, fsp->f_frsize); 1589789Sahrens 15900Sstevel@tonic-gate (void) printf("%-*s(%-*s): %*s %s %*s %s\n", 15915094Slling MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 15925094Slling SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 15935094Slling BLOCK_WIDTH, number_to_string(free_blocks_buf, 15945094Slling fsp->f_bfree, fsp->f_frsize, 512), 15955094Slling blocks_str, 15965094Slling NFILES_WIDTH, number_to_string(free_files_buf, 15975094Slling fsp->f_ffree, 1, 1), 15985094Slling files_str); 15990Sstevel@tonic-gate /* 16000Sstevel@tonic-gate * The total column used to use the same space as the mnt pt & special 16010Sstevel@tonic-gate * dev fields. However, this doesn't work with massive special dev 16020Sstevel@tonic-gate * fields * (eg > 500 chars) causing an enormous amount of white space 16030Sstevel@tonic-gate * before the total column (see bug 4100411). So the code was 16040Sstevel@tonic-gate * simplified to set the total column at the usual gap. 16050Sstevel@tonic-gate * This had the side effect of fixing a bug where the previously 16060Sstevel@tonic-gate * used static buffer was overflowed by the same massive special dev. 16070Sstevel@tonic-gate */ 16080Sstevel@tonic-gate (void) printf("%*s: %*s %s %*s %s\n", 16095094Slling MNT_SPEC_WIDTH, total_str, 16105094Slling BLOCK_WIDTH, number_to_string(total_blocks_buf, 16115094Slling total_blocks, fsp->f_frsize, 512), 16125094Slling blocks_str, 16135094Slling NFILES_WIDTH, number_to_string(total_files_buf, 16145094Slling fsp->f_files, 1, 1), 16155094Slling files_str); 16160Sstevel@tonic-gate } 16170Sstevel@tonic-gate 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate static void 16200Sstevel@tonic-gate eb_output(struct df_request *dfrp, struct statvfs64 *fsp) 16210Sstevel@tonic-gate { 16220Sstevel@tonic-gate numbuf_t free_files_buf; 16230Sstevel@tonic-gate numbuf_t free_kbytes_buf; 16240Sstevel@tonic-gate 16250Sstevel@tonic-gate STRINGS_INIT(); 16260Sstevel@tonic-gate 16270Sstevel@tonic-gate (void) printf("%-*s(%-*s): %*s %s\n", 16285094Slling MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 16295094Slling SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 16305094Slling MAX(KBYTE_WIDTH, NFILES_WIDTH), 16315094Slling number_to_string(free_kbytes_buf, 16325094Slling fsp->f_bfree, fsp->f_frsize, 1024), 16335094Slling kilobytes_str); 16340Sstevel@tonic-gate (void) printf("%-*s(%-*s): %*s %s\n", 16355094Slling MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 16365094Slling SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 16375094Slling MAX(NFILES_WIDTH, NFILES_WIDTH), 16385094Slling number_to_string(free_files_buf, fsp->f_ffree, 1, 1), 16395094Slling files_str); 16400Sstevel@tonic-gate } 16410Sstevel@tonic-gate 16420Sstevel@tonic-gate 16430Sstevel@tonic-gate static void 16440Sstevel@tonic-gate e_output(struct df_request *dfrp, struct statvfs64 *fsp) 16450Sstevel@tonic-gate { 16460Sstevel@tonic-gate numbuf_t free_files_buf; 16470Sstevel@tonic-gate 16480Sstevel@tonic-gate (void) printf("%-*s %*s\n", 16495094Slling FILESYSTEM_WIDTH, DFR_SPECIAL(dfrp), 16505094Slling NFILES_WIDTH, 16515094Slling number_to_string(free_files_buf, fsp->f_ffree, 1, 1)); 16520Sstevel@tonic-gate } 16530Sstevel@tonic-gate 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate static void 16560Sstevel@tonic-gate b_output(struct df_request *dfrp, struct statvfs64 *fsp) 16570Sstevel@tonic-gate { 16580Sstevel@tonic-gate numbuf_t free_blocks_buf; 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate (void) printf("%-*s %*s\n", 16615094Slling FILESYSTEM_WIDTH, DFR_SPECIAL(dfrp), 16625094Slling BLOCK_WIDTH, number_to_string(free_blocks_buf, 16635094Slling fsp->f_bfree, fsp->f_frsize, 1024)); 16640Sstevel@tonic-gate } 16650Sstevel@tonic-gate 16660Sstevel@tonic-gate 16670Sstevel@tonic-gate /* ARGSUSED */ 16680Sstevel@tonic-gate static void 16690Sstevel@tonic-gate n_output(struct df_request *dfrp, struct statvfs64 *fsp) 16700Sstevel@tonic-gate { 16710Sstevel@tonic-gate (void) printf("%-*s: %-*s\n", 16725094Slling MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 16735094Slling FSTYPE_WIDTH, dfrp->dfr_fstype); 16740Sstevel@tonic-gate } 16750Sstevel@tonic-gate 16760Sstevel@tonic-gate 16770Sstevel@tonic-gate static void 16780Sstevel@tonic-gate default_output(struct df_request *dfrp, struct statvfs64 *fsp) 16790Sstevel@tonic-gate { 16800Sstevel@tonic-gate numbuf_t free_blocks_buf; 16810Sstevel@tonic-gate numbuf_t free_files_buf; 16820Sstevel@tonic-gate 16830Sstevel@tonic-gate STRINGS_INIT(); 16840Sstevel@tonic-gate 16850Sstevel@tonic-gate (void) printf("%-*s(%-*s):%*s %s %*s %s\n", 16865094Slling MOUNT_POINT_WIDTH, DFR_MOUNT_POINT(dfrp), 16875094Slling SPECIAL_DEVICE_WIDTH, DFR_SPECIAL(dfrp), 16885094Slling BLOCK_WIDTH, number_to_string(free_blocks_buf, 16895094Slling fsp->f_bfree, fsp->f_frsize, 512), 16905094Slling blocks_str, 16915094Slling NFILES_WIDTH, number_to_string(free_files_buf, 16925094Slling fsp->f_ffree, 1, 1), 16935094Slling files_str); 16940Sstevel@tonic-gate } 16950Sstevel@tonic-gate 16960Sstevel@tonic-gate 16970Sstevel@tonic-gate /* ARGSUSED */ 16980Sstevel@tonic-gate static void 16990Sstevel@tonic-gate V_output(struct df_request *dfrp, struct statvfs64 *fsp) 17000Sstevel@tonic-gate { 17010Sstevel@tonic-gate char temp_buf[LINEBUF_SIZE]; 17020Sstevel@tonic-gate 17030Sstevel@tonic-gate if (df_options_len > 1) 17040Sstevel@tonic-gate (void) strcat(strcpy(temp_buf, df_options), " "); 17050Sstevel@tonic-gate else 17060Sstevel@tonic-gate temp_buf[0] = NUL; 17070Sstevel@tonic-gate 17080Sstevel@tonic-gate (void) printf("%s -F %s %s%s\n", 17095094Slling program_name, dfrp->dfr_fstype, temp_buf, 17105094Slling dfrp->dfr_cmd_arg ? dfrp->dfr_cmd_arg: DFR_SPECIAL(dfrp)); 17110Sstevel@tonic-gate } 17120Sstevel@tonic-gate 17130Sstevel@tonic-gate 17140Sstevel@tonic-gate /* 17150Sstevel@tonic-gate * This function is used to sort the array of df_requests according to fstype 17160Sstevel@tonic-gate */ 17170Sstevel@tonic-gate static int 17180Sstevel@tonic-gate df_reqcomp(const void *p1, const void *p2) 17190Sstevel@tonic-gate { 17200Sstevel@tonic-gate int v = strcmp(DFRP(p1)->dfr_fstype, DFRP(p2)->dfr_fstype); 17210Sstevel@tonic-gate 17220Sstevel@tonic-gate if (v != 0) 17230Sstevel@tonic-gate return (v); 17240Sstevel@tonic-gate else 17250Sstevel@tonic-gate return (DFRP(p1)->dfr_index - DFRP(p2)->dfr_index); 17260Sstevel@tonic-gate } 17270Sstevel@tonic-gate 17280Sstevel@tonic-gate 17290Sstevel@tonic-gate static void 17300Sstevel@tonic-gate vfs_error(char *file, int status) 17310Sstevel@tonic-gate { 17320Sstevel@tonic-gate if (status == VFS_TOOLONG) 17330Sstevel@tonic-gate errmsg(ERR_NOFLAGS, "a line in %s exceeds %d characters", 17345094Slling file, MNT_LINE_MAX); 17350Sstevel@tonic-gate else if (status == VFS_TOOMANY) 17360Sstevel@tonic-gate errmsg(ERR_NOFLAGS, "a line in %s has too many fields", file); 17370Sstevel@tonic-gate else if (status == VFS_TOOFEW) 17380Sstevel@tonic-gate errmsg(ERR_NOFLAGS, "a line in %s has too few fields", file); 17390Sstevel@tonic-gate else 17400Sstevel@tonic-gate errmsg(ERR_NOFLAGS, "error while reading %s: %d", file, status); 17410Sstevel@tonic-gate } 17420Sstevel@tonic-gate 17430Sstevel@tonic-gate 17440Sstevel@tonic-gate /* 17450Sstevel@tonic-gate * Try to determine the fstype for the specified block device. 17460Sstevel@tonic-gate * Return in order of decreasing preference: 17470Sstevel@tonic-gate * file system type from vfstab 17480Sstevel@tonic-gate * file system type as specified by -F option 17490Sstevel@tonic-gate * default file system type 17500Sstevel@tonic-gate */ 17510Sstevel@tonic-gate static char * 17520Sstevel@tonic-gate find_fstype(char *special) 17530Sstevel@tonic-gate { 17540Sstevel@tonic-gate struct vfstab vtab; 17550Sstevel@tonic-gate FILE *fp; 17560Sstevel@tonic-gate int status; 17570Sstevel@tonic-gate char *vfstab_file = VFS_TAB; 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate fp = xfopen(vfstab_file); 17600Sstevel@tonic-gate status = getvfsspec(fp, &vtab, special); 17610Sstevel@tonic-gate (void) fclose(fp); 17620Sstevel@tonic-gate if (status > 0) 17630Sstevel@tonic-gate vfs_error(vfstab_file, status); 17640Sstevel@tonic-gate 17650Sstevel@tonic-gate if (status == 0) { 17660Sstevel@tonic-gate if (F_option && ! EQ(FSType, vtab.vfs_fstype)) 17670Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 17680Sstevel@tonic-gate "warning: %s is of type %s", special, vtab.vfs_fstype); 17690Sstevel@tonic-gate return (new_string(vtab.vfs_fstype)); 17700Sstevel@tonic-gate } 17710Sstevel@tonic-gate else 17720Sstevel@tonic-gate return (F_option ? FSType : default_fstype(special)); 17730Sstevel@tonic-gate } 17740Sstevel@tonic-gate 17750Sstevel@tonic-gate /* 17760Sstevel@tonic-gate * When this function returns, the following fields are filled for all 17770Sstevel@tonic-gate * valid entries in the requests[] array: 17780Sstevel@tonic-gate * dfr_mte (if the file system is mounted) 17790Sstevel@tonic-gate * dfr_fstype 17800Sstevel@tonic-gate * dfr_index 17810Sstevel@tonic-gate * 17820Sstevel@tonic-gate * The function returns the number of errors that occurred while building 17830Sstevel@tonic-gate * the request list. 17840Sstevel@tonic-gate */ 17850Sstevel@tonic-gate static int 17860Sstevel@tonic-gate create_request_list( 17870Sstevel@tonic-gate int argc, 17880Sstevel@tonic-gate char *argv[], 17890Sstevel@tonic-gate struct df_request *requests_p[], 17900Sstevel@tonic-gate size_t *request_count) 17910Sstevel@tonic-gate { 17920Sstevel@tonic-gate struct df_request *requests; 17930Sstevel@tonic-gate struct df_request *dfrp; 17940Sstevel@tonic-gate size_t size; 17950Sstevel@tonic-gate size_t i; 17960Sstevel@tonic-gate size_t request_index = 0; 17970Sstevel@tonic-gate size_t max_requests; 17980Sstevel@tonic-gate int errors = 0; 17990Sstevel@tonic-gate 18000Sstevel@tonic-gate /* 18010Sstevel@tonic-gate * If no args, use the mounted file systems, otherwise use the 18020Sstevel@tonic-gate * user-specified arguments. 18030Sstevel@tonic-gate */ 18040Sstevel@tonic-gate if (argc == 0) { 18050Sstevel@tonic-gate mtab_read_file(); 18060Sstevel@tonic-gate max_requests = mount_table_entries; 18070Sstevel@tonic-gate } else 18080Sstevel@tonic-gate max_requests = argc; 18090Sstevel@tonic-gate 18100Sstevel@tonic-gate size = max_requests * sizeof (struct df_request); 18110Sstevel@tonic-gate requests = xmalloc(size); 18120Sstevel@tonic-gate (void) memset(requests, 0, size); 18130Sstevel@tonic-gate 18140Sstevel@tonic-gate if (argc == 0) { 18150Sstevel@tonic-gate /* 18160Sstevel@tonic-gate * If -Z wasn't specified, we skip mounts in other 18170Sstevel@tonic-gate * zones. This obviously is a noop in a non-global 18180Sstevel@tonic-gate * zone. 18190Sstevel@tonic-gate */ 18200Sstevel@tonic-gate boolean_t showall = (getzoneid() != GLOBAL_ZONEID) || Z_option; 18210Sstevel@tonic-gate struct zone_summary *zsp; 18220Sstevel@tonic-gate 18230Sstevel@tonic-gate if (!showall) { 18240Sstevel@tonic-gate zsp = fs_get_zone_summaries(); 18250Sstevel@tonic-gate if (zsp == NULL) 18260Sstevel@tonic-gate errmsg(ERR_FATAL, 18270Sstevel@tonic-gate "unable to retrieve list of zones"); 18280Sstevel@tonic-gate } 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate for (i = 0; i < mount_table_entries; i++) { 18310Sstevel@tonic-gate struct extmnttab *mtp = mount_table[i].mte_mount; 18320Sstevel@tonic-gate 18330Sstevel@tonic-gate if (EQ(mtp->mnt_fstype, MNTTYPE_SWAP)) 18340Sstevel@tonic-gate continue; 18350Sstevel@tonic-gate 18360Sstevel@tonic-gate if (!showall) { 18370Sstevel@tonic-gate if (fs_mount_in_other_zone(zsp, 18380Sstevel@tonic-gate mtp->mnt_mountp)) 18390Sstevel@tonic-gate continue; 18400Sstevel@tonic-gate } 18410Sstevel@tonic-gate dfrp = &requests[request_index++]; 18420Sstevel@tonic-gate dfrp->dfr_mte = &mount_table[i]; 18430Sstevel@tonic-gate dfrp->dfr_fstype = mtp->mnt_fstype; 18440Sstevel@tonic-gate dfrp->dfr_index = i; 18450Sstevel@tonic-gate dfrp->dfr_valid = TRUE; 18460Sstevel@tonic-gate } 18470Sstevel@tonic-gate } else { 18480Sstevel@tonic-gate struct stat64 *arg_stat; /* array of stat structures */ 18490Sstevel@tonic-gate bool_int *valid_stat; /* which structures are valid */ 18500Sstevel@tonic-gate 18510Sstevel@tonic-gate arg_stat = xmalloc(argc * sizeof (struct stat64)); 18520Sstevel@tonic-gate valid_stat = xmalloc(argc * sizeof (bool_int)); 18530Sstevel@tonic-gate 18540Sstevel@tonic-gate /* 18550Sstevel@tonic-gate * Obtain stat64 information for each argument before 18560Sstevel@tonic-gate * constructing the list of mounted file systems. By 18570Sstevel@tonic-gate * touching all these places we force the automounter 18580Sstevel@tonic-gate * to establish any mounts required to access the arguments, 18590Sstevel@tonic-gate * so that the corresponding mount table entries will exist 18600Sstevel@tonic-gate * when we look for them. 18610Sstevel@tonic-gate * It is still possible that the automounter may timeout 18620Sstevel@tonic-gate * mounts between the time we read the mount table and the 18630Sstevel@tonic-gate * time we process the request. Even in that case, when 18640Sstevel@tonic-gate * we issue the statvfs64(2) for the mount point, the file 18650Sstevel@tonic-gate * system will be mounted again. The only problem will 18660Sstevel@tonic-gate * occur if the automounter maps change in the meantime 18670Sstevel@tonic-gate * and the mount point is eliminated. 18680Sstevel@tonic-gate */ 18690Sstevel@tonic-gate for (i = 0; i < argc; i++) 18700Sstevel@tonic-gate valid_stat[i] = (stat64(argv[i], &arg_stat[i]) == 0); 18710Sstevel@tonic-gate 18720Sstevel@tonic-gate mtab_read_file(); 18730Sstevel@tonic-gate 18740Sstevel@tonic-gate for (i = 0; i < argc; i++) { 18750Sstevel@tonic-gate char *arg = argv[i]; 18760Sstevel@tonic-gate 18770Sstevel@tonic-gate dfrp = &requests[request_index]; 18780Sstevel@tonic-gate 18790Sstevel@tonic-gate dfrp->dfr_index = request_index; 18800Sstevel@tonic-gate dfrp->dfr_cmd_arg = arg; 18810Sstevel@tonic-gate 18820Sstevel@tonic-gate if (valid_stat[i]) { 188312420SJim.Rice@Sun.COM dfrp->dfr_fstype = arg_stat[i].st_fstype; 18840Sstevel@tonic-gate if (S_ISBLK(arg_stat[i].st_mode)) { 18850Sstevel@tonic-gate bdev_mount_entry(dfrp); 18860Sstevel@tonic-gate dfrp->dfr_valid = TRUE; 18870Sstevel@tonic-gate } else if (S_ISDIR(arg_stat[i].st_mode) || 18885094Slling S_ISREG(arg_stat[i].st_mode) || 18895094Slling S_ISFIFO(arg_stat[i].st_mode)) { 18900Sstevel@tonic-gate path_mount_entry(dfrp, 18915094Slling arg_stat[i].st_dev); 18920Sstevel@tonic-gate if (! DFR_ISMOUNTEDFS(dfrp)) { 18930Sstevel@tonic-gate errors++; 18940Sstevel@tonic-gate continue; 18950Sstevel@tonic-gate } 18960Sstevel@tonic-gate dfrp->dfr_valid = TRUE; 18970Sstevel@tonic-gate } 18980Sstevel@tonic-gate } else { 18990Sstevel@tonic-gate resource_mount_entry(dfrp); 19000Sstevel@tonic-gate dfrp->dfr_valid = DFR_ISMOUNTEDFS(dfrp); 19010Sstevel@tonic-gate } 19020Sstevel@tonic-gate 19030Sstevel@tonic-gate /* 19040Sstevel@tonic-gate * If we haven't managed to verify that the request 19050Sstevel@tonic-gate * is valid, we must have gotten a bad argument. 19060Sstevel@tonic-gate */ 19070Sstevel@tonic-gate if (!dfrp->dfr_valid) { 19080Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 19095094Slling "(%-10s) not a block device, directory or " 19105094Slling "mounted resource", arg); 19110Sstevel@tonic-gate errors++; 19120Sstevel@tonic-gate continue; 19130Sstevel@tonic-gate } 19140Sstevel@tonic-gate 19150Sstevel@tonic-gate /* 19160Sstevel@tonic-gate * Determine the file system type. 19170Sstevel@tonic-gate */ 19180Sstevel@tonic-gate if (DFR_ISMOUNTEDFS(dfrp)) 19190Sstevel@tonic-gate dfrp->dfr_fstype = 19205094Slling dfrp->dfr_mte->mte_mount->mnt_fstype; 19210Sstevel@tonic-gate else 19220Sstevel@tonic-gate dfrp->dfr_fstype = 19235094Slling find_fstype(dfrp->dfr_cmd_arg); 19240Sstevel@tonic-gate 19250Sstevel@tonic-gate request_index++; 19260Sstevel@tonic-gate } 19270Sstevel@tonic-gate } 19280Sstevel@tonic-gate *requests_p = requests; 19290Sstevel@tonic-gate *request_count = request_index; 19300Sstevel@tonic-gate return (errors); 19310Sstevel@tonic-gate } 19320Sstevel@tonic-gate 19330Sstevel@tonic-gate 19340Sstevel@tonic-gate /* 19350Sstevel@tonic-gate * Select the appropriate function and flags to use for output. 19360Sstevel@tonic-gate * Notice that using both -e and -b options produces a different form of 19370Sstevel@tonic-gate * output than either of those two options alone; this is the behavior of 19380Sstevel@tonic-gate * the SVR4 df. 19390Sstevel@tonic-gate */ 19400Sstevel@tonic-gate static struct df_output * 1941821Sdh145677 select_output(void) 19420Sstevel@tonic-gate { 19430Sstevel@tonic-gate static struct df_output dfo; 19440Sstevel@tonic-gate 19450Sstevel@tonic-gate /* 19460Sstevel@tonic-gate * The order of checking options follows the option precedence 19470Sstevel@tonic-gate * rules as they are listed in the man page. 19480Sstevel@tonic-gate */ 19490Sstevel@tonic-gate if (use_scaling) { /* comes from the -h option */ 19500Sstevel@tonic-gate dfo.dfo_func = k_output; 19510Sstevel@tonic-gate dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 19520Sstevel@tonic-gate } else if (V_option) { 19530Sstevel@tonic-gate dfo.dfo_func = V_output; 19540Sstevel@tonic-gate dfo.dfo_flags = DFO_NOFLAGS; 19550Sstevel@tonic-gate } else if (g_option) { 19560Sstevel@tonic-gate dfo.dfo_func = g_output; 19570Sstevel@tonic-gate dfo.dfo_flags = DFO_STATVFS; 19580Sstevel@tonic-gate } else if (k_option || P_option || v_option) { 19590Sstevel@tonic-gate dfo.dfo_func = k_output; 19600Sstevel@tonic-gate dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 19610Sstevel@tonic-gate } else if (t_option) { 19620Sstevel@tonic-gate dfo.dfo_func = t_output; 19630Sstevel@tonic-gate dfo.dfo_flags = DFO_STATVFS; 19640Sstevel@tonic-gate } else if (b_option && e_option) { 19650Sstevel@tonic-gate dfo.dfo_func = eb_output; 19660Sstevel@tonic-gate dfo.dfo_flags = DFO_STATVFS; 19670Sstevel@tonic-gate } else if (b_option) { 19680Sstevel@tonic-gate dfo.dfo_func = b_output; 19690Sstevel@tonic-gate dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 19700Sstevel@tonic-gate } else if (e_option) { 19710Sstevel@tonic-gate dfo.dfo_func = e_output; 19720Sstevel@tonic-gate dfo.dfo_flags = DFO_HEADER + DFO_STATVFS; 19730Sstevel@tonic-gate } else if (n_option) { 19740Sstevel@tonic-gate dfo.dfo_func = n_output; 19750Sstevel@tonic-gate dfo.dfo_flags = DFO_NOFLAGS; 19760Sstevel@tonic-gate } else { 19770Sstevel@tonic-gate dfo.dfo_func = default_output; 19780Sstevel@tonic-gate dfo.dfo_flags = DFO_STATVFS; 19790Sstevel@tonic-gate } 19800Sstevel@tonic-gate return (&dfo); 19810Sstevel@tonic-gate } 19820Sstevel@tonic-gate 19830Sstevel@tonic-gate 19840Sstevel@tonic-gate /* 19850Sstevel@tonic-gate * The (argc,argv) pair contains all the non-option arguments 19860Sstevel@tonic-gate */ 19870Sstevel@tonic-gate static void 19880Sstevel@tonic-gate do_df(int argc, char *argv[]) 19890Sstevel@tonic-gate { 19900Sstevel@tonic-gate size_t i; 19910Sstevel@tonic-gate struct df_request *requests; /* array of requests */ 19920Sstevel@tonic-gate size_t n_requests; 19930Sstevel@tonic-gate struct df_request *dfrp; 19940Sstevel@tonic-gate int errors; 19950Sstevel@tonic-gate 19960Sstevel@tonic-gate errors = create_request_list(argc, argv, &requests, &n_requests); 19970Sstevel@tonic-gate 19980Sstevel@tonic-gate if (n_requests == 0) 19990Sstevel@tonic-gate exit(errors); 20000Sstevel@tonic-gate 20010Sstevel@tonic-gate /* 20020Sstevel@tonic-gate * If we are going to run the FSType-specific df command, 20030Sstevel@tonic-gate * rearrange the requests so that we can issue a single command 20040Sstevel@tonic-gate * per file system type. 20050Sstevel@tonic-gate */ 20060Sstevel@tonic-gate if (o_option) { 20070Sstevel@tonic-gate size_t j; 20080Sstevel@tonic-gate 20090Sstevel@tonic-gate /* 20100Sstevel@tonic-gate * qsort is not a stable sorting method (i.e. requests of 20110Sstevel@tonic-gate * the same file system type may be swapped, and hence appear 20120Sstevel@tonic-gate * in the output in a different order from the one in which 20130Sstevel@tonic-gate * they were listed in the command line). In order to force 20140Sstevel@tonic-gate * stability, we use the dfr_index field which is unique 20150Sstevel@tonic-gate * for each request. 20160Sstevel@tonic-gate */ 20170Sstevel@tonic-gate qsort(requests, 20185094Slling n_requests, sizeof (struct df_request), df_reqcomp); 20190Sstevel@tonic-gate for (i = 0; i < n_requests; i = j) { 20200Sstevel@tonic-gate char *fstype = requests[i].dfr_fstype; 20210Sstevel@tonic-gate 20220Sstevel@tonic-gate for (j = i+1; j < n_requests; j++) 20230Sstevel@tonic-gate if (! EQ(fstype, requests[j].dfr_fstype)) 20240Sstevel@tonic-gate break; 20250Sstevel@tonic-gate 20260Sstevel@tonic-gate /* 20270Sstevel@tonic-gate * At this point, requests in the range [i,j) are 20280Sstevel@tonic-gate * of the same type. 20290Sstevel@tonic-gate * 20300Sstevel@tonic-gate * If the -F option was used, and the user specified 20310Sstevel@tonic-gate * arguments, the filesystem types must match 20320Sstevel@tonic-gate * 20330Sstevel@tonic-gate * XXX: the alternative of doing this check here is to 20340Sstevel@tonic-gate * invoke prune_list, but then we have to 20350Sstevel@tonic-gate * modify this code to ignore invalid requests. 20360Sstevel@tonic-gate */ 20370Sstevel@tonic-gate if (F_option && ! EQ(fstype, FSType)) { 20380Sstevel@tonic-gate size_t k; 20390Sstevel@tonic-gate 20400Sstevel@tonic-gate for (k = i; k < j; k++) { 20410Sstevel@tonic-gate dfrp = &requests[k]; 20420Sstevel@tonic-gate if (dfrp->dfr_cmd_arg != NULL) { 20430Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 20445094Slling "Warning: %s mounted as a " 20455094Slling "%s file system", 20465094Slling dfrp->dfr_cmd_arg, 20475094Slling dfrp->dfr_fstype); 20480Sstevel@tonic-gate errors++; 20490Sstevel@tonic-gate } 20500Sstevel@tonic-gate } 20510Sstevel@tonic-gate } else 20520Sstevel@tonic-gate errors += run_fs_specific_df(&requests[i], j-i); 20530Sstevel@tonic-gate } 20540Sstevel@tonic-gate } else { 20550Sstevel@tonic-gate size_t valid_requests; 20560Sstevel@tonic-gate 20570Sstevel@tonic-gate /* 20580Sstevel@tonic-gate * We have to prune the request list to avoid printing a header 20590Sstevel@tonic-gate * if there are no valid requests 20600Sstevel@tonic-gate */ 20610Sstevel@tonic-gate errors += prune_list(requests, n_requests, &valid_requests); 20620Sstevel@tonic-gate 20630Sstevel@tonic-gate if (valid_requests) { 20640Sstevel@tonic-gate struct df_output *dfop = select_output(); 20650Sstevel@tonic-gate 20660Sstevel@tonic-gate /* indicates if we already printed out a header line */ 20670Sstevel@tonic-gate int printed_header = 0; 20680Sstevel@tonic-gate 20690Sstevel@tonic-gate for (i = 0; i < n_requests; i++) { 20700Sstevel@tonic-gate dfrp = &requests[i]; 20710Sstevel@tonic-gate if (! dfrp->dfr_valid) 20720Sstevel@tonic-gate continue; 20730Sstevel@tonic-gate 20740Sstevel@tonic-gate /* 20750Sstevel@tonic-gate * If we don't have a mount point, 20760Sstevel@tonic-gate * this must be a block device. 20770Sstevel@tonic-gate */ 20780Sstevel@tonic-gate if (DFR_ISMOUNTEDFS(dfrp)) { 20790Sstevel@tonic-gate struct statvfs64 stvfs; 20800Sstevel@tonic-gate 20810Sstevel@tonic-gate if ((dfop->dfo_flags & DFO_STATVFS) && 20825094Slling statvfs64(DFR_MOUNT_POINT(dfrp), 20835094Slling &stvfs) == -1) { 20840Sstevel@tonic-gate errmsg(ERR_PERROR, 20855094Slling "cannot statvfs %s:", 20865094Slling DFR_MOUNT_POINT(dfrp)); 20870Sstevel@tonic-gate errors++; 20880Sstevel@tonic-gate continue; 20890Sstevel@tonic-gate } 20900Sstevel@tonic-gate if ((!printed_header) && 20910Sstevel@tonic-gate (dfop->dfo_flags & DFO_HEADER)) { 20920Sstevel@tonic-gate print_header(); 20930Sstevel@tonic-gate printed_header = 1; 20940Sstevel@tonic-gate } 20950Sstevel@tonic-gate 20960Sstevel@tonic-gate (*dfop->dfo_func)(dfrp, &stvfs); 20970Sstevel@tonic-gate } else { 20980Sstevel@tonic-gate /* 20990Sstevel@tonic-gate * -h option only works for 21000Sstevel@tonic-gate * mounted filesystems 21010Sstevel@tonic-gate */ 21020Sstevel@tonic-gate if (use_scaling) { 21030Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 21040Sstevel@tonic-gate "-h option incompatible with unmounted special device (%s)", 21050Sstevel@tonic-gate dfrp->dfr_cmd_arg); 21060Sstevel@tonic-gate errors++; 21070Sstevel@tonic-gate continue; 21080Sstevel@tonic-gate } 21090Sstevel@tonic-gate errors += run_fs_specific_df(dfrp, 1); 21100Sstevel@tonic-gate } 21110Sstevel@tonic-gate } 21120Sstevel@tonic-gate } 21130Sstevel@tonic-gate } 21140Sstevel@tonic-gate exit(errors); 21150Sstevel@tonic-gate } 21160Sstevel@tonic-gate 21170Sstevel@tonic-gate 21180Sstevel@tonic-gate /* 21190Sstevel@tonic-gate * The rest of this file implements the devnm command 21200Sstevel@tonic-gate */ 21210Sstevel@tonic-gate 21220Sstevel@tonic-gate static char * 21230Sstevel@tonic-gate find_dev_name(char *file, dev_t dev) 21240Sstevel@tonic-gate { 21250Sstevel@tonic-gate struct df_request dfreq; 21260Sstevel@tonic-gate 21270Sstevel@tonic-gate dfreq.dfr_cmd_arg = file; 21280Sstevel@tonic-gate dfreq.dfr_fstype = 0; 21290Sstevel@tonic-gate dfreq.dfr_mte = NULL; 21300Sstevel@tonic-gate path_mount_entry(&dfreq, dev); 21310Sstevel@tonic-gate return (DFR_ISMOUNTEDFS(&dfreq) ? DFR_SPECIAL(&dfreq) : NULL); 21320Sstevel@tonic-gate } 21330Sstevel@tonic-gate 21340Sstevel@tonic-gate 21350Sstevel@tonic-gate static void 21360Sstevel@tonic-gate do_devnm(int argc, char *argv[]) 21370Sstevel@tonic-gate { 21380Sstevel@tonic-gate int arg; 21390Sstevel@tonic-gate int errors = 0; 21400Sstevel@tonic-gate char *dev_name; 21410Sstevel@tonic-gate 21420Sstevel@tonic-gate if (argc == 1) 21430Sstevel@tonic-gate errmsg(ERR_NONAME, "Usage: %s name ...", DEVNM_CMD); 21440Sstevel@tonic-gate 21450Sstevel@tonic-gate mtab_read_file(); 21460Sstevel@tonic-gate 21470Sstevel@tonic-gate for (arg = 1; arg < argc; arg++) { 21480Sstevel@tonic-gate char *file = argv[arg]; 21490Sstevel@tonic-gate struct stat64 st; 21500Sstevel@tonic-gate 21510Sstevel@tonic-gate if (stat64(file, &st) == -1) { 21520Sstevel@tonic-gate errmsg(ERR_PERROR, "%s: ", file); 21530Sstevel@tonic-gate errors++; 21540Sstevel@tonic-gate continue; 21550Sstevel@tonic-gate } 21560Sstevel@tonic-gate 21570Sstevel@tonic-gate if (! is_remote_fs(st.st_fstype) && 21585094Slling ! EQ(st.st_fstype, MNTTYPE_TMPFS) && 21595094Slling (dev_name = find_dev_name(file, st.st_dev))) 21600Sstevel@tonic-gate (void) printf("%s %s\n", dev_name, file); 21610Sstevel@tonic-gate else 21620Sstevel@tonic-gate errmsg(ERR_NOFLAGS, 21635094Slling "%s not found", file); 21640Sstevel@tonic-gate } 21650Sstevel@tonic-gate exit(errors); 21660Sstevel@tonic-gate /* NOTREACHED */ 21670Sstevel@tonic-gate } 2168