df.c revision 90a1a13596fed05f78bee9f85d396628cc310803
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
*/
#include <dlfcn.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <locale.h>
#include <libintl.h>
#include <stdlib.h>
#include <ftw.h>
#include <errno.h>
#include <unistd.h>
#include <sys/int_limits.h>
#include <libzfs.h>
#include "fslib.h"
extern char *default_fstype(char *);
/*
* General notice:
* String pointers in this code may point to statically allocated memory
* or dynamically allocated memory. Furthermore, a dynamically allocated
* string may be pointed to by more than one pointer. This does not pose
* a problem because malloc'ed memory is never free'd (so we don't need
* to remember which pointers point to malloc'ed memory).
*/
/*
* TRANSLATION_NOTE
* Only strings passed as arguments to the TRANSLATE macro need to
* be translated.
*/
#ifndef MNTTYPE_LOFS
#define MNTTYPE_LOFS "lofs"
#endif
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MAX_OPTIONS 36
#define N_FSTYPES 20
#define MSGBUF_SIZE 1024
#define DEVNM_CMD "devnm"
#define FS_LIBPATH "/usr/lib/fs/"
#define NUL '\0'
#define FALSE 0
#define TRUE 1
/*
* Formatting constants
*/
#define FILESYSTEM_WIDTH 20
#define MOUNT_POINT_WIDTH 19
#define SPECIAL_DEVICE_WIDTH 18
#define FSTYPE_WIDTH 8
#define BLOCK_WIDTH 8
#define NFILES_WIDTH 8
#define KBYTE_WIDTH 11
#define AVAILABLE_WIDTH 10
#define SCALED_WIDTH 6
#define CAPACITY_WIDTH 9
#define BSIZE_WIDTH 6
#define FRAGSIZE_WIDTH 7
#define FSID_WIDTH 7
#define FLAG_WIDTH 8
#define NAMELEN_WIDTH 7
/*
* Flags for the errmsg() function
*/
#define ERR_NOFLAGS 0x0
/* as a prefix */
/* message */
/* the message */
/* message */
#define NUMBER_WIDTH 40
/*
* A numbuf_t is used when converting a number to a string representation
*/
typedef char numbuf_t[ NUMBER_WIDTH ];
/*
* We use bool_int instead of int to make clear which variables are
* supposed to be boolean
*/
typedef int bool_int;
struct mtab_entry {
};
struct df_request {
char *dfr_cmd_arg; /* what the user specified */
struct mtab_entry *dfr_mte;
char *dfr_fstype;
int dfr_index; /* to make qsort stable */
};
#define DFRP(p) ((struct df_request *)(p))
struct df_output {
int dfo_flags;
};
/*
* Output flags
*/
#define DFO_NOFLAGS 0x0
static char *program_name;
static char *o_option_arg; /* arg to the -o option */
static char *FSType;
/* to use as a terminator */
/*
* The following three variables support an in-memory copy of the mount table
* to speedup searches.
*/
static size_t mount_table_entries;
static size_t mount_table_allocated_entries;
static bool_int tty_output;
static bool_int use_scaling;
static int scale;
static void usage(void);
static void do_devnm(int, char **);
static void do_df(int, char **) __NORETURN;
static void parse_options(int, char **);
static char *basename(char *);
static libzfs_handle_t *(*_libzfs_init)(void);
static void (*_zfs_close)(zfs_handle_t *);
static libzfs_handle_t *g_zfs;
/*
* Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
* packages. A basic utility such as df shouldn't depend on optional
* filesystems.
*/
static boolean_t
load_libzfs(void)
{
void *hdl;
if (_libzfs_init != NULL)
"libzfs_init");
_zfs_prop_get_int = (uint64_t (*)())
if (_libzfs_init != NULL) {
g_zfs = _libzfs_init();
}
}
}
int
{
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/*
* The k_option implies SunOS 4.x compatibility: when the special
* device name is too long the line will be split except when the
* output has been redirected.
* This is also valid for the -h option.
*/
/* NOTREACHED */
}
/*
* Prints an error message to stderr.
*/
/* VARARGS2 */
static void
{
char buf[MSGBUF_SIZE];
int cc;
int offset;
if (flags & ERR_NONAME)
offset = 0;
else
if (flags & ERR_PERROR) {
}
usage();
exit(1);
}
static void
usage(void)
{
"Usage: %s [-F FSType] [-abeghklmntPVvZ]"
" [-o FSType-specific_options]"
" [directory | block_device | resource]", program_name);
exit(1);
/* NOTREACHED */
}
static char *
new_string(char *s)
{
char *p = NULL;
if (s) {
p = strdup(s);
if (p)
return (p);
/* NOTREACHED */
}
return (p);
}
/*
* Allocate memory using malloc but terminate if the allocation fails
*/
static void *
{
if (p)
return (p);
/* NOTREACHED */
return (NULL);
}
/*
* Allocate memory using realloc but terminate if the allocation fails
*/
static void *
{
if (p)
return (p);
/* NOTREACHED */
return (NULL);
}
/*
* fopen the specified file for reading but terminate if the fopen fails
*/
static FILE *
{
return (fp);
}
/*
* Read remote file system types from REMOTE_FS into the
* remote_fstypes array.
*/
static void
init_remote_fs(void)
{
char line_buf[LINEBUF_SIZE];
size_t fstype_index = 0;
"Warning: can't open %s, ignored", REMOTE_FS);
return;
}
char buf[LINEBUF_SIZE];
if (fstype_index == N_FSTYPES)
break;
}
}
/*
* Returns TRUE if fstype is a remote file system type;
* otherwise, returns FALSE.
*/
static int
is_remote_fs(char *fstype)
{
char **p;
static bool_int remote_fs_initialized;
if (! remote_fs_initialized) {
}
for (p = remote_fstypes; *p; p++)
return (TRUE);
return (FALSE);
}
static char *
basename(char *s)
{
char *p = strrchr(s, '/');
return (p ? p+1 : s);
}
/*
* Create a new "struct extmnttab" and make sure that its fields point
* to malloc'ed memory
*/
static struct extmnttab *
{
return (new);
}
static void
{
if (status == MNT_TOOLONG)
else if (status == MNT_TOOMANY)
"a line in %s has too many fields", mtab_file);
else if (status == MNT_TOOFEW)
"a line in %s has too few fields", mtab_file);
else
exit(1);
/* NOTREACHED */
}
/*
* Read the mount table from the specified file.
* We keep the table in memory for faster lookups.
*/
static void
mtab_read_file(void)
{
int status;
mount_table_entries = 0;
mount_table_allocated_entries * sizeof (struct mtab_entry));
== 0) {
struct mtab_entry *mtep;
sizeof (struct mtab_entry));
}
MNTOPT_IGNORE) != NULL);
}
return;
/* NOTREACHED */
}
/*
* We use this macro when we want to record the option for the purpose of
* passing it to the FS-specific df
*/
static void
{
int arg;
opterr = 0; /* getopt shouldn't complain about unknown options */
if (arg == 'F') {
if (F_option)
"more than one FSType specified");
F_option = 1;
SET_OPTION(P);
SET_OPTION(a);
SET_OPTION(b);
SET_OPTION(e);
SET_OPTION(g);
} else if (arg == 'h') {
use_scaling = TRUE;
scale = 1024;
SET_OPTION(k);
SET_OPTION(l);
SET_OPTION(m);
SET_OPTION(n);
SET_OPTION(t);
} else if (arg == 'o') {
if (o_option)
"the -o option can only be specified once");
} else if (arg == 'Z') {
SET_OPTION(Z);
} else if (arg == '?') {
}
}
/*
* Option sanity checks
*/
if (use_scaling && o_option)
}
/*
* Check if the user-specified argument is a resource name.
* A resource name is whatever is placed in the mnt_special field of
* struct mnttab. In the case of NFS, a resource name has the form
* hostname:pathname
* We try to find an exact match between the user-specified argument
* and the mnt_special field of a mount table entry.
* We also use the heuristic of removing the basename from the user-specified
* argument and repeating the test until we get a match. This works
* fine for NFS but may fail for other remote file system types. However,
* it is guaranteed that the function will not fail if the user specifies
* the exact resource name.
* If successful, this function sets the 'dfr_mte' field of '*dfrp'
*/
static void
{
char *name;
/*
* We need our own copy since we will modify the string
*/
for (;;) {
char *p;
int i;
/*
* Compare against all known mount points.
* We start from the most recent mount, which is at the
* end of the array.
*/
for (i = mount_table_entries - 1; i >= 0; i--) {
break;
}
}
/*
* Remove the last component of the pathname.
* If there is no such component, this is not a resource name.
*/
if (p == NULL)
break;
*p = NUL;
}
}
/*
* Try to match the command line argument which is a block special device
* with the special device of one of the mounted file systems.
* If one is found, set the appropriate field of 'dfrp' to the mount
* table entry.
*/
static void
{
int i;
/*
* Compare against all known mount points.
* We start from the most recent mount, which is at the
* end of the array.
*/
for (i = mount_table_entries - 1; i >= 0; i--) {
break;
}
}
}
static struct mtab_entry *
{
/* int len = strlen(mtp->mnt_mountp); */
return (NULL);
/*
* check if device numbers match. If there is a cached device number
* in the mtab_entry, use it, otherwise get the device number
* either from the mnttab entry or by stat'ing the mount point.
*/
if (! mtep->mte_dev_is_valid) {
if (dev == 0)
return (NULL);
} else {
}
}
}
return (mtep);
}
return (NULL);
}
/*
* Find the mount point under which the user-specified path resides
* and set the 'dfr_mte' field of '*dfrp' to point to the mount table entry.
*/
static void
{
char dirpath[MAXPATHLEN];
int i;
/*
* Expand the given path to get a canonical version (i.e. an absolute
* path without symbolic links).
*/
return;
}
/*
* If the mnt point is lofs, search from the top of entries from
* For non-lofs mount points, return the first entry from the bottom
*/
char p, m;
int score;
int best_score = 0;
int best_index = -1;
for (i = 0; i < mount_table_entries; i++) {
continue;
score = 0;
/*
* Count the number of matching characters
* until either path or mountpoint is exhausted
*/
score++;
if (p == '\0' || m == '\0')
break;
}
/* Both exhausted so we have a match */
if (p == '\0' && m == '\0') {
best_index = i;
break;
}
/*
* We have exhausted the mountpoint and the current
* character in the path is a '/' hence the full path
* traverses this mountpoint.
* Record this as the best candidate so far.
*/
if (p == '/' && m == '\0') {
if (score > best_score) {
best_index = i;
best_score = score;
}
}
}
if (best_index > -1)
} else {
for (i = mount_table_entries - 1; i >= 0; i--) {
/*
* If executing in a zone, there might be lofs
* mounts for which the real mount point is
* invisible; accept the "best fit" for this
* devid.
*/
MNTTYPE_LOFS)) {
break;
}
}
}
}
if (! match) {
"Could not find mount point for %s", dir);
return;
}
}
/*
* Execute a single FS-specific df command for all given requests
* Return 0 if successful, 1 otherwise.
*/
static int
{
int i;
int argv_index;
char **argv;
int status;
char cmd_path[MAXPATHLEN];
char *fstype;
if (entries == 0)
return (0);
return (0);
/*
* Argv entries:
* 1 for the path
* 2 for -o <options>
* 1 for the generic options that we propagate
* 1 for the terminating NULL pointer
* n for the number of user-specified arguments
*/
argv_index = 1;
if (o_option) {
}
/*
* Check if we need to propagate any generic options
*/
if (df_options_len > 1)
/*
* If there is a user-specified path, we pass that to the
* FS-specific df. Otherwise, we are guaranteed to have a mount
* point, since a request without a user path implies that
* we are reporting only on mounted file systems.
*/
for (i = 0; i < entries; i++) {
: dfrp->dfr_cmd_arg;
}
if (V_option) {
for (i = 0; i < argv_index-1; i++)
return (0);
}
if (pid == -1) {
return (1);
} else if (pid == 0) {
"operation not applicable for FSType %s",
fstype);
else
exit(2);
}
/*
* Reap the child
*/
for (;;) {
if (wpid == -1)
continue;
else {
return (1);
}
else
break;
}
}
/*
* Remove from the request list all requests that do not apply.
* Notice that the subsequent processing of the requests depends on
* the sanity checking performed by this function.
*/
static int
{
size_t i;
int errors = 0;
for (i = 0; i < n_requests; i++) {
/*
* Skip file systems that are not mounted if either the
* -l or -n options were specified. If none of these options
* are present, the appropriate FS-specific df will be invoked.
*/
if (! DFR_ISMOUNTEDFS(dfrp)) {
"%s option incompatible with unmounted "
"special device (%s)",
errors++;
}
else
n_valid++;
continue;
}
/*
* Check for inconsistency between the argument of -F and
* the actual file system type.
* If there is an inconsistency and the user specified a
* path, this is an error since we are asked to interpret
* the path using the wrong file system type. If there is
* no path associated with this request, we quietly ignore it.
*/
"Warning: %s mounted as a %s file system",
errors++;
}
continue;
}
/*
* Skip remote file systems if the -l option is present
*/
"Warning: %s is not a local file system",
dfrp->dfr_cmd_arg);
errors++;
}
continue;
}
/*
* Skip file systems mounted as "ignore" unless the -a option
* is present, or the user explicitly specified them on
* the command line.
*/
continue;
}
n_valid++;
}
return (errors);
}
/*
* Print the appropriate header for the requested output format.
* Options are checked in order of their precedence.
*/
static void
print_header(void)
{
if (use_scaling) { /* this comes from the -h option */
int arg = 'h';
(void) printf("%-*s %*s %*s %*s %-*s %s\n",
TRANSLATE("Mounted on"));
SET_OPTION(h);
return;
}
if (k_option) {
int arg = 'h';
TRANSLATE("Mounted on"));
SET_OPTION(h);
return;
}
if (m_option) {
int arg = 'h';
TRANSLATE("Mounted on"));
SET_OPTION(h);
return;
}
/* Added for XCU4 compliance */
if (P_option) {
int arg = 'h';
TRANSLATE("Mounted on"));
SET_OPTION(h);
return;
}
/* End XCU4 */
if (v_option) {
(void) printf("%-*s %-*s %*s %*s %*s %-*s\n",
return;
}
if (e_option) {
return;
}
if (b_option) {
return;
}
}
/*
* Convert an unsigned long long to a string representation and place the
* result in the caller-supplied buffer.
* The given number is in units of "unit_from" size, but the
* converted number will be in units of "unit_to" size. The unit sizes
* must be powers of 2.
* The value "(unsigned long long)-1" is a special case and is always
* converted to "-1".
* Returns a pointer to the caller-supplied buffer.
*/
static char *
char *buf, /* put the result here */
unsigned long long number, /* convert this number */
int unit_from, /* from units of this size */
int unit_to) /* to units of this size */
{
if ((long long)number == (long long)-1)
else {
else
}
return (buf);
}
/*
* Convert an unsigned long long to a string representation and place the
* result in the caller-supplied buffer.
* The given number is in units of "unit_from" size,
* this will first be converted to a number in 1024 or 1000 byte size,
* depending on the scaling factor.
* Then the number is scaled down until it is small enough to be in a good
* human readable format i.e. in the range 0 thru scale-1.
* If it's smaller than 10 there's room enough to provide one decimal place.
* The value "(unsigned long long)-1" is a special case and is always
* converted to "-1".
* Returns a pointer to the caller-supplied buffer.
*/
static char *
unsigned long long number, /* convert this number */
int unit_from,
int scale)
{
unsigned long long save = 0;
char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */
if ((long long)number == (long long)-1) {
return (buf);
}
/*
* Convert number from unit_from to given scale (1024 or 1000).
* This means multiply number by unit_from and divide by scale.
*
* Would like to multiply by unit_from and then divide by scale,
* but if the first multiplication would overflow, then need to
* divide by scale and then multiply by unit_from.
*/
(unsigned long long)unit_from;
} else {
(unsigned long long)scale;
}
/*
* Now we have number as a count of scale units.
* Stop scaling when we reached exa bytes, then something is
* probably wrong with our number.
*/
uom++; /* next unit of measurement */
}
/* check if we should output a decimal place after the point */
/* sprintf() will round for us */
} else {
}
return (buf);
}
/*
* The statvfs() implementation allows us to return only two values, the total
* number of blocks and the number of blocks free. The equation 'used = total -
* free' will not work for ZFS filesystems, due to the nature of pooled storage.
* We choose to return values in the statvfs structure that will produce correct
* results for 'used' and 'available', but not 'total'. This function will open
* the underlying ZFS dataset if necessary and get the real value.
*/
static void
{
return;
/*
* We want to get the total size for this filesystem as bounded by any
* quotas. In order to do this, we start at the current filesystem and
* work upwards looking for the smallest quota. When we reach the
* pool itself, the quota is the amount used plus the amount
* available.
*/
return;
*slash = '\0';
break;
/* true at first iteration of loop */
if (first) {
if (quota == 0)
quota = UINT64_MAX;
}
quota = this_quota;
/* true at last iteration of loop */
}
}
/*
* Modify total only if we managed to get some stats from libzfs.
*/
if (quota != 0)
}
/*
* The output will appear properly columnized regardless of the names of
* the various fields
*/
static void
{
char *temp_buf;
#define DEFINE_STR_LEN(var) \
}
/*
* TRANSLATION_NOTE
* The first argument of each of the following macro invocations is a
* string that needs to be translated.
*/
+ 20); /* plus slop - nulls & formatting */
(void) printf("%-*s %*lu %-*s %*lu %-*s\n",
/*
* Adjust available_blocks value - it can be less than 0 on
* a 4.x file system. Reset it to 0 in order to avoid printing
* negative numbers.
*/
if ((long long)available_blocks < (long long)0)
available_blocks = (fsblkcnt64_t)0;
(void) printf("%*s %-*s %*s %-*s %*s %-*s %*s %-*s\n",
(void) printf("%*s %-*s %*lu %-*s %s\n",
(void) printf("%*s %-*s %#*.*lx %-*s %*s %-*s\n\n",
}
static void
{
char capacity_buf[LINEBUF_SIZE];
/*
* If the free block count is -1, don't trust anything but the total
* number of blocks.
*/
} else {
/*
* The capacity estimation is bogus when available_blocks is 0
* and the super-user has allocated more space. The reason
* is that reserved_blocks is inaccurate in that case, because
* when the super-user allocates space, free_blocks is updated
* but available_blocks is not (since it can't drop below 0).
*
* XCU4 and POSIX.2 require that any fractional result of the
* capacity estimation be rounded to the next highest integer,
* hence the addition of 0.5.
*/
(total_blocks == 0) ? 0.0 :
((double)used_blocks /
(double)(total_blocks - reserved_blocks))
* 100.0 + 0.5);
}
/*
* The available_blocks can be less than 0 on a 4.x file system.
* Reset it to 0 in order to avoid printing negative numbers.
*/
if ((long long)available_blocks < (long long)0)
available_blocks = (fsblkcnt64_t)0;
/*
* Print long special device names (usually NFS mounts) in a line
* by themselves when the output is directed to a terminal.
*/
file_system = "";
}
if (use_scaling) { /* comes from the -h option */
(void) printf("%-*s %*s %*s %*s %-*s %-s\n",
return;
}
if (v_option) {
(void) printf("%-*.*s %-*.*s %*lld %*lld %*lld %-.*s\n",
return;
}
(void) printf("%-*s %*s %*s %*s %-*s %-s\n",
} else if (m_option) {
(void) printf("%-*s %*s %*s %*s %-*s %-s\n",
} else {
(void) printf("%-*s %*s %*s %*s %-*s %-s\n",
}
}
/*
* The following is for internationalization support.
*/
static bool_int strings_initialized;
static char *files_str;
static char *blocks_str;
static char *total_str;
static char *kilobytes_str;
static void
strings_init(void)
{
}
static void
{
STRINGS_INIT();
(void) printf("%-*s(%-*s): %*s %s %*s %s\n",
/*
* The total column used to use the same space as the mnt pt & special
* dev fields. However, this doesn't work with massive special dev
* fields * (eg > 500 chars) causing an enormous amount of white space
* before the total column (see bug 4100411). So the code was
* simplified to set the total column at the usual gap.
* This had the side effect of fixing a bug where the previously
* used static buffer was overflowed by the same massive special dev.
*/
(void) printf("%*s: %*s %s %*s %s\n",
}
static void
{
STRINGS_INIT();
(void) printf("%-*s(%-*s): %*s %s\n",
(void) printf("%-*s(%-*s): %*s %s\n",
}
static void
{
(void) printf("%-*s %*s\n",
}
static void
{
(void) printf("%-*s %*s\n",
}
/* ARGSUSED */
static void
{
(void) printf("%-*s: %-*s\n",
}
static void
{
STRINGS_INIT();
(void) printf("%-*s(%-*s):%*s %s %*s %s\n",
}
/* ARGSUSED */
static void
{
char temp_buf[LINEBUF_SIZE];
if (df_options_len > 1)
else
(void) printf("%s -F %s %s%s\n",
}
/*
* This function is used to sort the array of df_requests according to fstype
*/
static int
{
if (v != 0)
return (v);
else
}
static void
{
if (status == VFS_TOOLONG)
file, MNT_LINE_MAX);
else if (status == VFS_TOOMANY)
else if (status == VFS_TOOFEW)
else
}
/*
* Try to determine the fstype for the specified block device.
* Return in order of decreasing preference:
* file system type from vfstab
* file system type as specified by -F option
* default file system type
*/
static char *
find_fstype(char *special)
{
int status;
char *vfstab_file = VFS_TAB;
if (status > 0)
if (status == 0) {
}
else
}
/*
* When this function returns, the following fields are filled for all
* valid entries in the requests[] array:
* dfr_mte (if the file system is mounted)
* dfr_fstype
* dfr_index
*
* The function returns the number of errors that occurred while building
* the request list.
*/
static int
int argc,
char *argv[],
struct df_request *requests_p[],
{
struct df_request *requests;
struct df_request *dfrp;
size_t i;
size_t request_index = 0;
int errors = 0;
/*
* If no args, use the mounted file systems, otherwise use the
* user-specified arguments.
*/
if (argc == 0) {
} else
max_requests = argc;
if (argc == 0) {
/*
* If -Z wasn't specified, we skip mounts in other
* zones. This obviously is a noop in a non-global
* zone.
*/
struct zone_summary *zsp;
if (!showall) {
zsp = fs_get_zone_summaries();
"unable to retrieve list of zones");
}
for (i = 0; i < mount_table_entries; i++) {
continue;
if (!showall) {
mtp->mnt_mountp))
continue;
}
}
} else {
/*
* Obtain stat64 information for each argument before
* constructing the list of mounted file systems. By
* touching all these places we force the automounter
* to establish any mounts required to access the arguments,
* so that the corresponding mount table entries will exist
* when we look for them.
* It is still possible that the automounter may timeout
* mounts between the time we read the mount table and the
* time we process the request. Even in that case, when
* we issue the statvfs64(2) for the mount point, the file
* system will be mounted again. The only problem will
* occur if the automounter maps change in the meantime
* and the mount point is eliminated.
*/
for (i = 0; i < argc; i++)
for (i = 0; i < argc; i++) {
if (valid_stat[i]) {
if (! DFR_ISMOUNTEDFS(dfrp)) {
errors++;
continue;
}
}
} else {
}
/*
* If we haven't managed to verify that the request
* is valid, we must have gotten a bad argument.
*/
"(%-10s) not a block device, directory or "
"mounted resource", arg);
errors++;
continue;
}
/*
* Determine the file system type.
*/
if (DFR_ISMOUNTEDFS(dfrp))
dfrp->dfr_fstype =
else
dfrp->dfr_fstype =
}
}
*requests_p = requests;
return (errors);
}
/*
* Select the appropriate function and flags to use for output.
* Notice that using both -e and -b options produces a different form of
* output than either of those two options alone; this is the behavior of
* the SVR4 df.
*/
static struct df_output *
select_output(void)
{
/*
* The order of checking options follows the option precedence
* rules as they are listed in the man page.
*/
if (use_scaling) { /* comes from the -h option */
} else if (V_option) {
} else if (g_option) {
} else if (t_option) {
} else if (b_option) {
} else if (e_option) {
} else if (n_option) {
} else {
}
return (&dfo);
}
/*
* The (argc,argv) pair contains all the non-option arguments
*/
static void
{
size_t i;
struct df_request *dfrp;
int errors;
if (n_requests == 0)
/*
* If we are going to run the FSType-specific df command,
* rearrange the requests so that we can issue a single command
* per file system type.
*/
if (o_option) {
size_t j;
/*
* qsort is not a stable sorting method (i.e. requests of
* the same file system type may be swapped, and hence appear
* in the output in a different order from the one in which
* they were listed in the command line). In order to force
* stability, we use the dfr_index field which is unique
* for each request.
*/
for (i = 0; i < n_requests; i = j) {
for (j = i+1; j < n_requests; j++)
break;
/*
* At this point, requests in the range [i,j) are
* of the same type.
*
* If the -F option was used, and the user specified
* arguments, the filesystem types must match
*
* XXX: the alternative of doing this check here is to
* invoke prune_list, but then we have to
* modify this code to ignore invalid requests.
*/
size_t k;
for (k = i; k < j; k++) {
"Warning: %s mounted as a "
"%s file system",
dfrp->dfr_fstype);
errors++;
}
}
} else
}
} else {
/*
* We have to prune the request list to avoid printing a header
* if there are no valid requests
*/
if (valid_requests) {
/* indicates if we already printed out a header line */
int printed_header = 0;
for (i = 0; i < n_requests; i++) {
continue;
/*
* If we don't have a mount point,
* this must be a block device.
*/
if (DFR_ISMOUNTEDFS(dfrp)) {
&stvfs) == -1) {
"cannot statvfs %s:",
errors++;
continue;
}
if ((!printed_header) &&
print_header();
printed_header = 1;
}
} else {
/*
* -h option only works for
* mounted filesystems
*/
if (use_scaling) {
"-h option incompatible with unmounted special device (%s)",
dfrp->dfr_cmd_arg);
errors++;
continue;
}
}
}
}
}
}
/*
* The rest of this file implements the devnm command
*/
static char *
{
struct df_request dfreq;
dfreq.dfr_fstype = 0;
}
static void
{
int arg;
int errors = 0;
char *dev_name;
if (argc == 1)
errors++;
continue;
}
else
"%s not found", file);
}
/* NOTREACHED */
}