470 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			470 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* df - disk free block printout	Author: Andy Tanenbaum
 | 
						|
 *
 | 
						|
 * 91/04/30 Kees J. Bot (kjb@cs.vu.nl)
 | 
						|
 *	Map filename arguments to the devices they live on.
 | 
						|
 *	Changed output to show percentages.
 | 
						|
 *
 | 
						|
 * 92/12/12 Kees J. Bot
 | 
						|
 *	Posixized.  (Almost, the normal output is in kilobytes, it should
 | 
						|
 *	be 512-byte units.  'df -P' and 'df -kP' are as it should be.)
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <dirent.h>
 | 
						|
#if __minix_vmd
 | 
						|
#include <sys/mnttab.h>
 | 
						|
#else
 | 
						|
#include <minix/minlib.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <minix/config.h>
 | 
						|
#include <minix/const.h>
 | 
						|
#include <minix/type.h>
 | 
						|
#include <servers/mfs/const.h>
 | 
						|
#include <servers/mfs/type.h>
 | 
						|
#include <servers/mfs/super.h>
 | 
						|
#undef printf
 | 
						|
 | 
						|
#if !__minix_vmd
 | 
						|
/* Map Minix-vmd names to Minix names. */
 | 
						|
#define v12_super_block		super_block
 | 
						|
#define SUPER_V1		SUPER_MAGIC
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
#define ISDISK(mode)	S_ISBLK(mode)	/* || S_ISCHR for raw device??? */
 | 
						|
 | 
						|
extern int errno;
 | 
						|
char MTAB[] = "/etc/mtab";
 | 
						|
 | 
						|
struct mtab {	/* List of mounted devices from /etc/mtab. */
 | 
						|
	struct mtab	*next;
 | 
						|
	dev_t		device;
 | 
						|
	char		*devname;
 | 
						|
	char		*mountpoint;
 | 
						|
} *mtab= NULL;
 | 
						|
 | 
						|
struct mtab *searchtab(char *name);
 | 
						|
static void readmtab(const char *type);
 | 
						|
int df(const struct mtab *mt);
 | 
						|
bit_t bit_count(unsigned blocks, bit_t bits, int fd, int bs);
 | 
						|
 | 
						|
int iflag= 0;	/* Focus on inodes instead of blocks. */
 | 
						|
int Pflag= 0;	/* Posix standard output. */
 | 
						|
int kflag= 0;	/* Output in kilobytes instead of 512 byte units for -P. */
 | 
						|
int istty;	/* isatty(1) */
 | 
						|
uid_t ruid, euid;	/* To sometimes change identities. */
 | 
						|
gid_t rgid, egid;
 | 
						|
 | 
						|
void usage(void)
 | 
						|
{
 | 
						|
	fprintf(stderr, "Usage: df [-ikP] [-t type] [device]...\n");
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
int unitsize;
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  struct mtab *mt;
 | 
						|
  char *type= "dev";
 | 
						|
  int ex= 0;
 | 
						|
 | 
						|
  while (argc > 1 && argv[1][0] == '-') {
 | 
						|
  	char *opt= argv[1]+1;
 | 
						|
 | 
						|
  	while (*opt != 0) {
 | 
						|
  		switch (*opt++) {
 | 
						|
  		case 'i':	iflag= 1;	break;
 | 
						|
  		case 'k':	kflag= 1;	break;
 | 
						|
  		case 'P':	Pflag= 1;	break;
 | 
						|
  		case 't':
 | 
						|
			if (argc < 3) usage();
 | 
						|
			type= argv[2];
 | 
						|
			argv++;
 | 
						|
			argc--;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			usage();
 | 
						|
		}
 | 
						|
	}
 | 
						|
	argc--;
 | 
						|
	argv++;
 | 
						|
  }
 | 
						|
 | 
						|
  istty= isatty(1);
 | 
						|
  ruid= getuid(); euid= geteuid();
 | 
						|
  rgid= getgid(); egid= getegid();
 | 
						|
 | 
						|
  readmtab(type);
 | 
						|
 
 | 
						|
  if(!Pflag || (Pflag && kflag)) unitsize = 1024;
 | 
						|
  else unitsize = 512;
 | 
						|
 | 
						|
  if (Pflag) {
 | 
						|
	printf(!iflag ? "\
 | 
						|
Filesystem    %4d-blocks      Used    Available  Capacity  Mounted on\n" : "\
 | 
						|
Filesystem         Inodes       IUsed      IFree    %%IUsed    Mounted on\n",
 | 
						|
		unitsize);
 | 
						|
  } else {
 | 
						|
	printf("%s\n", !iflag ? "\
 | 
						|
Filesystem      Size (kB)       Free       Used    % Files%   Mounted on" : "\
 | 
						|
Filesystem          Files       Free       Used    % BUsed%   Mounted on"
 | 
						|
	);
 | 
						|
  }
 | 
						|
 | 
						|
  if (argc == 1) {
 | 
						|
	for (mt= mtab; mt != NULL; mt= mt->next) ex |= df(mt);
 | 
						|
  } else {
 | 
						|
	for (i = 1; i < argc; i++) ex |= df(searchtab(argv[i]));
 | 
						|
  }
 | 
						|
  exit(ex);
 | 
						|
}
 | 
						|
 | 
						|
static void readmtab(const char *type)
 | 
						|
/* Turn the mounted file table into a list. */
 | 
						|
{
 | 
						|
  struct mtab **amt= &mtab, *new;
 | 
						|
  struct stat st;
 | 
						|
 | 
						|
#if __minix_vmd
 | 
						|
  char *devname, *mountpoint;
 | 
						|
  FILE *mtf;
 | 
						|
  struct mnttab mte, look;
 | 
						|
 | 
						|
  if ((mtf= fopen(MTAB, "r")) == NULL) {
 | 
						|
	fprintf(stderr, "df: can't open %s\n", MTAB);
 | 
						|
	return;
 | 
						|
  }
 | 
						|
 | 
						|
  look.mnt_special= NULL;
 | 
						|
  look.mnt_mountp= NULL;
 | 
						|
  look.mnt_fstype= type;
 | 
						|
  look.mnt_mntopts= NULL;
 | 
						|
 | 
						|
  while (getmntany(mtf, &mte, &look) >= 0) {
 | 
						|
  	devname= mte.mnt_special;
 | 
						|
  	mountpoint= mte.mnt_mountp;
 | 
						|
 | 
						|
		/* Skip bad entries, can't complain about everything. */
 | 
						|
	if (stat(devname, &st) < 0 || !ISDISK(st.st_mode)) continue;
 | 
						|
 | 
						|
		/* Make new list cell. */
 | 
						|
	if ((new= (struct mtab *) malloc(sizeof(*new))) == NULL
 | 
						|
	  || (new->devname= (char *) malloc(strlen(devname) + 1)) == NULL
 | 
						|
	  || (new->mountpoint= (char *) malloc(strlen(mountpoint) + 1)) == NULL
 | 
						|
	) break;
 | 
						|
 | 
						|
	new->device= st.st_rdev;
 | 
						|
	strcpy(new->devname, devname);
 | 
						|
	strcpy(new->mountpoint, mountpoint);
 | 
						|
 | 
						|
	*amt= new;		/* Add the cell to the end. */
 | 
						|
	amt= &new->next;
 | 
						|
	*amt= NULL;
 | 
						|
  }
 | 
						|
  fclose(mtf);
 | 
						|
 | 
						|
#else /* __minix */
 | 
						|
  char devname[128], mountpoint[128], version[10], rw_flag[10];
 | 
						|
 | 
						|
  if (load_mtab("df") < 0) exit(1);
 | 
						|
 | 
						|
  while (get_mtab_entry(devname, mountpoint, version, rw_flag),
 | 
						|
							  devname[0] != 0) {
 | 
						|
	if (strcmp(type, "dev") == 0) {
 | 
						|
		if (strcmp(version, "1") != 0 && strcmp(version, "2") != 0 &&
 | 
						|
		 	strcmp(version, "3") && strcmp(version, "MFSv3"))
 | 
						|
			continue;
 | 
						|
	} else {
 | 
						|
		if (strcmp(type, version) != 0) continue;
 | 
						|
	}
 | 
						|
 | 
						|
		/* Skip bad entries, can't complain about everything. */
 | 
						|
	if (stat(devname, &st) < 0 || !ISDISK(st.st_mode)) continue;
 | 
						|
 | 
						|
		/* Make new list cell. */
 | 
						|
	if ((new= (struct mtab *) malloc(sizeof(*new))) == NULL
 | 
						|
	  || (new->devname= (char *) malloc(strlen(devname) + 1)) == NULL
 | 
						|
	  || (new->mountpoint= (char *) malloc(strlen(mountpoint) + 1)) == NULL
 | 
						|
	) break;
 | 
						|
 | 
						|
	new->device= st.st_rdev;
 | 
						|
	strcpy(new->devname, devname);
 | 
						|
	strcpy(new->mountpoint, mountpoint);
 | 
						|
 | 
						|
	*amt= new;		/* Add the cell to the end. */
 | 
						|
	amt= &new->next;
 | 
						|
	*amt= NULL;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
struct mtab *searchtab(char *name)
 | 
						|
/* See what we can do with a user supplied name, there are five possibilities:
 | 
						|
 * 1. It's a device and it is in the mtab: Return mtab entry.
 | 
						|
 * 2. It's a device and it is not in the mtab: Return device mounted on "".
 | 
						|
 * 3. It's a file and lives on a device in the mtab: Return mtab entry.
 | 
						|
 * 4. It's a file and it's not on an mtab device: Search /dev for the device
 | 
						|
 *    and return this device as mounted on "???".
 | 
						|
 * 5. It's junk: Return something df() will choke on.
 | 
						|
 */
 | 
						|
{
 | 
						|
  static struct mtab unknown;
 | 
						|
  static char devname[5 + NAME_MAX + 1]= "/dev/";
 | 
						|
  struct mtab *mt;
 | 
						|
  struct stat st;
 | 
						|
  DIR *dp;
 | 
						|
  struct dirent *ent;
 | 
						|
 | 
						|
  unknown.devname= name;
 | 
						|
  unknown.mountpoint= "";
 | 
						|
 | 
						|
  if (stat(name, &st) < 0) return &unknown;	/* Case 5. */
 | 
						|
 | 
						|
  unknown.device= ISDISK(st.st_mode) ? st.st_rdev : st.st_dev;
 | 
						|
 | 
						|
  for (mt= mtab; mt != NULL; mt= mt->next) {
 | 
						|
	if (unknown.device == mt->device)
 | 
						|
		return mt;			/* Case 1 & 3. */
 | 
						|
  }
 | 
						|
 | 
						|
  if (ISDISK(st.st_mode)) {
 | 
						|
	return &unknown;			/* Case 2. */
 | 
						|
  }
 | 
						|
 | 
						|
  if ((dp= opendir("/dev")) == NULL) return &unknown;	/* Disaster. */
 | 
						|
 | 
						|
  while ((ent= readdir(dp)) != NULL) {
 | 
						|
	if (ent->d_name[0] == '.') continue;
 | 
						|
	strcpy(devname + 5, ent->d_name);
 | 
						|
	if (stat(devname, &st) >= 0 && ISDISK(st.st_mode)
 | 
						|
		&& unknown.device == st.st_rdev
 | 
						|
	) {
 | 
						|
		unknown.devname= devname;
 | 
						|
		unknown.mountpoint= "???";
 | 
						|
		break;
 | 
						|
	}
 | 
						|
  }
 | 
						|
  closedir(dp);
 | 
						|
  return &unknown;				/* Case 4. */
 | 
						|
}
 | 
						|
 | 
						|
/* (num / tot) in percentages rounded up. */
 | 
						|
#define percent(num, tot)  ((int) ((100L * (num) + ((tot) - 1)) / (tot)))
 | 
						|
 | 
						|
/* One must be careful printing all these _t types. */
 | 
						|
#define L(n)	((long) (n))
 | 
						|
 | 
						|
int df(const struct mtab *mt)
 | 
						|
{
 | 
						|
  int fd;
 | 
						|
  bit_t i_count, z_count;
 | 
						|
  block_t totblocks, busyblocks, offset;
 | 
						|
  int n, block_size;
 | 
						|
  struct v12_super_block super, *sp;
 | 
						|
 | 
						|
  /* Don't allow Joe User to df just any device. */
 | 
						|
  seteuid(*mt->mountpoint == 0 ? ruid : euid);
 | 
						|
  setegid(*mt->mountpoint == 0 ? rgid : egid);
 | 
						|
 | 
						|
  if ((fd = open(mt->devname, O_RDONLY)) < 0) {
 | 
						|
	fprintf(stderr, "df: %s: %s\n", mt->devname, strerror(errno));
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
  lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET);	/* skip boot block */
 | 
						|
 | 
						|
  if (read(fd, (char *) &super, sizeof(super)) != (int) sizeof(super)) {
 | 
						|
	fprintf(stderr, "df: Can't read super block of %s\n", mt->devname);
 | 
						|
	close(fd);
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
 | 
						|
  sp = &super;
 | 
						|
  if (sp->s_magic != SUPER_V1 && sp->s_magic != SUPER_V2
 | 
						|
      && sp->s_magic != SUPER_V3) {
 | 
						|
	fprintf(stderr, "df: %s: Not a valid file system\n", mt->devname);
 | 
						|
	close(fd);
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
 | 
						|
  if(sp->s_magic != SUPER_V3) block_size = _STATIC_BLOCK_SIZE;
 | 
						|
  else block_size = sp->s_block_size;
 | 
						|
 | 
						|
  if(block_size < _MIN_BLOCK_SIZE) {
 | 
						|
	fprintf(stderr, "df: %s: funny block size (%d)\n",
 | 
						|
		mt->devname, block_size);
 | 
						|
	close(fd);
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
 | 
						|
  if (sp->s_magic == SUPER_V1) {
 | 
						|
	sp->s_zones = sp->s_nzones;
 | 
						|
	sp->s_inodes_per_block = V1_INODES_PER_BLOCK;
 | 
						|
  } else {
 | 
						|
	sp->s_inodes_per_block = V2_INODES_PER_BLOCK(block_size);
 | 
						|
  }
 | 
						|
 | 
						|
  /* If the s_firstdatazone_old field is zero, we have to compute the value. */
 | 
						|
  if (sp->s_firstdatazone_old == 0) {
 | 
						|
	offset = START_BLOCK + sp->s_imap_blocks + sp->s_zmap_blocks;
 | 
						|
	offset += (sp->s_ninodes + sp->s_inodes_per_block - 1) /
 | 
						|
		sp->s_inodes_per_block;
 | 
						|
 | 
						|
	sp->s_firstdatazone = (offset + (1 << sp->s_log_zone_size) - 1) >>
 | 
						|
		sp->s_log_zone_size;
 | 
						|
  } else {
 | 
						|
	sp->s_firstdatazone = sp->s_firstdatazone_old;
 | 
						|
  }
 | 
						|
 | 
						|
  lseek(fd, (off_t) block_size * 2L, SEEK_SET);	/* skip rest of super block */
 | 
						|
 | 
						|
  i_count = bit_count(sp->s_imap_blocks, (bit_t) (sp->s_ninodes+1),
 | 
						|
  	fd, block_size);
 | 
						|
 | 
						|
  if (i_count == -1) {
 | 
						|
	fprintf(stderr, "df: Can't find bit maps of %s\n", mt->devname);
 | 
						|
	close(fd);
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
  i_count--;	/* There is no inode 0. */
 | 
						|
 | 
						|
  /* The first bit in the zone map corresponds with zone s_firstdatazone - 1
 | 
						|
   * This means that there are s_zones - (s_firstdatazone - 1) bits in the map
 | 
						|
   */
 | 
						|
  z_count = bit_count(sp->s_zmap_blocks,
 | 
						|
	(bit_t) (sp->s_zones - (sp->s_firstdatazone - 1)), fd, block_size);
 | 
						|
 | 
						|
  if (z_count == -1) {
 | 
						|
	fprintf(stderr, "df: Can't find bit maps of %s\n", mt->devname);
 | 
						|
	close(fd);
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
  /* Don't forget those zones before sp->s_firstdatazone - 1 */
 | 
						|
  z_count += sp->s_firstdatazone - 1;
 | 
						|
 | 
						|
#ifdef __minix_vmd
 | 
						|
  totblocks = sp->s_zones;
 | 
						|
  busyblocks = z_count;
 | 
						|
#else
 | 
						|
  totblocks = (block_t) sp->s_zones << sp->s_log_zone_size;
 | 
						|
  busyblocks = (block_t) z_count << sp->s_log_zone_size;
 | 
						|
#endif
 | 
						|
 | 
						|
  busyblocks = busyblocks * (block_size/512) / (unitsize/512);
 | 
						|
  totblocks = totblocks * (block_size/512) / (unitsize/512);
 | 
						|
 | 
						|
  /* Print results. */
 | 
						|
  printf("%s", mt->devname);
 | 
						|
  n= strlen(mt->devname);
 | 
						|
  if (n > 15 && istty) { putchar('\n'); n= 0; }
 | 
						|
  while (n < 15) { putchar(' '); n++; }
 | 
						|
 | 
						|
  if (!Pflag && !iflag) {
 | 
						|
	printf(" %9ld  %9ld  %9ld %3d%%   %3d%%   %s\n",
 | 
						|
		L(totblocks),				/* Blocks */
 | 
						|
		L(totblocks - busyblocks),		/* free */
 | 
						|
		L(busyblocks),				/* used */
 | 
						|
		percent(busyblocks, totblocks),		/* % */
 | 
						|
		percent(i_count, sp->s_ninodes),	/* FUsed% */
 | 
						|
		mt->mountpoint				/* Mounted on */
 | 
						|
	);
 | 
						|
  }
 | 
						|
  if (!Pflag && iflag) {
 | 
						|
	printf(" %9ld  %9ld  %9ld %3d%%   %3d%%   %s\n",
 | 
						|
		L(sp->s_ninodes),			/* Files */
 | 
						|
		L(sp->s_ninodes - i_count),		/* free */
 | 
						|
		L(i_count),				/* used */
 | 
						|
		percent(i_count, sp->s_ninodes),	/* % */
 | 
						|
		percent(busyblocks, totblocks),		/* BUsed% */
 | 
						|
		mt->mountpoint				/* Mounted on */
 | 
						|
	);
 | 
						|
  }
 | 
						|
  if (Pflag && !iflag) {
 | 
						|
	printf(" %9ld   %9ld  %9ld     %4d%%    %s\n",
 | 
						|
		L(totblocks),				/* Blocks */
 | 
						|
		L(busyblocks),				/* Used */
 | 
						|
		totblocks - busyblocks,			/* Available */
 | 
						|
		percent(busyblocks, totblocks),		/* Capacity */
 | 
						|
		mt->mountpoint				/* Mounted on */
 | 
						|
	);
 | 
						|
  }
 | 
						|
  if (Pflag && iflag) {
 | 
						|
	printf(" %9ld   %9ld  %9ld     %4d%%    %s\n",
 | 
						|
		L(sp->s_ninodes),			/* Inodes */
 | 
						|
		L(i_count),				/* IUsed */
 | 
						|
		L(sp->s_ninodes - i_count),		/* IAvail */
 | 
						|
		percent(i_count, sp->s_ninodes),	/* Capacity */
 | 
						|
		mt->mountpoint				/* Mounted on */
 | 
						|
	);
 | 
						|
  }
 | 
						|
  close(fd);
 | 
						|
  return(0);
 | 
						|
}
 | 
						|
 | 
						|
bit_t bit_count(unsigned blocks, bit_t bits, int fd, int block_size)
 | 
						|
{
 | 
						|
  char *wptr;
 | 
						|
  int i, b;
 | 
						|
  bit_t busy;
 | 
						|
  char *wlim;
 | 
						|
  static char *buf = NULL;
 | 
						|
  static char bits_in_char[1 << CHAR_BIT];
 | 
						|
  static int bufsize = 0;
 | 
						|
 | 
						|
  if(bufsize < block_size) {
 | 
						|
	if(buf) free(buf);
 | 
						|
	if(!(buf = malloc(block_size))) {
 | 
						|
		fprintf(stderr, "df: malloc failed\n");
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
	bufsize = block_size;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Precalculate bitcount for each char. */
 | 
						|
  if (bits_in_char[1] != 1) {
 | 
						|
	for (b = (1 << 0); b < (1 << CHAR_BIT); b <<= 1)
 | 
						|
		for (i = 0; i < (1 << CHAR_BIT); i++)
 | 
						|
			if (i & b) bits_in_char[i]++;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Loop on blocks, reading one at a time and counting bits. */
 | 
						|
  busy = 0;
 | 
						|
  for (i = 0; i < blocks && bits != 0; i++) {
 | 
						|
	if (read(fd, buf, block_size) != block_size) return(-1);
 | 
						|
 | 
						|
	wptr = &buf[0];
 | 
						|
	if (bits >= CHAR_BIT * block_size) {
 | 
						|
		wlim = &buf[block_size];
 | 
						|
		bits -= CHAR_BIT * block_size;
 | 
						|
	} else {
 | 
						|
		b = bits / CHAR_BIT;	/* whole chars in map */
 | 
						|
		wlim = &buf[b];
 | 
						|
		bits -= b * CHAR_BIT;	/* bits in last char, if any */
 | 
						|
		b = *wlim & ((1 << bits) - 1);	/* bit pattern from last ch */
 | 
						|
		busy += bits_in_char[b];
 | 
						|
		bits = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Loop on the chars of a block. */
 | 
						|
	while (wptr != wlim)
 | 
						|
		busy += bits_in_char[*wptr++ & ((1 << CHAR_BIT) - 1)];
 | 
						|
  }
 | 
						|
  return(busy);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * $PchId: df.c,v 1.7 1998/07/27 18:42:17 philip Exp $
 | 
						|
 */
 |