1218 lines
27 KiB
C
1218 lines
27 KiB
C
/*
|
|
* df(1) - report disk free block counts and other statistics
|
|
*/
|
|
|
|
#ident "$Revision: 1.68 $"
|
|
|
|
#include <bstring.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <mntent.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/attributes.h>
|
|
#include <sys/param.h>
|
|
#include <sys/fsid.h>
|
|
#include <sys/fstyp.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statfs.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/syssgi.h>
|
|
#include <sys/uuid.h>
|
|
#include <sys/fs/xfs_fsops.h>
|
|
#include <sys/mount.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <diskinfo.h>
|
|
#include <invent.h>
|
|
#include <locale.h>
|
|
#include <fmtmsg.h>
|
|
#include <unistd.h>
|
|
#include <sgi_nl.h>
|
|
#include <msgs/uxsgicore.h>
|
|
|
|
#define KSHIFT 10
|
|
#define BBTOK(bb) ((bb) >> (KSHIFT - BBSHIFT))
|
|
|
|
#ifdef DEBUG
|
|
char MTAB[] = "./mtab";
|
|
#else
|
|
char MTAB[] = MOUNTED;
|
|
#endif
|
|
|
|
typedef long long df_blkcnt_t;
|
|
typedef long long df_filcnt_t;
|
|
|
|
char do_free_scan; /* force scan of freelist */
|
|
char do_inode_stats; /* print used and free file counts */
|
|
char do_FStype; /* print the VFS type */
|
|
char Vflag; /* print formatted command lines */
|
|
char eflag; /* print free file count */
|
|
char rtflag; /* print xfs realtime free information */
|
|
int Pflag = 0; /* xpg4: formatted header */
|
|
unsigned long mtptwidth = 19ul; /* width of mount point field */
|
|
|
|
char *fs_type; /* File system type to search for */
|
|
enum units { halfk, kb, mb, human } units = halfk;
|
|
int local_fs_only; /* list only local filesystems */
|
|
int explicit_args; /* true if arguments were given */
|
|
int errors = 0;
|
|
|
|
int main(int argc, char **argv);
|
|
int same_mount(char *devpath, struct mntent *mntp);
|
|
struct mntent *getmntbyid(mountid_t *midp);
|
|
char *devnm(char *file, mountid_t *midp);
|
|
char *searchdir(DIR *dirp, dev_t dev);
|
|
void put_head(void);
|
|
void put_line(struct mntent *mntp);
|
|
int stat_entry(struct mntent *mntp, struct statfs *sfbp);
|
|
int ping_nfs_server(struct mntent *mntp);
|
|
int scan_freelist(struct mntent *mntp, struct statfs *sfbp);
|
|
int bread(int fd, daddr_t bno, char *bp, int cnt);
|
|
void xfs_getrt(char *name, struct statfs *sfbp);
|
|
int usage(void);
|
|
int is_block_or_char(char *path);
|
|
int my_getmountid(const char *name, mountid_t *mid);
|
|
int my_getmntany(FILE *mtabp, struct mntent *mntent, char *mntpnt);
|
|
static void chknsnip(char *, unsigned int);
|
|
|
|
|
|
/*
|
|
* The options specifier string argument to getopt.
|
|
*/
|
|
char opts[] = "befhikmPlnqrtw:VF:";
|
|
|
|
char cmd_label[16] = "UX:df";
|
|
|
|
/*
|
|
* error messages
|
|
*/
|
|
struct dferr {
|
|
int flag;
|
|
char *cmsg; /* catalog */
|
|
char *dmsg; /* default */
|
|
} dferr[] = {
|
|
{ SGINL_SYSERR, _SGI_DMMX_cannotstat,
|
|
"cannot stat %s" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_notfnd,
|
|
"%s not found" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_NFSservnotresp,
|
|
"%s: NFS server %s not responding" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_supblk,
|
|
"unrecognizable superblock on %s" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_freelist,
|
|
"cannot scan freelist for filesystem %s" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_badfreecnt,
|
|
"bad free count for block %ld on %s" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_badfreeblk,
|
|
"bad free block %ld on %s" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_rdfreeblk,
|
|
"cannot read free block %ld on %s" },
|
|
{ SGINL_SYSERR, _SGI_DMMX_CannotOpen,
|
|
"Cannot open %s" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_cantgetsts,
|
|
"Cannot get status from %s" },
|
|
{ SGINL_NOSYSERR, _SGI_DMMX_df_readsb,
|
|
"Cannot read superblock on %s" },
|
|
};
|
|
|
|
#define ERR_STAT 0
|
|
#define ERR_notfnd 1
|
|
#define ERR_NFSservnotresp 2
|
|
#define ERR_supblk 3
|
|
#define ERR_freelist 4
|
|
#define ERR_badfreecnt 5
|
|
#define ERR_badfreeblk 6
|
|
#define ERR_rdfreeblk 7
|
|
#define ERR_open 8
|
|
#define ERR_status 9
|
|
#define ERR_readsb 10
|
|
|
|
#define DF_MAX_ERR 10
|
|
|
|
int
|
|
usage(void)
|
|
{
|
|
_sgi_nl_usage(SGINL_USAGE, "UX:df",
|
|
gettxt(_SGI_DMMX_df_usage, "df [-%s] [-F type] [-w width] [filesystem ...]"),
|
|
"befhikmPlnqrtV");
|
|
_sgi_nl_usage(SGINL_USAGE, "UX:devnm",
|
|
gettxt(_SGI_DMMX_devnm_usage, "devnm name"));
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* some msg prints
|
|
*/
|
|
static int
|
|
error(int nbr, char *arg1, char *arg2)
|
|
{
|
|
register struct dferr *ep;
|
|
register int oerrno = errno;
|
|
|
|
/* Won't pass anything >= MAXPATHLEN */
|
|
chknsnip(arg1, MAXPATHLEN);
|
|
chknsnip(arg2, MAXPATHLEN);
|
|
|
|
if(nbr > DF_MAX_ERR)
|
|
return(oerrno);
|
|
ep = dferr + nbr;
|
|
_sgi_nl_error(ep->flag, cmd_label,
|
|
gettxt(ep->cmsg, ep->dmsg),
|
|
arg1,
|
|
arg2);
|
|
return(oerrno);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
char *cp, *basename;
|
|
register int c, i, j;
|
|
unsigned width;
|
|
register struct mntent *mntp;
|
|
register FILE *mtabp;
|
|
mountid_t mid;
|
|
struct mntent mntent;
|
|
|
|
/*
|
|
* intnl support
|
|
*/
|
|
(void)setlocale(LC_ALL, "");
|
|
(void)setcat("uxsgicore");
|
|
(void)strncpy(cmd_label + 3, argv[0], 10);
|
|
(void)setlabel(cmd_label);
|
|
|
|
/*
|
|
* Check whether we're invoked as devnm(1M).
|
|
*/
|
|
basename = argv[0];
|
|
while ((cp = strrchr(basename, '/')) != NULL) {
|
|
if (cp[1] != '\0') {
|
|
basename = cp + 1;
|
|
break;
|
|
}
|
|
*cp = '\0';
|
|
}
|
|
if (strcmp(basename, "devnm") == 0) {
|
|
while (--argc > 0) {
|
|
if (my_getmountid(*++argv, &mid) < 0) {
|
|
error(ERR_STAT, *argv, 0);
|
|
errors++;
|
|
continue;
|
|
}
|
|
cp = devnm(*argv, &mid);
|
|
if (cp != NULL)
|
|
printf("%s %s\n", cp, *argv);
|
|
else {
|
|
error(ERR_notfnd, *argv, 0);
|
|
errors++;
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
if (getenv("HUMAN_BLOCKS") && !getenv("POSIXLY_CORRECT")) {
|
|
units = human;
|
|
}
|
|
|
|
/*
|
|
* Invoked as df(1): process options and arguments.
|
|
*/
|
|
while ((c = getopt(argc, argv, opts)) != EOF) {
|
|
switch (c) {
|
|
case 'k':
|
|
units = kb;
|
|
break;
|
|
|
|
case 'm':
|
|
units = mb;
|
|
break;
|
|
|
|
case 'h':
|
|
units = human;
|
|
break;
|
|
|
|
case 'P':
|
|
Pflag = 1;
|
|
break;
|
|
|
|
case 'b':
|
|
units = halfk;
|
|
break;
|
|
|
|
case 'f':
|
|
do_free_scan = 1;
|
|
break;
|
|
|
|
case 'q':
|
|
break;
|
|
|
|
case 'i':
|
|
do_inode_stats = 1;
|
|
break;
|
|
|
|
case 'l':
|
|
local_fs_only = 1;
|
|
break;
|
|
|
|
case 't': /* XXX SVID compatibility */
|
|
break;
|
|
|
|
case 'F': /* SVID */
|
|
fs_type = optarg;
|
|
break;
|
|
|
|
case 'n': /* SVID */
|
|
do_FStype = 1;
|
|
break;
|
|
|
|
case 'e': /* SVID */
|
|
eflag = 1;
|
|
break;
|
|
|
|
case 'V': /* SVID */
|
|
Vflag = 1;
|
|
break;
|
|
|
|
case 'r':
|
|
rtflag = 1;
|
|
break;
|
|
|
|
case 'w':
|
|
width = strtoul(optarg, NULL, 0);
|
|
/* don't bother to complain if wrong */
|
|
if(width)
|
|
mtptwidth = width;
|
|
break;
|
|
|
|
default:
|
|
return usage();
|
|
}
|
|
}
|
|
|
|
explicit_args = (optind < argc);
|
|
if ((mtabp = setmntent(MTAB, "r")) == NULL)
|
|
return(error(ERR_open, MTAB, 0));
|
|
|
|
put_head();
|
|
if (!explicit_args) {
|
|
while ((mntp = getmntent(mtabp)) != NULL) {
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0 ||
|
|
(mntp->mnt_opts != NULL && strstr(mntp->mnt_opts, "ignore")))
|
|
continue;
|
|
put_line(mntp);
|
|
}
|
|
} else {
|
|
local_fs_only = 0;
|
|
/*
|
|
* Check for argv/mtab overlap.
|
|
*/
|
|
for (i = optind; i < argc; i++) {
|
|
/*
|
|
* If an exact match is found in the mtab file and it's
|
|
* type is not LOFS we have what we need. If not we must
|
|
* use the mountid to find a match in the mtab file.
|
|
*/
|
|
if ((my_getmntany(mtabp, &mntent, argv[i]) == 0) &&
|
|
(strcmp( mntent.mnt_type, MNTTYPE_LOFS) != 0)) {
|
|
mntp = &mntent;
|
|
} else if (is_block_or_char(argv[i]) ||
|
|
(my_getmountid(argv[i], &mid) == -1) ||
|
|
!(mntp = getmntbyid(&mid)) ||
|
|
(is_nohide(argv[i], mntp))) {
|
|
bzero(&mntent, sizeof(mntent));
|
|
chknsnip(argv[i], MAXPATHLEN);
|
|
mntent.mnt_fsname = argv[i];
|
|
mntent.mnt_dir = mntent.mnt_type = NULL;
|
|
mntp = &mntent;
|
|
}
|
|
put_line(mntp);
|
|
}
|
|
}
|
|
endmntent(mtabp);
|
|
|
|
return errors;
|
|
}
|
|
|
|
int
|
|
is_block_or_char(char *path)
|
|
{
|
|
struct stat sb;
|
|
|
|
if (stat(path, &sb) == 0) {
|
|
return(S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode));
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
is_nohide(char *path, struct mntent *mntp)
|
|
{
|
|
struct stat sb1, sb2;
|
|
|
|
if ((stat(path, &sb1) == 0) && (stat(mntp->mnt_dir, &sb2) == 0)) {
|
|
if (sb1.st_dev != sb2.st_dev) {
|
|
return (1);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
struct mntent *
|
|
getmntbyid(mountid_t *midp)
|
|
{
|
|
register FILE *mtabp;
|
|
register struct mntent *mntp;
|
|
mountid_t mnt_mid;
|
|
|
|
if ((mtabp = setmntent(MTAB, "r")) == NULL)
|
|
return NULL;
|
|
while ((mntp = getmntent(mtabp)) != NULL) {
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0)
|
|
continue;
|
|
if (my_getmountid(mntp->mnt_dir, &mnt_mid) < 0)
|
|
continue;
|
|
if (bcmp(midp, &mnt_mid, sizeof(mountid_t)) == 0)
|
|
return mntp;
|
|
}
|
|
endmntent(mtabp);
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
old_device_name(char *file)
|
|
{
|
|
register int i;
|
|
char fsid[FSTYPSZ];
|
|
char *name;
|
|
struct stat stb;
|
|
|
|
static struct devs {
|
|
char *dirname;
|
|
DIR *dirp;
|
|
} devs[] = { /* in order of desired search */
|
|
/*
|
|
* /dev/dsk before /dev, so in the miniroot, devnm /
|
|
* will give /dev/dsk/dks0d1s1 not /dev/root.
|
|
*/
|
|
"/dev/dsk", NULL,
|
|
"/dev", NULL,
|
|
"/dev/xlv", NULL,
|
|
};
|
|
static char devname[MAXPATHLEN];
|
|
|
|
/*
|
|
* Assume a disk-based file system.
|
|
*/
|
|
if (stat(file, &stb) == -1) {
|
|
return(NULL);
|
|
}
|
|
if (devs[0].dirp == NULL) {
|
|
for (i = 0; i < sizeof(devs) / sizeof(devs[0]); i++)
|
|
devs[i].dirp = opendir(devs[i].dirname);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(devs) / sizeof(devs[0]); i++) {
|
|
if (devs[i].dirp != NULL
|
|
&& chdir(devs[i].dirname) == 0
|
|
&& (name = searchdir(devs[i].dirp, stb.st_dev)) != NULL) {
|
|
/* Can't put more than sizeof(devname) in devname */
|
|
if((strlen(devs[i].dirname)+strlen(name))
|
|
>= sizeof(devname)){
|
|
return NULL;
|
|
|
|
}
|
|
sprintf(devname, "%s/%s", devs[i].dirname, name);
|
|
return devname;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If all else fails, see if the library routine whose job it is
|
|
* to convert from dev_t's to names can do it. :-> We had to
|
|
* do all the earlier work for backwards compatibility, and we
|
|
* only try the fast and easy way as a last resort.
|
|
*/
|
|
{
|
|
int length = MAXPATHLEN;
|
|
|
|
if (dev_to_devname(stb.st_dev, devname, &length))
|
|
return (devname);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
searchdir(DIR *dirp, dev_t dev)
|
|
{
|
|
register struct dirent *dep;
|
|
struct stat stb;
|
|
|
|
rewinddir(dirp);
|
|
while ((dep = readdir(dirp)) != NULL) {
|
|
if (dep->d_ino == 0 || strcmp(dep->d_name, ".") == 0 ||
|
|
strcmp(dep->d_name, "..") == 0)
|
|
continue;
|
|
if (stat(dep->d_name, &stb) < 0)
|
|
continue;
|
|
if (dev != stb.st_rdev || !S_ISBLK(stb.st_mode))
|
|
continue;
|
|
return dep->d_name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
devnm(char *file, mountid_t *midp)
|
|
{
|
|
char *name = NULL;
|
|
register struct mntent *mntp;
|
|
|
|
if (mntp = getmntbyid(midp)) {
|
|
if ((strcmp(mntp->mnt_type, MNTTYPE_XFS) == 0) ||
|
|
(strcmp(mntp->mnt_type, MNTTYPE_EFS) == 0)) {
|
|
name = old_device_name(mntp->mnt_dir);
|
|
if (!name) {
|
|
name = mntp->mnt_fsname;
|
|
}
|
|
} else {
|
|
name = mntp->mnt_fsname;
|
|
}
|
|
} else {
|
|
name = old_device_name(file);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
char *unitname(enum units u, int pflag)
|
|
{
|
|
switch (u) {
|
|
case halfk: return (pflag ? "512-blocks" : "blocks");
|
|
case kb: return (pflag ? "1024-blocks" : "kbytes");
|
|
case mb: return ("MB-blocks");
|
|
case human: return ("Size");
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/* we no longer worry quite so much if it fits in 80 columns, and
|
|
* we no longer truncate mount points at all, and certainly not
|
|
* shorter if -i given. Since we never truncate at all, we allow
|
|
* less by default, which works for most people. With -i, on
|
|
* 80 columns, the mount points are always wrapped, which doesn't
|
|
* look tooLengthened the numeric fields because large filesystems
|
|
* are now relatively common, anddoing so keeps the columns lined up
|
|
* more often.
|
|
*/
|
|
void
|
|
put_head(void)
|
|
{
|
|
if(do_FStype || eflag || Vflag)
|
|
return;
|
|
if (Pflag) {
|
|
printf("%-*s %s Used Available Capacity",
|
|
mtptwidth, "Filesystem", unitname(units, 1));
|
|
} else if (units == human) {
|
|
printf("%-*s Type %s use avail %%use ",
|
|
mtptwidth, "Filesystem", unitname(units, 0));
|
|
} else if (units == mb) {
|
|
printf("%-*s Type %s use avail %%use",
|
|
mtptwidth, "Filesystem", unitname(units, 0));
|
|
} else {
|
|
printf("%-*s Type %s use avail %%use",
|
|
mtptwidth, "Filesystem", unitname(units, 0));
|
|
}
|
|
if (do_inode_stats)
|
|
printf(" iuse ifree %%iuse Mounted\n");
|
|
else
|
|
printf(" Mounted on\n");
|
|
}
|
|
|
|
/* Return a number formated in human format. Stolen from top. */
|
|
char *
|
|
number(df_blkcnt_t kbytes)
|
|
{
|
|
#define NSTR 4
|
|
static char bufs[NSTR][16];
|
|
static int index = 0;
|
|
char *p;
|
|
char tag;
|
|
double amt;
|
|
df_blkcnt_t mega = 1024; /* MB = 1024 KB */
|
|
df_blkcnt_t giga = (1024*mega); /* GB = 1024 MB */
|
|
df_blkcnt_t tera = (1024*giga); /* TB = 1024 GB */
|
|
|
|
p = bufs[index];
|
|
index = (index + 1) % NSTR;
|
|
|
|
if (kbytes == 0) {
|
|
strcpy(p, "0");
|
|
return (p);
|
|
}
|
|
|
|
amt = (double)kbytes;
|
|
|
|
if (amt >= tera) {
|
|
amt /= tera;
|
|
tag = 'T';
|
|
} else if (amt >= giga) {
|
|
amt /= giga;
|
|
tag = 'G';
|
|
} else if (amt >= mega) {
|
|
amt /= mega;
|
|
tag = 'M';
|
|
} else {
|
|
tag = 'K';
|
|
}
|
|
if (amt >= 10) {
|
|
sprintf(p, "%4.0f%c", amt, tag);
|
|
} else {
|
|
sprintf(p, "%4.1f%c", amt, tag);
|
|
}
|
|
return (p);
|
|
}
|
|
|
|
#define CEIL(val) \
|
|
( (val == (int) val) ? (int)val : ((int)val)+1)
|
|
double
|
|
ceil_f(double val) {
|
|
return ( (val == (int) val) ? val : val+1);
|
|
}
|
|
|
|
#define PERCENT(amount, total) \
|
|
ceil_f(((double)((amount) * 100.)) / ((double)(total)))
|
|
#define ZERO_PERCENT(amt, tot) \
|
|
((amt) = 0, (tot) = 1)
|
|
|
|
void
|
|
put_line(struct mntent *mntp)
|
|
{
|
|
struct statfs sfsb;
|
|
register df_blkcnt_t total;
|
|
register df_blkcnt_t used;
|
|
register long factor = 1;
|
|
|
|
if (local_fs_only && strcmp(mntp->mnt_type, MNTTYPE_EFS) != 0 &&
|
|
strcmp(mntp->mnt_type, MNTTYPE_XFS) != 0)
|
|
return;
|
|
if (!explicit_args && strcmp(mntp->mnt_type, MNTTYPE_DBG) == 0)
|
|
return;
|
|
if (!explicit_args && strcmp(mntp->mnt_type, MNTTYPE_FD) == 0)
|
|
return;
|
|
if (!explicit_args && strcmp(mntp->mnt_type, MNTTYPE_HWGFS) == 0)
|
|
return;
|
|
if (fs_type && mntp->mnt_type && strcmp(mntp->mnt_type, fs_type) != 0)
|
|
return;
|
|
|
|
/*
|
|
* Get filesystem statistics. If do_free_scan, get free counts
|
|
* the hard way.
|
|
*/
|
|
if (stat_entry(mntp, &sfsb) != 0)
|
|
return;
|
|
if (do_free_scan && scan_freelist(mntp, &sfsb) != 0)
|
|
return;
|
|
|
|
/*
|
|
* Compute total size and scale used and avail counts.
|
|
* Convert counts to 512-byte units if not already so.
|
|
* statfs block counts are in terms of fragment size, if
|
|
* fragment size is non-zero.
|
|
*/
|
|
if (sfsb.f_frsize >= BBSIZE)
|
|
factor = (sfsb.f_frsize >> BBSHIFT);
|
|
else if (sfsb.f_bsize >= BBSIZE)
|
|
factor = (sfsb.f_bsize >> BBSHIFT);
|
|
else
|
|
factor = 1; /* safety -XXX */
|
|
sfsb.f_blocks *= factor;
|
|
sfsb.f_bfree *= factor;
|
|
|
|
total = sfsb.f_blocks;
|
|
switch (units) {
|
|
case halfk: break;
|
|
case human:
|
|
/* Fall through */
|
|
case kb:
|
|
sfsb.f_bfree = BBTOK(sfsb.f_bfree);
|
|
sfsb.f_blocks = total = BBTOK(total);
|
|
break;
|
|
case mb:
|
|
sfsb.f_bfree = BBTOK(sfsb.f_bfree) >> 10;
|
|
sfsb.f_blocks = total = BBTOK(total) >> 10;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* handle the 'V' option for svr4
|
|
*/
|
|
|
|
if (Vflag) {
|
|
printf("df -F %s %s\n", mntp->mnt_type,
|
|
mntp->mnt_dir ? mntp->mnt_dir : mntp->mnt_fsname);
|
|
return;
|
|
}
|
|
/*
|
|
* Print the new line, calculating percentages.
|
|
*/
|
|
if (sfsb.f_blocks > 0)
|
|
used = sfsb.f_blocks - sfsb.f_bfree;
|
|
else
|
|
ZERO_PERCENT(used, sfsb.f_blocks);
|
|
/* don't truncate any longer. See comments at put_head() */
|
|
|
|
printf("%-*s ", mtptwidth, mntp->mnt_fsname);
|
|
|
|
if (eflag) {
|
|
printf("%7lld\n", (df_filcnt_t)sfsb.f_ffree);
|
|
return;
|
|
}
|
|
if (do_FStype)
|
|
printf("%7s", mntp->mnt_type);
|
|
else if (Pflag) {
|
|
if (units == human) {
|
|
printf("%4s %4s %5s %3d%% ",
|
|
number(total), number(used),
|
|
number(sfsb.f_bfree),
|
|
(int)PERCENT(used, sfsb.f_blocks));
|
|
} else {
|
|
printf("%10lld %8lld %7lld %3d%% ",
|
|
total, used,
|
|
(df_blkcnt_t)sfsb.f_bfree,
|
|
(int)PERCENT(used, sfsb.f_blocks));
|
|
}
|
|
} else {
|
|
if (units == human) {
|
|
printf("%7s %4s %4s %5s %3d%%",
|
|
mntp->mnt_type,
|
|
number(total), number(used),
|
|
number(sfsb.f_bfree),
|
|
(int)PERCENT(used, sfsb.f_blocks));
|
|
} else if (units == mb) {
|
|
printf("%7s %8lld %8lld %7lld %4d",
|
|
mntp->mnt_type, total, used,
|
|
(df_blkcnt_t)sfsb.f_bfree,
|
|
(int)PERCENT(used, sfsb.f_blocks));
|
|
} else {
|
|
printf("%7s %8lld %8lld %8lld %3d",
|
|
mntp->mnt_type, total, used,
|
|
(df_blkcnt_t)sfsb.f_bfree,
|
|
(int)PERCENT(used, sfsb.f_blocks));
|
|
}
|
|
}
|
|
|
|
if (do_inode_stats) {
|
|
register df_filcnt_t iused;
|
|
|
|
if (sfsb.f_files > 0)
|
|
iused = sfsb.f_files - sfsb.f_ffree;
|
|
else
|
|
ZERO_PERCENT(iused, sfsb.f_files);
|
|
if (units == human) {
|
|
printf(" %5s %7s %3d ",
|
|
number(iused/1000),
|
|
number((df_filcnt_t)sfsb.f_ffree / 1000),
|
|
(int)PERCENT(iused, sfsb.f_files));
|
|
} else {
|
|
printf(" %7lld %7lld %3d ",
|
|
iused, (df_filcnt_t)sfsb.f_ffree,
|
|
(int)PERCENT(iused, sfsb.f_files));
|
|
}
|
|
}
|
|
if (!do_FStype) {
|
|
if (mntp->mnt_dir != NULL)
|
|
printf(" %s", mntp->mnt_dir);
|
|
else
|
|
printf(" %-.6s", sfsb.f_fname);
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
int
|
|
stat_entry(struct mntent *mntp, struct statfs *sfbp)
|
|
{
|
|
register char *name;
|
|
register short fstyp;
|
|
static char typename[FSTYPSZ];
|
|
struct mntent *mnt2;
|
|
mountid_t mid;
|
|
|
|
/*
|
|
* Check the filesystem's root directory, or if we weren't given
|
|
* the root, the filesystem device.
|
|
*/
|
|
/*
|
|
* If its type is NFS, ping the server's null procedure before
|
|
* attempting a statfs(2) call, which might hang.
|
|
*/
|
|
if (!ping_nfs_server(mntp))
|
|
return ETIMEDOUT;
|
|
name = mntp->mnt_dir;
|
|
if (name != NULL) {
|
|
/*
|
|
* We have a mounted filesystem. If its type is NFS, ping
|
|
* the server's null procedure before attempting a statfs(2)
|
|
* call, which might hang.
|
|
*/
|
|
fstyp = 0;
|
|
if (statfs(name, sfbp, sizeof *sfbp, fstyp) == 0) {
|
|
fstyp = sfbp->f_fstyp;
|
|
} else {
|
|
errors++;
|
|
return(error(ERR_status, name, 0));
|
|
}
|
|
if (rtflag)
|
|
xfs_getrt(name, sfbp);
|
|
} else {
|
|
/*
|
|
* If no directory is given, assume mntp->mnt_fsname is
|
|
* an unmounted filesystem device or a file contained in
|
|
* the filesystem of interest, and loop over type indices
|
|
* trying to statfs it.
|
|
*/
|
|
register short nfstyp;
|
|
|
|
name = mntp->mnt_fsname;
|
|
nfstyp = sysfs(GETNFSTYP);
|
|
for (fstyp = 1; fstyp < nfstyp; fstyp++) {
|
|
if (statfs(name, sfbp, sizeof *sfbp, fstyp) == 0)
|
|
break;
|
|
}
|
|
if (fstyp == nfstyp) {
|
|
/*
|
|
* Not an unmounted filesystem device. Check for
|
|
* a file in some filesystem.
|
|
*/
|
|
fstyp = 0;
|
|
if (statfs(name, sfbp, sizeof *sfbp, fstyp) == 0) {
|
|
fstyp = sfbp->f_fstyp;
|
|
if ((my_getmountid(name, &mid) == 0) &&
|
|
(mnt2 = getmntbyid(&mid))) {
|
|
*mntp = *mnt2;
|
|
mntp->mnt_fsname = name;
|
|
}
|
|
} else {
|
|
errors++;
|
|
return(error(ERR_status, name, 0));
|
|
}
|
|
if (rtflag)
|
|
xfs_getrt(name, sfbp);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
pstatfs(mntp->mnt_fsname, sfbp);
|
|
#endif
|
|
|
|
/*
|
|
* Now get the filesystem type's name and set it in mntp if it's
|
|
* not already set.
|
|
*/
|
|
if (mntp->mnt_type == NULL) {
|
|
if (fstyp == 0
|
|
|| fstyp != sfbp->f_fstyp
|
|
|| sysfs(GETFSTYP, fstyp, typename) < 0) {
|
|
errors++;
|
|
return(error(ERR_status, name, 0));
|
|
}
|
|
mntp->mnt_type = typename;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NFS-specific code.
|
|
*/
|
|
#include <netdb.h>
|
|
#include <rpc/rpc.h>
|
|
#include <sys/fs/nfs.h>
|
|
#include <sys/socket.h>
|
|
|
|
#define WAIT 1 /* wait one second before first retransmission */
|
|
#define TOTAL 3 /* wait no more than three seconds (two tries) */
|
|
|
|
int
|
|
ping_nfs_server(struct mntent *mntp)
|
|
{
|
|
char *fsname, *cp;
|
|
int len, sock;
|
|
static int hostlen;
|
|
static char host[MAXHOSTNAMELEN+1];
|
|
struct hostent *hp;
|
|
struct sockaddr_in sin;
|
|
struct timeval tv;
|
|
CLIENT *client;
|
|
static enum clnt_stat clnt_stat;
|
|
|
|
if (!mntp->mnt_type || ((strcmp(mntp->mnt_type, MNTTYPE_NFS) != 0) &&
|
|
(strcmp(mntp->mnt_type, MNTTYPE_NFS2) != 0) &&
|
|
(strcmp(mntp->mnt_type, MNTTYPE_NFS3) != 0) &&
|
|
(strcmp(mntp->mnt_type, MNTTYPE_CACHEFS) != 0)))
|
|
return 1;
|
|
|
|
fsname = mntp->mnt_fsname;
|
|
cp = strchr(fsname, ':');
|
|
if (cp == NULL)
|
|
return 1;
|
|
len = cp - fsname;
|
|
if (len >= sizeof host)
|
|
return 1;
|
|
if (len == hostlen && strncmp(fsname, host, len) == 0)
|
|
return clnt_stat != RPC_TIMEDOUT;
|
|
hostlen = len;
|
|
(void) strncpy(host, fsname, len);
|
|
host[len] = '\0';
|
|
|
|
hp = gethostbyname(host);
|
|
if (hp == NULL || hp->h_addrtype != AF_INET)
|
|
return 1;
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = NFS_PORT;
|
|
bcopy(hp->h_addr, &sin.sin_addr, sizeof sin.sin_addr);
|
|
bzero(sin.sin_zero, sizeof sin.sin_zero);
|
|
|
|
tv.tv_sec = WAIT;
|
|
tv.tv_usec = 0;
|
|
sock = RPC_ANYSOCK;
|
|
client = clntudp_create(&sin, NFS_PROGRAM, NFS_VERSION, tv, &sock);
|
|
if (client == NULL)
|
|
return 1;
|
|
|
|
tv.tv_sec = TOTAL;
|
|
clnt_stat = clnt_call(client, RFS_NULL, xdr_void, 0, xdr_void, 0, tv);
|
|
clnt_destroy(client);
|
|
if (clnt_stat == RPC_TIMEDOUT) {
|
|
errors++;
|
|
error(ERR_NFSservnotresp, mntp->mnt_dir, host);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* EFS-dependent code.
|
|
*/
|
|
#include <values.h>
|
|
#include <sys/fs/efs_fs.h>
|
|
#include <sys/fs/efs_ino.h>
|
|
#include <sys/fs/efs_sb.h>
|
|
|
|
daddr_t efs_freescan(int fd, struct efs *sp);
|
|
daddr_t efs_freescancg(int fd, struct efs *sp, int cg);
|
|
|
|
int
|
|
scan_freelist(struct mntent *mntp, struct statfs *sfbp)
|
|
{
|
|
register int fd;
|
|
register daddr_t bfree;
|
|
union {
|
|
char block[BBSIZE];
|
|
#ifdef Fs_MAGIC
|
|
struct filsys bell;
|
|
#endif /* Fs_MAGIC */
|
|
struct efs efs;
|
|
} super;
|
|
#ifdef Fs_MAGIC
|
|
daddr_t bell_freescan();
|
|
#endif /* Fs_MAGIC */
|
|
char *rawpath;
|
|
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_EFS) != 0)
|
|
return 0;
|
|
|
|
rawpath = findrawpath(mntp->mnt_fsname);
|
|
if ((fd = open(rawpath ? rawpath : mntp->mnt_fsname, 0)) < 0) {
|
|
errors++;
|
|
return(error(ERR_open, mntp->mnt_fsname, 0));
|
|
}
|
|
if (bread(fd, EFS_SUPERBB, (char *) &super, sizeof super)) {
|
|
close(fd);
|
|
errors++;
|
|
return(error(ERR_readsb, mntp->mnt_fsname, 0));
|
|
}
|
|
if (IS_EFS_MAGIC(super.efs.fs_magic)) {
|
|
bfree = efs_freescan(fd, &super.efs);
|
|
#ifdef FsMAGIC
|
|
} else if (super.bell.s_magic == FsMAGIC) {
|
|
bfree = bell_freescan(fd, &super.bell, mntp->mnt_fsname);
|
|
#endif /* FsMAGIC */
|
|
} else {
|
|
errors++;
|
|
error(ERR_supblk, mntp->mnt_fsname, 0);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
if (bfree < 0) {
|
|
errors++;
|
|
error(ERR_freelist, mntp->mnt_fsname, 0);
|
|
return -1;
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s freescan %ld, bfree %lld\n",
|
|
mntp->mnt_fsname, bfree, (df_blkcnt_t)sfbp->f_bfree);
|
|
#endif
|
|
sfbp->f_bfree = (df_blkcnt_t)bfree;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef FsMAGIC
|
|
daddr_t
|
|
bell_freescan(fd, sp, filesys)
|
|
int fd;
|
|
struct filsys *sp;
|
|
char *filesys;
|
|
{
|
|
register daddr_t freeblocks;
|
|
register int i;
|
|
register daddr_t b;
|
|
struct fblk fbuf;
|
|
|
|
freeblocks = 0;
|
|
|
|
/*
|
|
* copy superblock free info to private fblk buf.
|
|
*/
|
|
fbuf.df_nfree = sp->s_nfree;
|
|
bcopy((char *)sp->s_free, (char *)fbuf.df_free, sizeof sp->s_free);
|
|
b = 0;
|
|
|
|
for (;;) {
|
|
if (fbuf.df_nfree == 0)
|
|
break;
|
|
|
|
if (fbuf.df_nfree < 0 || fbuf.df_nfree > NICFREE) {
|
|
error(ERR_badfreecnt, (char *)b, filesys);
|
|
return -1;
|
|
}
|
|
|
|
freeblocks += fbuf.df_nfree;
|
|
|
|
b = fbuf.df_free[0];
|
|
if (b == 0) {
|
|
freeblocks--;
|
|
break;
|
|
}
|
|
|
|
if (b < sp->s_isize || b >= sp->s_fsize) {
|
|
error(ERR_badfreeblk, (char *)b, filesys);
|
|
return -1;
|
|
}
|
|
|
|
if (bread(fd, b, (char *) &fbuf, sizeof fbuf)) {
|
|
error(ERR_rdfreeblk, (char *)b, filesys);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return freeblocks;
|
|
}
|
|
#endif /* FsMAGIC */
|
|
|
|
int
|
|
bread(int fd, daddr_t bno, char *bp, int cnt)
|
|
{
|
|
register int n;
|
|
register int nb;
|
|
|
|
nb = (int)BTOBB(cnt);
|
|
if ((n = readb(fd, bp, bno, nb)) != nb)
|
|
return (n < 0) ? errno : EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
daddr_t
|
|
efs_freescan(int fd, struct efs *sp)
|
|
{
|
|
int cg, nf, nfree = 0;
|
|
|
|
for (cg = 0; cg < sp->fs_ncg; cg++)
|
|
if ((nf = efs_freescancg(fd, sp, cg)) < 0)
|
|
return -1;
|
|
else
|
|
nfree += nf;
|
|
return nfree;
|
|
}
|
|
|
|
/* bn to byte offset of bm block */
|
|
#define EFS_BMBOFF(bn) (((bn) & 07777) >> 3)
|
|
|
|
/* bn to block offset in bitmap */
|
|
#define EFS_BMBB(bn) ((bn) >> 12)
|
|
|
|
/* bn to bit "offset" in bitmap byte */
|
|
#define EFS_BMBIT(bn) ((bn) & 07)
|
|
|
|
#define EFS_BMBASE(fs) ((fs)->fs_bmblock ? (fs)->fs_bmblock : EFS_BITMAPBB)
|
|
|
|
daddr_t
|
|
efs_freescancg(int fd, struct efs *sp, int cg)
|
|
{
|
|
int ib, nfree = 0;
|
|
daddr_t nb, bn = EFS_CGIMIN(sp, cg) + sp->fs_cgisize;
|
|
daddr_t lastbn = EFS_CGIMIN(sp, cg + 1) - 1;
|
|
daddr_t bmbn = EFS_BMBASE(sp) + EFS_BMBB(bn);
|
|
daddr_t lastbmbn = EFS_BMBASE(sp) + EFS_BMBB(lastbn);
|
|
char b, *cp, *bp = (char *)malloc((nb = lastbmbn - bmbn + 1) * BBSIZE);
|
|
if (bp == NULL)
|
|
return -1;
|
|
|
|
if (readb(fd, bp, bmbn, nb) != nb)
|
|
return -1;
|
|
|
|
cp = bp + EFS_BMBOFF(bn);
|
|
b = *cp;
|
|
b >>= EFS_BMBIT(bn);
|
|
ib = BITSPERBYTE - EFS_BMBIT(bn);
|
|
for (; bn <= lastbn; cp++, ib=BITSPERBYTE, b = *cp) /* each byte */
|
|
for (; bn <= lastbn && ib--; bn++) { /* each bit */
|
|
if (b & 01)
|
|
nfree++;
|
|
b >>= 1;
|
|
}
|
|
free(bp);
|
|
return nfree;
|
|
}
|
|
|
|
/*
|
|
* Get xfs realtime space information and add it to sfbp.
|
|
* Don't complain if it doesn't work.
|
|
* Don't worry if it doesn't fit in 32 bits...
|
|
*/
|
|
void
|
|
xfs_getrt(char *name, struct statfs *sfbp)
|
|
{
|
|
int bsize;
|
|
xfs_fsop_counts_t cnt;
|
|
int factor;
|
|
int fd;
|
|
xfs_fsop_geom_t geo;
|
|
|
|
fd = open(name, O_RDONLY);
|
|
if (fd < 0)
|
|
return;
|
|
if (syssgi(SGI_XFS_FSOPERATIONS, fd, XFS_FS_GEOMETRY, (void *)0, &geo) < 0 ||
|
|
syssgi(SGI_XFS_FSOPERATIONS, fd, XFS_FS_COUNTS, (void *)0, &cnt) < 0) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
close(fd);
|
|
if (!geo.rtblocks)
|
|
return;
|
|
bsize = sfbp->f_frsize ? sfbp->f_frsize : sfbp->f_bsize;
|
|
factor = geo.blocksize / bsize; /* currently this is == 1 */
|
|
sfbp->f_blocks += geo.rtblocks * factor;
|
|
sfbp->f_bfree += (cnt.freertx * geo.rtextsize) * factor;
|
|
}
|
|
|
|
/* truncate arbitarily long arguments to a defined max value */
|
|
|
|
static void chknsnip(s, max)
|
|
char *s;
|
|
unsigned int max;
|
|
{
|
|
char *p;
|
|
|
|
if(!s)
|
|
return;
|
|
|
|
if(strlen(s) >= max) {
|
|
p=s;
|
|
p+=(max-1);
|
|
*p='\0';
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
pstatfs(char *name, struct statfs *sfbp)
|
|
{
|
|
fprintf(stderr, "\
|
|
statfs(%s) = {\n\
|
|
fstyp %d,\n\
|
|
bsize %ld,\n\
|
|
frsize %ld,\n\
|
|
blocks %lld,\n\
|
|
bfree %lld,\n\
|
|
files %lld,\n\
|
|
ffree %lld,\n\
|
|
fname %.6s,\n\
|
|
fpack %.6s\n\
|
|
}\n",
|
|
name,
|
|
sfbp->f_fstyp,
|
|
sfbp->f_bsize,
|
|
sfbp->f_frsize,
|
|
(df_blkcnt_t)sfbp->f_blocks,
|
|
(df_blkcnt_t)sfbp->f_bfree,
|
|
(df_filcnt_t)sfbp->f_files,
|
|
(df_filcnt_t)sfbp->f_ffree,
|
|
sfbp->f_fname,
|
|
sfbp->f_fpack);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
my_getmountid(const char *name, mountid_t *mid)
|
|
{
|
|
struct stat s;
|
|
int i;
|
|
int len;
|
|
char *p;
|
|
|
|
if (lstat(name, &s) < 0 || !S_ISLNK(s.st_mode) || !(len = s.st_size))
|
|
p = (char *)name;
|
|
else if (stat(name, &s) < 0)
|
|
p = (char *)name;
|
|
else if (S_ISDIR(s.st_mode)) {
|
|
p = malloc(strlen(name) + 3);
|
|
sprintf(p, "%s/.", name);
|
|
} else {
|
|
p = malloc(len + 1);
|
|
if (readlink(name, p, len) != len) {
|
|
free(p);
|
|
p = (char *)name;
|
|
} else {
|
|
p[len] = '\0';
|
|
i = my_getmountid(p, mid);
|
|
free(p);
|
|
return i;
|
|
}
|
|
}
|
|
i = getmountid(p, mid);
|
|
if (p != name)
|
|
free(p);
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Look for an exact match in the mtab file based on
|
|
* mnt_fsname or mnt_dir.
|
|
*/
|
|
int
|
|
my_getmntany(FILE *mtabp, struct mntent *mntent, char *mntpnt)
|
|
{
|
|
struct mntent mntpref;
|
|
|
|
bzero(&mntpref, sizeof(mntpref));
|
|
mntpref.mnt_fsname = mntpnt;
|
|
rewind(mtabp);
|
|
if (getmntany(mtabp, mntent, &mntpref) == 0)
|
|
return 0;
|
|
|
|
/* now try the match on mnt_dir */
|
|
mntpref.mnt_fsname = NULL;
|
|
mntpref.mnt_dir = mntpnt;
|
|
rewind(mtabp);
|
|
if (getmntany(mtabp, mntent, &mntpref) == 0)
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|