home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char SCCSID[] = "@(#) fsinfo.c 1.25 93/08/18 00:09:06";
- #endif
-
- /*
- * "du" enhanced disk usage summary - version 2.
- *
- * Copyright 1990-1993, Unicom Systems Development. All rights reserved.
- * See accompanying README file for terms of distribution and use.
- *
- * Edit at tabstops=4.
- */
-
- /*
- * All of the machine-specific filesystem support should be encapsulated
- * within this one file. We maintain the necessary filesystem information
- * as a dynamically allocated list of (struct fsinfo) records, one record
- * per mounted filesystem.
- *
- * There are four routines for working with filesystem information.
- *
- * fs_initinfo() - Initialize the "Fsinfo_list" filesystem information.
- * fs_getinfo() - Gets filesystem information on a entry.
- * fs_linkdone() - Determines whether a file has been visited already.
- * fs_numblocks() - Calculates disk usage of an entry.
- *
- * One of the trickiest tasks is discovering what filesystems are mounted.
- * This is *highly* system specific. We also provide a set of three
- * routines -- open_mnttab(), read_mnttab(), and close_mnttab() -- for
- * fs_initinfo() to use to get this information.
- */
-
- #include "config.h"
- #include <stdio.h>
- #include <string.h>
- #ifdef USE_UNISTD
- # include <unistd.h>
- #endif
- #include <sys/types.h>
- #include <sys/stat.h>
- #include "du.h"
-
- /*
- * Within each filesystem information record, we maintain a dynamically
- * allocated bit vector that indicates which inodes have been already
- * visited on the filesystem. If the system has a working statfs() call
- * then we can preallocate the bit vector to precisely the right size.
- * If statfs() is missing (e.g. old SysV) or broken (e.g. the Neurotic
- * File System) we will instead make a guess and grow the bit vector as
- * needed. In this case, the allocation increment will be in chunks of
- * ITABINCR/8 bytes (since one byte will hold the status of 8 inodes).
- * Selection of this value is a simple speed vs memory tradeoff.
- */
- #define ITABINCR 8192
-
- /*
- * Length of bit vector required for a particular number of inodes.
- */
- #define NUMINO_TO_VECLEN(NUMINO) ((NUMINO)/8 + 1)
-
- /*
- * Can you believe this shit?
- */
- #ifdef USE_MOUNT_MNTTAB
- # include <mnttab.h>
- # define mount_struct mnttab
- # define mount_device mt_dev
- # define mount_point mt_filsys
- #endif
- #ifdef USE_MOUNT_R4MNTTAB
- # include <sys/mnttab.h>
- # define mount_struct mnttab
- # define mount_device mnt_special
- # define mount_point mnt_mountp
- #endif
- #ifdef USE_MOUNT_MNTENT
- # include <mntent.h>
- # define mount_struct mntent
- # define mount_device mnt_fsname
- # define mount_point mnt_dir
- #endif
- #ifdef USE_MOUNT_FSTAB
- # include <fstab.h>
- # define mount_struct fstab
- # define mount_device fs_spec
- # define mount_point fs_file
- #endif
- #ifdef USE_MOUNT_MNTCTL
- # include <sys/mntctl.h>
- # include <sys/vmount.h>
- struct mount_struct {
- char *mount_device;
- char *mount_point;
- };
- #endif
-
- /*
- * sigh...
- */
- #ifdef USE_STATFS_SYSV
- # include <sys/statfs.h>
- # define STATFS(MNT, PTR, PSIZE, FSTYPE) \
- statfs(MNT, PTR, PSIZE, FSTYPE)
- #endif
- #ifdef USE_STATFS_BSD
- # include <sys/mount.h>
- # define STATFS(MNT, PTR, PSIZE, FSTYPE) \
- statfs(MNT, PTR)
- #endif
- #ifdef USE_STATFS_SUN
- # include <sys/vfs.h>
- # define STATFS(MNT, PTR, PSIZE, FSTYPE) \
- statfs(MNT, PTR, PSIZE, FSTYPE)
- #endif
- #ifdef USE_STATFS_HPUX
- # include <sys/vfs.h>
- # define STATFS(MNT, PTR, PSIZE, FSTYPE) \
- statfs(MNT, PTR)
- #endif
- #ifdef USE_STATFS_NONE
- # include <sys/param.h>
- struct statfs {
- long f_bsize, f_files;
- };
- # define STATFS(MNT, PTR, PSIZE, FSTYPE) \
- ((PTR)->f_bsize = BSIZE, (PTR)->f_files = 0, 0)
- #endif
-
-
- /*
- * Dynamically allocated list of filesystem information records.
- */
- struct fsinfo **Fsinfo_list;
- int Fsinfo_size;
-
- /*
- * Mount table handling routines.
- */
- int open_mnttab __ARGS((void));
- struct mount_struct *read_mnttab __ARGS((void));
- int close_mnttab __ARGS((void));
-
-
- /*
- * fs_initinfo() - Initialize the "Fsinfo_list" filesystem information.
- */
- void fs_initinfo()
- {
- struct fsinfo *fsp;
- struct mount_struct *mnt;
- struct stat sbuf;
- struct statfs fsbuf;
- int n;
-
- Fsinfo_list = (struct fsinfo **) xmalloc(sizeof(struct fsinfo *));
- Fsinfo_list[0] = NULL;
- Fsinfo_size = 0;
-
- /*
- * Open up the mount table.
- */
- if (open_mnttab() != 0)
- errmssg(ERR_ABORT, errno, "could not open mount table");
-
- /*
- * Initialize a filesystem information record for each mounted filesystem.
- */
- while ((mnt = read_mnttab()) != NULL) {
-
- /*
- * Get the information on this filesystem.
- */
- if (stat(mnt->mount_point, &sbuf) != 0) {
- errmssg(ERR_WARN, errno, "could not stat \"%s\"", mnt->mount_point);
- continue;
- }
- if (STATFS(mnt->mount_point, &fsbuf, sizeof(struct statfs), 0) != 0) {
- errmssg(ERR_WARN, errno,
- "could not statfs \"%s\"", mnt->mount_point);
- continue;
- }
-
- /*
- * Allocate and initialize the filesystem information structure.
- * If "f_files" is zero then we take an initial guess at the
- * number of inodes. This will happen if this system does not
- * have a statfs() call or this is an NFS filesystem.
- */
- fsp = (struct fsinfo *) xmalloc(sizeof(struct fsinfo));
- fsp->dev = sbuf.st_dev;
- fsp->nino = (fsbuf.f_files > 0 ? fsbuf.f_files : ITABINCR);
- fsp->bsize = fsbuf.f_bsize;
- fsp->nindir = fsp->bsize / sizeof(daddr_t);
- fsp->remote = (sbuf.st_dev < 0) ||
- (strchr(mnt->mount_device, ':') != NULL);
-
- #ifdef BROKE_STBLOCKS
- /* very cool algorithm by Lars Henrik Mathiesen <thorinn@diku.dk> */
- if (sbuf.st_blocks == 0) {
- errmssg(ERR_WARN, 0,
- "cannot determine \"%s\" stat blksize - assuming 512",
- mnt->mount_device);
- fsp->stbsize = 512;
- } else {
- int round, ratio;
- round = (sbuf.st_size + fsbuf.f_bsize - 1) & ~(fsbuf.f_bsize - 1);
- ratio = round / sbuf.st_blocks;
- for (fsp->stbsize = 512 ; fsp->stbsize < ratio ; fsp->stbsize <<= 1)
- ;
- }
- #endif
-
- /*
- * Determine how long pathname buffers should be for
- * entries on this filesystem.
- */
- fsp->path_max = max_path_len(mnt->mount_point);
-
- /*
- * Create the bit vector that is used by fs_linkdone() to track
- * which inodes have already been encountered.
- */
- n = NUMINO_TO_VECLEN(fsp->nino);
- fsp->idone = (unsigned char *) xmalloc((unsigned)n);
- (void) memset((PTRTYPE *)fsp->idone, 0, n);
-
- /*
- * Attach the filesystem information to the end of the list.
- */
- Fsinfo_list = (struct fsinfo **) xrealloc((PTRTYPE *)Fsinfo_list,
- (Fsinfo_size+2)*sizeof(struct fsinfo *));
- Fsinfo_list[Fsinfo_size++] = fsp;
- Fsinfo_list[Fsinfo_size] = NULL;
-
- #ifdef DEBUG
- Dprintf(stderr, "*** mount_device=\"%s\" mount_point=\"%s\"\n",
- mnt->mount_device, mnt->mount_point);
- Dprintf(stderr, " remote=%s dev=%d nino=%d bsize=%d nindir=%d\n",
- (fsp->remote ? "TRUE" : "FALSE"),
- fsp->dev, fsp->nino, fsp->bsize, fsp->nindir);
- #ifdef BROKE_STBLOCKS
- Dprintf(stderr, " stbsize=%d", fsp->stbsize);
- #endif
- Dprintf(stderr, " path_max=%d\n", fsp->path_max);
- #endif
-
- }
-
- (void) close_mnttab();
-
- }
-
-
- /*
- * fs_getinfo() - Gets filesystem information on a entry.
- *
- * Given the inode info for an entry, locate the corresponding filesystem
- * info record and return a pointer to it. The most recent result is cached
- * to reduce the number of list searches.
- */
- struct fsinfo *fs_getinfo(sbufp)
- register struct stat *sbufp;
- {
- static struct fsinfo *fsp_save = NULL;
- register struct fsinfo **fsp;
-
- if (fsp_save != NULL && fsp_save->dev == sbufp->st_dev)
- return fsp_save;
- for (fsp = Fsinfo_list ; *fsp != NULL ; ++fsp) {
- if ((*fsp)->dev == sbufp->st_dev) {
- fsp_save = *fsp;
- return fsp_save;
- }
- }
- return (struct fsinfo *) NULL;
- }
-
-
- /*
- * fs_linkdone() - Determines whether a file has been visited already.
- *
- * This procedure implements the logic to avoid recounting of multiply
- * linked files. Each (struct fsinfo) contains a bit vector to track
- * which inodes have already been visited. The first time this procedure
- * is called for a particular inode, we return FALSE and mark it in the bit
- * vector. TRUE is returned subsequent times this procedure is called for
- * the same inode.
- */
- int fs_linkdone(fsp, sbufp)
- register struct fsinfo *fsp;
- register struct stat *sbufp;
- {
- register unsigned char *rowp;
- int mask;
- unsigned old_bytes, new_bytes;
-
- /*
- * Verify we haven't gone off the edge of the bit vector. This
- * could happen if we had to take an initial guess at the number
- * of inodes.
- */
- if (sbufp->st_ino > fsp->nino) {
-
- old_bytes = NUMINO_TO_VECLEN(fsp->nino);
- #ifdef DEBUG
- Dprintf(stderr, "*** growing bit vector for device %d at inode %d\n",
- fsp->dev, sbufp->st_ino);
- Dprintf(stderr, " old vector size %d bytes (%d inodes)\n",
- old_bytes, fsp->nino);
- #endif
- while (sbufp->st_ino > fsp->nino)
- fsp->nino += ITABINCR;
- new_bytes = NUMINO_TO_VECLEN(fsp->nino);
- #ifdef DEBUG
- Dprintf(stderr, " new vector size %d bytes (%d inodes)\n",
- new_bytes, fsp->nino);
- #endif
-
- fsp->idone =
- (unsigned char *)xrealloc((PTRTYPE *)fsp->idone, new_bytes);
- (void) memset((PTRTYPE *)(fsp->idone+old_bytes),
- 0, new_bytes-old_bytes);
- }
-
- /*
- * Locate the bit within the vector for this inode.
- */
- rowp = fsp->idone + (sbufp->st_ino >> 3);
- mask = 1 << (sbufp->st_ino & 07);
-
- /*
- * If the bit is set then this link was already done.
- */
- if (*rowp & mask)
- return TRUE;
-
- /*
- * Set the bit and indicate the link hasn't been done yet.
- */
- *rowp |= mask;
- return FALSE;
- }
-
-
- /*
- * A classic UNIX filesys contains ten data block addresses in the inode.
- */
- #define DIRBLKS 10
-
- /*
- * Macro to calculate ceiling(A/B) pretty efficiently.
- */
- #define CEIL_DIV(A, B) (((A)+(B)-1) / (B))
-
- /*
- * fs_numblocks() - Calculates disk usage of an entry.
- *
- * This routine is a profiling "hot spot". It is called for every entry,
- * and about 15% of the time appears to be spent here. Unfortunately, the
- * calculation this routine has to implement is apparently easy to botch given
- * the number of vendor's du's that get it wrong. Therefore, I traded a
- * bit of speed for readability and clarity. Even still, my benchmarking
- * tests show that it stands up well to other implementations.
- */
- long fs_numblocks(fsp, sbufp)
- register struct fsinfo *fsp;
- struct stat *sbufp;
- {
-
- register long n_used; /* num blocks used, incl overhead */
-
- #ifdef USE_STBLOCKS /*{*/
-
- /*
- * Ignore this entry if we are counting usage for a specific
- * user and that user doesn't own this entry.
- */
- if (Selected_user >= 0 && sbufp->st_uid != Selected_user)
- return 0L;
-
- #ifdef BROKE_STBLOCKS
- # define STBSIZE(fsp) ((fsp)->stbsize)
- #else
- # define STBSIZE(fsp) (512)
- #endif
-
- /*
- * This is a piece of cake on systems with "st_blocks".
- */
- if (Report_blksize == STBSIZE(fsp))
- n_used = sbufp->st_blocks;
- else if (Report_blksize == 0)
- n_used = CEIL_DIV(sbufp->st_blocks*STBSIZE(fsp), fsp->bsize);
- else
- n_used = CEIL_DIV(sbufp->st_blocks*STBSIZE(fsp), Report_blksize);
-
- #else /*}!USE_STBLOCKS{*/
-
- register long n_to_place; /* num data blocks to be placed */
- long n_single_ind; /* scratch single indirect block cntr */
- long n_double_ind; /* scratch double indirect block cntr */
-
- /*
- * Ignore this entry if we are counting usage for a specific
- * user and that user doesn't own this entry.
- */
- if (Selected_user >= 0 && sbufp->st_uid != Selected_user)
- return 0L;
-
- /*
- * Determine the number of data blocks required to store this file.
- */
- n_used = CEIL_DIV(sbufp->st_size, fsp->bsize);
-
- /*
- * The first DIRBLKS addresses are stored directly in the inode
- * and thus require no additional overhead. Figure out how many
- * data blocks remain to be placed through indirect addresses.
- */
- n_to_place = n_used - DIRBLKS;
-
- /*
- * If the file has DIRBLKS or less data blocks then the entire
- * file can be stored in direct data blocks, there is no indirect
- * block overhead, and thus we are done.
- */
- if (n_to_place <= 0)
- goto done;
-
- /*
- * With the single indirect block, we can get another "nindir" blocks.
- */
- ++n_used;
- n_to_place -= fsp->nindir;
- if (n_to_place <= 0)
- goto done;
-
- /*
- * With the double indirect block, we can get another "nindir" single
- * indirect blocks, for a total of another "nindir**2" data blocks.
- */
- n_single_ind = CEIL_DIV(n_to_place, fsp->nindir);
- if (n_single_ind > fsp->nindir)
- n_single_ind = fsp->nindir;
- n_used += 1 + n_single_ind;
- n_to_place -= n_single_ind * fsp->nindir ;
- if (n_to_place <= 0)
- goto done;
-
- /*
- * With the triple indirect block, we can get another "nindir" double
- * indirect blocks, for another "nindir**2" single indirect blocks, for
- * a total of another "nindir**3" data blocks.
- */
- n_single_ind = CEIL_DIV(n_to_place, fsp->nindir);
- n_double_ind = CEIL_DIV(n_single_ind, fsp->nindir);
- n_used += 1 + n_double_ind + n_single_ind;
-
- done:
-
- /*
- * If required, convert from native blocksize to reporting blocksize.
- */
- if (Report_blksize != 0 && Report_blksize != fsp->bsize)
- n_used = CEIL_DIV(n_used*fsp->bsize, Report_blksize);
-
- #endif /*}!USE_STBLOCKS*/
-
- return n_used;
-
- }
-
-
- /*****************************************************************************
- *
- * Mount Table Access Routines
- *
- * int open_mnttab();
- * Initialize for mount table scan. Return zero on success.
- *
- * struct mount_struct *read_mnttab();
- * Return information on next mount entry; NULL at end of table.
- *
- * int close_mnttab();
- * Close the mount table. Return zero on success, nonzero on error.
- *
- ****************************************************************************/
-
-
- #ifdef USE_MOUNT_MNTTAB /*{*/
-
- #ifndef MNTTAB
- # ifdef PNMNTTAB
- # define MNTTAB PNMNTTAB
- # else
- # define MNTTAB "/etc/mnttab"
- # endif
- #endif
-
- #ifndef ISMNTFREE
- # define ISMNTFREE(mp) ((mp)->mt_dev[0] == '\0')
- #endif
-
- static FILE *fpmnt;
-
- int open_mnttab()
- {
- return ((fpmnt = fopen(MNTTAB, "r")) != NULL ? 0 : -1);
- }
-
- struct mnttab *read_mnttab()
- {
- static struct mnttab mbuf;
- while (fread((PTRTYPE *)&mbuf, sizeof(mbuf), 1, fpmnt) == 1) {
- if (!ISMNTFREE(&mbuf))
- return &mbuf;
- }
- return (struct mnttab *)NULL;
- }
-
- int close_mnttab()
- {
- return fclose(fpmnt);
- }
-
- #endif /*} USE_MOUNT_MNTTAB*/
-
-
- /****************************************************************************/
-
-
- #ifdef USE_MOUNT_R4MNTTAB /*{*/
-
- #ifndef MNTTAB
- # define MNTTAB "/etc/mnttab"
- #endif
-
- static FILE *fpmnt;
-
- int open_mnttab()
- {
- return ((fpmnt = fopen(MNTTAB, "r")) != NULL ? 0 : -1);
- }
-
- struct mnttab *read_mnttab()
- {
- static struct mnttab mbuf;
- mntnull(&mbuf);
- return (getmntent(fpmnt, &mbuf) == 0 ? &mbuf : (struct mnttab *)NULL);
- }
-
- int close_mnttab()
- {
- return fclose(fpmnt);
- }
-
- #endif /*} USE_MOUNT_R4MNTTAB*/
-
-
- /****************************************************************************/
-
-
- #ifdef USE_MOUNT_MNTENT /*{*/
-
- #ifndef MNT_MNTTAB
- # define MNT_MNTTAB "/etc/mtab"
- #endif
-
- static FILE *fpmnt;
-
- int open_mnttab()
- {
- return ((fpmnt = setmntent(MNT_MNTTAB, "r")) != NULL ? 0 : -1);
- }
-
- struct mntent *read_mnttab()
- {
- return getmntent(fpmnt);
- }
-
- int close_mnttab()
- {
- return endmntent(fpmnt);
- }
-
- #endif /*}USE_MOUNT_MNTENT*/
-
-
- /****************************************************************************/
-
-
- #ifdef USE_MOUNT_FSTAB /*{*/
-
- int open_mnttab()
- {
- return (setfsent() ? 0 : -1);
- }
-
- struct fstab *read_mnttab()
- {
- return getfsent();
- }
-
- int close_mnttab()
- {
- endfsent();
- return 0;
- }
-
- #endif /*}USE_MOUNT_FSTAB*/
-
-
- /****************************************************************************/
-
-
- #ifdef USE_MOUNT_MNTCTL /*{*/
-
- /*
- * Here is a fine example of what happens when you have ivory tower
- * doctorates instead of experienced system programmers designing stuff.
- */
-
- static char *mntctl_buf;
- static char *mntctl_entry;
- static int mntctl_fscount;
-
- int open_mnttab()
- {
- unsigned n;
-
- if (mntctl(MCTL_QUERY, sizeof(n), (char *)&n) != 0)
- return -1;
- n += 1024;
- mntctl_buf = xmalloc(n);
- if ((mntctl_fscount = mntctl(MCTL_QUERY, n, mntctl_buf)) == -1)
- return -1;
- mntctl_entry = mntctl_buf;
- return 0;
- }
-
- struct mount_struct *read_mnttab()
- {
- struct vmount *v;
- unsigned n, m;
- static struct mount_struct mbuf = { NULL, NULL };
-
- if (mntctl_fscount <= 0)
- return (struct mount_struct *) NULL;
-
- v = (struct vmount *)mntctl_entry;
- mntctl_entry += v->vmt_length;
- --mntctl_fscount;
-
- if (mbuf.mount_device != NULL)
- free(mbuf.mount_device);
- if (mbuf.mount_point != NULL)
- free(mbuf.mount_point);
-
- n = vmt2datasize(v, VMT_STUB);
- mbuf.mount_point = xmalloc(n+1);
- (void) strncpy(mbuf.mount_point, vmt2dataptr(v, VMT_STUB), n);
- mbuf.mount_point[n] = '\0';
-
- n = vmt2datasize(v, VMT_HOST);
- m = vmt2datasize(v, VMT_OBJECT);
- mbuf.mount_device = xmalloc(n+sizeof((char)':')+m+1);
- if (v->vmt_flags & MNT_REMOTE) {
- (void) strncpy(mbuf.mount_device, vmt2dataptr(v, VMT_HOST), n);
- mbuf.mount_device[n] = '\0';
- n = strlen(mbuf.mount_device);
- mbuf.mount_device[n++] = ':';
- } else {
- n = 0;
- }
- (void) strncpy(mbuf.mount_device+n, vmt2dataptr(v, VMT_OBJECT), m);
- mbuf.mount_device[n+m] = '\0';
-
- return &mbuf;
- }
-
- int close_mnttab()
- {
- free((PTRTYPE *)mntctl_buf);
- return 0;
- }
-
- #endif /*} USE_MOUNT_MNTCTL*/
-
-