/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/*
* df
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mntent.h>
#include <sys/fs/ufs_fs.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <sys/file.h>
#include <sys/statvfs.h>
#include <sys/mnttab.h>
#include <sys/mkdev.h>
#include <locale.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <libintl.h>
extern char *getenv();
extern char *getcwd();
extern char *realpath();
extern off_t lseek();
/*
* Raw name to block device name translation function.
* This comes from libadm.
*/
extern char *getfullblkname();
static void usage(), pheader();
static char *mpath(char *);
static char *zap_chroot(char *);
static char *pathsuffix(char *, char *);
static char *xmalloc(unsigned int);
static int chroot_stat(char *, int (*)(), char *, char **);
static int bread(char *, int, daddr_t, char *, int);
static int subpath(char *, char *);
static int abspath(char *, char *, char *);
static void show_inode_usage();
static void dfreedev(char *);
static void dfreemnt(char *, struct mnttab *);
static void print_totals();
static void print_itotals();
static void print_statvfs(struct statvfs64 *);
static int mdev(char *, struct mnttab **);
static struct mntlist *mkmntlist();
static struct mnttab *mntdup(struct mnttab *mnt);
static struct mntlist *findmntent(char *, struct stat64 *, struct mntlist *);
#define bcopy(f, t, n) memcpy(t, f, n)
#define bzero(s, n) memset(s, 0, n)
#define bcmp(s, d, n) memcmp(s, d, n)
#define index(s, r) strchr(s, r)
#define rindex(s, r) strrchr(s, r)
#define dbtok(x, b) \
((b) < (fsblkcnt64_t)1024 ? \
(x) / ((fsblkcnt64_t)1024 / (b)) : (x) * ((b) / (fsblkcnt64_t)1024))
int aflag = 0; /* even the uninteresting ones */
int bflag = 0; /* print only number of kilobytes free */
int eflag = 0; /* print only number of file entries free */
int gflag = 0; /* print entire statvfs structure */
int hflag = 0; /* don't print header */
int iflag = 0; /* information for inodes */
int nflag = 0; /* print VFStype name */
int tflag = 0; /* print totals */
int errflag = 0;
int errcode = 0;
char *typestr = "ufs";
fsblkcnt64_t t_totalblks, t_avail, t_free, t_used, t_reserved;
int t_inodes, t_iused, t_ifree;
/*
* cached information recording previous chroot history.
*/
static char *chrootpath;
extern int optind;
extern char *optarg;
union {
struct fs iu_fs;
char dummy[SBSIZE];
} sb;
#define sblock sb.iu_fs
/*
* This structure is used to chain mntent structures into a list
* and to cache stat information for each member of the list.
*/
struct mntlist {
struct mnttab *mntl_mnt;
struct mntlist *mntl_next;
dev_t mntl_dev;
int mntl_devvalid;
};
char *subopts [] = {
#define A_FLAG 0
"a",
#define I_FLAG 1
"i",
NULL
};
int
main(int argc, char *argv[])
{
struct mnttab mnt;
int opt;
char *suboptions, *value;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
while ((opt = getopt(argc, argv, "beghkno:t")) != EOF) {
switch (opt) {
case 'b': /* print only number of kilobytes free */
bflag++;
break;
case 'e':
eflag++; /* print only number of file entries free */
iflag++;
break;
case 'g':
gflag++;
break;
case 'n':
nflag++;
break;
case 'k':
break;
case 'h':
hflag++;
break;
case 'o':
/*
* ufs specific options.
*/
suboptions = optarg;
while (*suboptions != '\0') {
switch (getsubopt(&suboptions,
subopts, &value)) {
case I_FLAG: /* information for inodes */
iflag++;
break;
default:
usage();
}
}
break;
case 't': /* print totals */
tflag++;
break;
case 'V': /* Print command line */
{
char *opt_text;
int opt_count;
(void) fprintf(stdout, "df -F ufs ");
for (opt_count = 1; opt_count < argc;
opt_count++) {
opt_text = argv[opt_count];
if (opt_text)
(void) fprintf(stdout, " %s ",
opt_text);
}
(void) fprintf(stdout, "\n");
}
break;
case '?':
errflag++;
}
}
if (errflag)
usage();
if (gflag && iflag) {
printf(gettext("df: '-g' and '-o i' are mutually exclusive\n"));
exit(1);
}
if (bflag || eflag)
tflag = 0;
/*
* Cache CHROOT information for later use; assume that $CHROOT matches
* the cumulative arguments given to chroot calls.
*/
chrootpath = getenv("CHROOT");
if (chrootpath != NULL && strcmp(chrootpath, "/") == 0)
chrootpath = NULL;
if (argc <= optind) {
/*
* Take this path when "/usr/lib/fs/ufs/df" is specified, and
* there are no mountpoints specified.
* E.g., these command lines take us down this path
* /usr/lib/fs/ufs/df -o i
* /usr/lib/fs/ufs/df
*/
FILE *mtabp;
if ((mtabp = fopen(MNTTAB, "r")) == NULL) {
(void) fprintf(stderr, "df: ");
perror(MNTTAB);
exit(1);
}
pheader();
while (getmntent(mtabp, &mnt) == 0) {
if (strcmp(typestr, mnt.mnt_fstype) != 0) {
continue;
}
dfreemnt(mnt.mnt_mountp, &mnt);
}
if (tflag)
if (iflag)
print_itotals();
else
print_totals();
(void) fclose(mtabp);
} else {
int i;
struct mntlist *mntl;
struct stat64 *argstat;
char **devnames;
char *cp;
/* Arguments are processed till optind, adjust the pointers */
argv += optind;
argc -= optind;
/*
* Obtain stat64 information for each argument before
* constructing the list of mounted file systems. This
* ordering forces 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.
*/
argstat = (struct stat64 *)xmalloc(argc * sizeof (*argstat));
devnames = (char **)xmalloc(argc * sizeof (char *));
for (i = 0; i < argc; i++) {
/*
* Given a raw device name, get the block device name
*/
cp = getfullblkname(argv[i]);
if (cp == NULL || *cp == '\0') {
if (cp != NULL)
free(cp);
cp = strdup(argv[i]);
if (cp == NULL) {
int j;
fprintf(stderr, gettext(
"df: memory allocation failure\n"));
for (j = 0; j < i; j++)
free(devnames[j]);
free(devnames);
free(argstat);
exit(1);
}
}
if (stat64(cp, &argstat[i]) < 0) {
errcode = errno;
/*
* Mark as no longer interesting.
*/
argv[i] = NULL;
devnames[i] = NULL;
free(cp);
} else {
devnames[i] = cp;
}
}
pheader();
aflag++;
/*
* Construct the list of mounted file systems.
*/
mntl = mkmntlist();
/*
* Iterate through the argument list, reporting on each one.
*/
for (i = 0; i < argc; i++) {
struct mntlist *mlp;
int isblk;
/*
* Skip if we've already determined that we can't
* process it.
*/
if (argv[i] == NULL)
continue;
/*
* If the argument names a device, report on the file
* system associated with the device rather than on
* the one containing the device's directory entry
*/
cp = devnames[i];
if ((isblk = (argstat[i].st_mode&S_IFMT) == S_IFBLK) ||
(argstat[i].st_mode & S_IFMT) == S_IFCHR) {
if (isblk && strcmp(mpath(cp), "") != 0) {
struct mnttab *mp;
if (mdev(cp, &mp))
return (1);
dfreemnt(mp->mnt_mountp, mp);
} else {
dfreedev(cp);
}
free(cp);
devnames[i] = NULL;
continue;
}
/*
* Get this argument's corresponding mount table
* entry.
*/
mlp = findmntent(cp, &argstat[i], mntl);
free(cp);
devnames[i] = NULL;
if (mlp == NULL) {
(void) fprintf(stderr,
gettext("Could not find mount point for %s\n"),
argv[i]);
continue;
}
dfreemnt(mlp->mntl_mnt->mnt_mountp, mlp->mntl_mnt);
}
free(devnames);
free(argstat);
}
return (0);
}
void
pheader()
{
if (hflag)
return;
if (nflag)
(void) printf(gettext("VFStype name - ufs\n"));
if (iflag) {
if (eflag)
/*
* TRANSLATION_NOTE
* Following string is used as a table header.
* Translated items should start at the same
* columns as the original items.
*/
(void) printf(gettext(
"Filesystem ifree\n"));
else {
/*
* TRANSLATION_NOTE
* Following string is used as a table header.
* Translated items should start at the same
* columns as the original items.
*/
(void) printf(gettext(
"Filesystem iused ifree %%iused Mounted on\n"));
}
} else {
if (gflag)
/*
* TRANSLATION_NOTE
* Following string is used as a table header.
* Translated items should start at the same
* columns as the original items.
*/
(void) printf(gettext(
"Filesystem f_type f_fsize f_bfree f_bavail f_files f_ffree "
"f_fsid f_flag f_fstr\n"));
else
if (bflag)
/*
* TRANSLATION_NOTE
* Following string is used as a table header.
* Translated items should start at the same
* columns as the original items.
*/
(void) printf(gettext(
"Filesystem avail\n"));
else {
/*
* TRANSLATION_NOTE
* Following string is used as a table header.
* Translated items should start at the same
* columns as the original items.
*/
(void) printf(gettext(
"Filesystem kbytes used avail capacity Mounted on\n"));
}
}
}
/*
* Report on a block or character special device. Assumed not to be
* mounted. N.B. checks for a valid UFS superblock.
*/
void
dfreedev(char *file)
{
fsblkcnt64_t totalblks, availblks, avail, free, used;
int fi;
fi = open64(file, 0);
if (fi < 0) {
(void) fprintf(stderr, "df: ");
perror(file);
return;
}
if (bread(file, fi, SBLOCK, (char *)&sblock, SBSIZE) == 0) {
(void) close(fi);
return;
}
if ((sblock.fs_magic != FS_MAGIC) &&
(sblock.fs_magic != MTB_UFS_MAGIC)) {
(void) fprintf(stderr, gettext(
"df: %s: not a ufs file system\n"),
file);
(void) close(fi);
return;
}
if (sblock.fs_magic == FS_MAGIC &&
(sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 &&
sblock.fs_version != UFS_VERSION_MIN)) {
(void) fprintf(stderr, gettext(
"df: %s: unrecognized version of UFS: %d\n"),
file, sblock.fs_version);
(void) close(fi);
return;
}
if (sblock.fs_magic == MTB_UFS_MAGIC &&
(sblock.fs_version > MTB_UFS_VERSION_1 ||
sblock.fs_version < MTB_UFS_VERSION_MIN)) {
(void) fprintf(stderr, gettext(
"df: %s: unrecognized version of UFS: %d\n"),
file, sblock.fs_version);
(void) close(fi);
return;
}
(void) printf("%-20.20s", file);
if (iflag) {
if (eflag) {
(void) printf("%8ld", sblock.fs_cstotal.cs_nifree);
} else {
show_inode_usage(
(fsfilcnt64_t)sblock.fs_ncg *
(fsfilcnt64_t)sblock.fs_ipg,
(fsfilcnt64_t)sblock.fs_cstotal.cs_nifree);
}
} else {
totalblks = (fsblkcnt64_t)sblock.fs_dsize;
free =
(fsblkcnt64_t)sblock.fs_cstotal.cs_nbfree *
(fsblkcnt64_t)sblock.fs_frag +
(fsblkcnt64_t)sblock.fs_cstotal.cs_nffree;
used = totalblks - free;
availblks = totalblks / (fsblkcnt64_t)100 *
((fsblkcnt64_t)100 - (fsblkcnt64_t)sblock.fs_minfree);
avail = availblks > used ? availblks - used : (fsblkcnt64_t)0;
if (bflag) {
(void) printf("%8lld\n", dbtok(avail,
(fsblkcnt64_t)sblock.fs_fsize));
} else {
(void) printf(" %7lld %7lld %7lld",
dbtok(totalblks, (fsblkcnt64_t)sblock.fs_fsize),
dbtok(used, (fsblkcnt64_t)sblock.fs_fsize),
dbtok(avail, (fsblkcnt64_t)sblock.fs_fsize));
(void) printf("%6.0f%%",
availblks == 0 ? 0.0 :
(double)used / (double)availblks * 100.0);
(void) printf(" ");
}
if (tflag) {
t_totalblks += dbtok(totalblks,
(fsblkcnt64_t)sblock.fs_fsize);
t_used += dbtok(used, (fsblkcnt64_t)sblock.fs_fsize);
t_avail += dbtok(avail, (fsblkcnt64_t)sblock.fs_fsize);
t_free += free;
}
}
if ((!bflag) && (!eflag))
(void) printf(" %s\n", mpath(file));
else if (eflag)
(void) printf("\n");
(void) close(fi);
}
void
dfreemnt(char *file, struct mnttab *mnt)
{
struct statvfs64 fs;
if (statvfs64(file, &fs) < 0 &&
chroot_stat(file, statvfs64, (char *)&fs, &file) < 0) {
(void) fprintf(stderr, "df: ");
perror(file);
return;
}
if (!aflag && fs.f_blocks == 0) {
return;
}
if (!isatty(fileno(stdout))) {
(void) printf("%s", mnt->mnt_special);
} else {
if (strlen(mnt->mnt_special) > (size_t)20) {
(void) printf("%s\n", mnt->mnt_special);
(void) printf(" ");
} else {
(void) printf("%-20.20s", mnt->mnt_special);
}
}
if (iflag) {
if (eflag) {
(void) printf("%8lld", fs.f_ffree);
} else {
show_inode_usage(fs.f_files, fs.f_ffree);
}
} else {
if (gflag) {
print_statvfs(&fs);
} else {
fsblkcnt64_t totalblks, avail, free, used, reserved;
totalblks = fs.f_blocks;
free = fs.f_bfree;
used = totalblks - free;
avail = fs.f_bavail;
reserved = free - avail;
if ((long long)avail < 0)
avail = 0;
if (bflag) {
(void) printf("%8lld\n", dbtok(avail,
(fsblkcnt64_t)fs.f_frsize));
} else {
(void) printf(" %7lld %7lld %7lld",
dbtok(totalblks,
(fsblkcnt64_t)fs.f_frsize),
dbtok(used, (fsblkcnt64_t)fs.f_frsize),
dbtok(avail, (fsblkcnt64_t)fs.f_frsize));
totalblks -= reserved;
(void) printf("%6.0f%%",
totalblks == 0 ? 0.0 :
(double)used / (double)totalblks * 100.0);
(void) printf(" ");
if (tflag) {
t_totalblks += dbtok(totalblks + reserved,
(fsblkcnt64_t)fs.f_bsize);
t_reserved += reserved;
t_used += dbtok(used,
(fsblkcnt64_t)fs.f_frsize);
t_avail += dbtok(avail,
(fsblkcnt64_t)fs.f_frsize);
t_free += free;
}
}
}
}
if ((!bflag) && (!eflag) && (!gflag))
(void) printf(" %s\n", mnt->mnt_mountp);
else if (eflag)
(void) printf("\n");
}
static void
show_inode_usage(fsfilcnt64_t total, fsfilcnt64_t free)
{
fsfilcnt64_t used = total - free;
int missing_info = ((long long)total == (long long)-1 ||
(long long)free == (long long)-1);
if (missing_info)
(void) printf("%8s", "*");
else
(void) printf("%8lld", used);
if ((long long)free == (long long)-1)
(void) printf("%8s", "*");
else
(void) printf(" %7lld", free);
if (missing_info)
(void) printf("%6s ", "*");
else
(void) printf("%6.0f%% ", (double)used / (double)total * 100.0);
}
/*
* Return the suffix of path obtained by stripping off the prefix
* that is the value of the CHROOT environment variable. If this
* value isn't obtainable or if it's not a prefix of path, return NULL.
*/
static char *
zap_chroot(char *path)
{
return (pathsuffix(path, chrootpath));
}
/*
* Stat/statfs a file after stripping off leading directory to which we are
* chroot'd. Used to find the TFS mount that applies to the current
* activated NSE environment.
*/
static int
chroot_stat(char *dir, int (*statfunc)(), char *statp, char **dirp)
{
if ((dir = zap_chroot(dir)) == NULL)
return (-1);
if (dirp)
*dirp = dir;
return (*statfunc)(dir, statp);
}
/*
* Given a name like /dev/dsk/c1d0s2, returns the mounted path, like /usr.
*/
char *
mpath(char *file)
{
struct mnttab mnt;
FILE *mnttab;
struct stat64 device_stat, mount_stat;
char *mname;
mnttab = fopen(MNTTAB, "r");
if (mnttab == NULL) {
return ("");
}
mname = "";
while ((getmntent(mnttab, &mnt)) == 0) {
if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0) {
continue;
}
if (strcmp(file, mnt.mnt_special) == 0) {
if (stat64(mnt.mnt_mountp, &mount_stat) != 0)
continue;
if (stat64(mnt.mnt_special, &device_stat) != 0)
continue;
if (device_stat.st_rdev == mount_stat.st_dev) {
mname = mnt.mnt_mountp;
break;
}
}
}
fclose(mnttab);
return (mname);
}
/*
* Given a special device, return mnttab entry
* Returns 0 on success
*/
int
mdev(char *spec, struct mnttab **mntbp)
{
FILE *mntp;
struct mnttab mnt;
if ((mntp = fopen(MNTTAB, "r")) == 0) {
(void) fprintf(stderr, "df: ");
perror(MNTTAB);
return (1);
}
while (getmntent(mntp, &mnt) == 0) {
if (strcmp(spec, mnt.mnt_special) == 0) {
(void) fclose(mntp);
*mntbp = mntdup(&mnt);
return (0);
}
}
(void) fclose(mntp);
(void) fprintf(stderr, "df : couldn't find mnttab entry for %s", spec);
return (1);
}
/*
* Find the entry in mlist that corresponds to the file named by path
* (i.e., that names a mount table entry for the file system in which
* path lies). The pstat argument must point to stat information for
* path.
*
* Return the entry or NULL if there's no match.
*
* As it becomes necessary to obtain stat information about previously
* unexamined mlist entries, gather the information and cache it with the
* entries.
*
* The routine's strategy is to convert path into its canonical, symlink-free
* representation canon (which will require accessing the file systems on the
* branch from the root to path and thus may cause the routine to hang if any
* of them are inaccessible) and to use it to search for a mount point whose
* name is a substring of canon and whose corresponding device matches that of
* canon. This technique avoids accessing unnecessary file system resources
* and thus prevents the program from hanging on inaccessible resources unless
* those resources are necessary for accessing path.
*/
static struct mntlist *
findmntent(char *path, struct stat64 *pstat, struct mntlist *mlist)
{
static char cwd[MAXPATHLEN];
char canon[MAXPATHLEN];
char scratch[MAXPATHLEN];
struct mntlist *mlp;
/*
* If path is relative and we haven't already determined the current
* working directory, do so now. Calculating the working directory
* here lets us do the work once, instead of (potentially) repeatedly
* in realpath().
*/
if (*path != '/' && cwd[0] == '\0') {
if (getcwd(cwd, MAXPATHLEN) == NULL) {
cwd[0] = '\0';
return (NULL);
}
}
/*
* Find an absolute pathname in the native file system name space that
* corresponds to path, stuffing it into canon.
*
* If CHROOT is set in the environment, assume that chroot($CHROOT)
* (or an equivalent series of calls) was executed and convert the
* path to the equivalent name in the native file system's name space.
* Doing so allows direct comparison with the names in mtab entires,
* which are assumed to be recorded relative to the native name space.
*/
if (abspath(cwd, path, scratch) < 0)
return (NULL);
if (strcmp(scratch, "/") == 0 && chrootpath != NULL) {
/*
* Force canon to be in canonical form; if the result from
* abspath was "/" and chrootpath isn't the null string, we
* must strip off a trailing slash.
*/
scratch[0] = '\0';
}
(void) sprintf(canon, "%s%s", chrootpath ? chrootpath : "", scratch);
again:
for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
struct mnttab *mnt = mlp->mntl_mnt;
/*
* Ignore uninteresting mounts.
*/
if (strcmp(mnt->mnt_fstype, typestr) != 0)
continue;
/*
* The mount entry covers some prefix of the file.
* See whether it's the entry for the file system
* containing the file by comparing device ids.
*/
if (mlp->mntl_dev == NODEV) {
struct stat64 fs_sb;
if (stat64(mnt->mnt_mountp, &fs_sb) < 0 &&
chroot_stat(mnt->mnt_mountp, stat64, (char *)&fs_sb,
(char **)NULL) < 0) {
continue;
}
mlp->mntl_dev = fs_sb.st_dev;
}
if (pstat->st_dev == mlp->mntl_dev)
return (mlp);
}
return (NULL);
}
/*
* Convert the path given in raw to canonical, absolute, symlink-free
* form, storing the result in the buffer named by canon, which must be
* at least MAXPATHLEN bytes long. "wd" contains the current working
* directory; accepting this value as an argument lets our caller cache
* the value, so that realpath (called from this routine) doesn't have
* to recalculate it each time it's given a relative pathname.
*
* Return 0 on success, -1 on failure.
*/
static int
abspath(char *wd, char *raw, char *canon)
{
char absbuf[MAXPATHLEN];
/*
* Preliminary sanity check.
*/
if (wd == NULL || raw == NULL || canon == NULL)
return (-1);
/*
* If the path is relative, convert it to absolute form,
* using wd if it's been supplied.
*/
if (raw[0] != '/') {
char *limit = absbuf + sizeof (absbuf);
char *d;
/* Fill in working directory. */
if (strlcpy(absbuf, wd, sizeof (absbuf)) >= sizeof (absbuf))
return (-1);
/* Add separating slash. */
d = absbuf + strlen(absbuf);
if (d < limit)
*d++ = '/';
/* Glue on the relative part of the path. */
while (d < limit && (*d++ = *raw++))
continue;
raw = absbuf;
}
/*
* Call realpath to canonicalize and resolve symlinks.
*/
return (realpath(raw, canon) == NULL ? -1 : 0);
}
/*
* Return a pointer to the trailing suffix of full that follows the prefix
* given by pref. If pref isn't a prefix of full, return NULL. Apply
* pathname semantics to the prefix test, so that pref must match at a
* component boundary.
*/
static char *
pathsuffix(char *full, char *pref)
{
int preflen;
if (full == NULL || pref == NULL)
return (NULL);
preflen = strlen(pref);
if (strncmp(pref, full, preflen) != 0)
return (NULL);
/*
* pref is a substring of full. To be a subpath, it cannot cover a
* partial component of full. The last clause of the test handles the
* special case of the root.
*/
if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1)
return (NULL);
if (preflen == 1 && full[0] == '/')
return (full);
else
return (full + preflen);
}
/*
* Return zero iff the path named by sub is a leading subpath
* of the path named by full.
*
* Treat null paths as matching nothing.
*/
static int
subpath(char *full, char *sub)
{
return (pathsuffix(full, sub) == NULL);
}
offset_t llseek();
int
bread(char *file, int fi, daddr_t bno, char *buf, int cnt)
{
int n;
(void) llseek(fi, (offset_t)bno * DEV_BSIZE, 0);
if ((n = read(fi, buf, cnt)) < 0) {
/* probably a dismounted disk if errno == EIO */
if (errno != EIO) {
(void) fprintf(stderr, gettext("df: read error on "));
perror(file);
(void) fprintf(stderr, "bno = %ld\n", bno);
} else {
(void) fprintf(stderr, gettext(
"df: premature EOF on %s\n"), file);
(void) fprintf(stderr,
"bno = %ld expected = %d count = %d\n", bno, cnt, n);
}
return (0);
}
return (1);
}
char *
xmalloc(unsigned int size)
{
char *ret;
char *malloc();
if ((ret = (char *)malloc(size)) == NULL) {
(void) fprintf(stderr, gettext("umount: ran out of memory!\n"));
exit(1);
}
return (ret);
}
struct mnttab *
mntdup(struct mnttab *mnt)
{
struct mnttab *new;
new = (struct mnttab *)xmalloc(sizeof (*new));
new->mnt_special =
(char *)xmalloc((unsigned)(strlen(mnt->mnt_special) + 1));
(void) strcpy(new->mnt_special, mnt->mnt_special);
new->mnt_mountp =
(char *)xmalloc((unsigned)(strlen(mnt->mnt_mountp) + 1));
(void) strcpy(new->mnt_mountp, mnt->mnt_mountp);
new->mnt_fstype =
(char *)xmalloc((unsigned)(strlen(mnt->mnt_fstype) + 1));
(void) strcpy(new->mnt_fstype, mnt->mnt_fstype);
if (mnt->mnt_mntopts != NULL) {
new->mnt_mntopts =
(char *)xmalloc((unsigned)(strlen(mnt->mnt_mntopts) + 1));
(void) strcpy(new->mnt_mntopts, mnt->mnt_mntopts);
} else {
new->mnt_mntopts = NULL;
}
#ifdef never
new->mnt_freq = mnt->mnt_freq;
new->mnt_passno = mnt->mnt_passno;
#endif /* never */
return (new);
}
void
usage()
{
(void) fprintf(stderr, gettext(
"ufs usage: df [generic options] [-o i] [directory | special]\n"));
exit(1);
}
struct mntlist *
mkmntlist()
{
FILE *mounted;
struct mntlist *mntl;
struct mntlist *mntst = NULL;
struct extmnttab mnt;
if ((mounted = fopen(MNTTAB, "r")) == NULL) {
(void) fprintf(stderr, "df : ");
perror(MNTTAB);
exit(1);
}
resetmnttab(mounted);
while (getextmntent(mounted, &mnt, sizeof (struct extmnttab)) == NULL) {
mntl = (struct mntlist *)xmalloc(sizeof (*mntl));
mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt));
mntl->mntl_next = mntst;
mntl->mntl_devvalid = 1;
mntl->mntl_dev = makedev(mnt.mnt_major, mnt.mnt_minor);
mntst = mntl;
}
(void) fclose(mounted);
return (mntst);
}
void
print_statvfs(struct statvfs64 *fs)
{
int i;
for (i = 0; i < FSTYPSZ; i++)
(void) printf("%c", fs->f_basetype[i]);
(void) printf(" %7d %7lld %7lld",
fs->f_frsize,
fs->f_blocks,
fs->f_bavail);
(void) printf(" %7lld %7lld %7d",
fs->f_files,
fs->f_ffree,
fs->f_fsid);
(void) printf(" 0x%x ",
fs->f_flag);
for (i = 0; i < 14; i++)
(void) printf("%c",
(fs->f_fstr[i] == '\0') ? ' ' : fs->f_fstr[i]);
printf("\n");
}
void
print_totals()
{
/*
* TRANSLATION_NOTE
* Following string is used as a table header.
* Translated items should start at the same
* columns as the original items.
*/
(void) printf(gettext("Totals %8lld %7lld %7lld"),
t_totalblks, t_used, t_avail);
(void) printf("%6.0f%%\n",
(t_totalblks - t_reserved) == (fsblkcnt64_t)0 ?
0.0 :
(double)t_used / (double)(t_totalblks - t_reserved) * 100.0);
}
void
print_itotals()
{
/*
* TRANSLATION_NOTE
* Following string is used as a table header.
* Translated items should start at the same
* columns as the original items.
*/
(void) printf(gettext("Totals %8d %7d%6.0f%%\n"),
t_iused,
t_ifree,
t_inodes == 0 ? 0.0 : (double)t_iused / (double)t_inodes * 100.0);
}