df.c

Go to the documentation of this file.
00001 /* df - disk free block printout        Author: Andy Tanenbaum
00002  *
00003  * 91/04/30 Kees J. Bot (kjb@cs.vu.nl)
00004  *      Map filename arguments to the devices they live on.
00005  *      Changed output to show percentages.
00006  *
00007  * 92/12/12 Kees J. Bot
00008  *      Posixized.  (Almost, the normal output is in kilobytes, it should
00009  *      be 512-byte units.  'df -P' and 'df -kP' are as it should be.)
00010  *
00011  */
00012 
00013 #include <stdio.h>
00014 #include <sys/types.h>
00015 #include <sys/stat.h>
00016 #include <limits.h>
00017 #include <fcntl.h>
00018 #include <errno.h>
00019 #include <unistd.h>
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <dirent.h>
00024 #if __minix_vmd
00025 #include <sys/mnttab.h>
00026 #else
00027 #include <minix/minlib.h>
00028 #endif
00029 
00030 #include <minix/config.h>
00031 #include <minix/const.h>
00032 #include <minix/type.h>
00033 #include <servers/fs/const.h>
00034 #include <servers/fs/type.h>
00035 #include <servers/fs/super.h>
00036 #undef printf
00037 
00038 #if !__minix_vmd
00039 /* Map Minix-vmd names to Minix names. */
00040 #define v12_super_block         super_block
00041 #define SUPER_V1                SUPER_MAGIC
00042 
00043 #endif
00044 
00045 #define ISDISK(mode)    S_ISBLK(mode)   /* || S_ISCHR for raw device??? */
00046 
00047 extern int errno;
00048 char MTAB[] = "/etc/mtab";
00049 
00050 struct mtab {   /* List of mounted devices from /etc/mtab. */
00051         struct mtab     *next;
00052         dev_t           device;
00053         char            *devname;
00054         char            *mountpoint;
00055 } *mtab= NULL;
00056 
00057 struct mtab *searchtab(char *name);
00058 void readmtab(char *type);
00059 int df(const struct mtab *mt);
00060 bit_t bit_count(unsigned blocks, bit_t bits, int fd, int bs);
00061 
00062 int iflag= 0;   /* Focus on inodes instead of blocks. */
00063 int Pflag= 0;   /* Posix standard output. */
00064 int kflag= 0;   /* Output in kilobytes instead of 512 byte units for -P. */
00065 int istty;      /* isatty(1) */
00066 uid_t ruid, euid;       /* To sometimes change identities. */
00067 gid_t rgid, egid;
00068 
00069 void usage(void)
00070 {
00071         fprintf(stderr, "Usage: df [-ikP] [-t type] [device]...\n");
00072         exit(1);
00073 }
00074 
00075 int unitsize;
00076 
00077 int main(int argc, char *argv[])
00078 {
00079   int i;
00080   struct mtab *mt;
00081   char *type= "dev";
00082   int ex= 0;
00083 
00084   while (argc > 1 && argv[1][0] == '-') {
00085         char *opt= argv[1]+1;
00086 
00087         while (*opt != 0) {
00088                 switch (*opt++) {
00089                 case 'i':       iflag= 1;       break;
00090                 case 'k':       kflag= 1;       break;
00091                 case 'P':       Pflag= 1;       break;
00092                 case 't':
00093                         if (argc < 3) usage();
00094                         type= argv[2];
00095                         argv++;
00096                         argc--;
00097                         break;
00098                 default:
00099                         usage();
00100                 }
00101         }
00102         argc--;
00103         argv++;
00104   }
00105 
00106   istty= isatty(1);
00107   ruid= getuid(); euid= geteuid();
00108   rgid= getgid(); egid= getegid();
00109 
00110   readmtab(type);
00111  
00112   if(!Pflag || (Pflag && kflag)) unitsize = 1024;
00113   else unitsize = 512;
00114 
00115   if (Pflag) {
00116         printf(!iflag ? "\
00117 Filesystem    %4d-blocks      Used    Available  Capacity  Mounted on\n" : "\
00118 Filesystem         Inodes       IUsed      IFree    %%IUsed    Mounted on\n",
00119                 unitsize);
00120   } else {
00121         printf("%s\n", !iflag ? "\
00122 Filesystem      Size (kB)       Free       Used    % Files%   Mounted on" : "\
00123 Filesystem          Files       Free       Used    % BUsed%   Mounted on"
00124         );
00125   }
00126 
00127   if (argc == 1) {
00128         for (mt= mtab; mt != NULL; mt= mt->next) ex |= df(mt);
00129   } else {
00130         for (i = 1; i < argc; i++) ex |= df(searchtab(argv[i]));
00131   }
00132   exit(ex);
00133 }
00134 
00135 void readmtab(char *type)
00136 /* Turn the mounted file table into a list. */
00137 {
00138   struct mtab **amt= &mtab, *new;
00139   struct stat st;
00140 
00141 #if __minix_vmd
00142   char *devname, *mountpoint;
00143   FILE *mtf;
00144   struct mnttab mte, look;
00145 
00146   if ((mtf= fopen(MTAB, "r")) == NULL) {
00147         fprintf(stderr, "df: can't open %s\n", MTAB);
00148         return;
00149   }
00150 
00151   look.mnt_special= NULL;
00152   look.mnt_mountp= NULL;
00153   look.mnt_fstype= type;
00154   look.mnt_mntopts= NULL;
00155 
00156   while (getmntany(mtf, &mte, &look) >= 0) {
00157         devname= mte.mnt_special;
00158         mountpoint= mte.mnt_mountp;
00159 
00160                 /* Skip bad entries, can't complain about everything. */
00161         if (stat(devname, &st) < 0 || !ISDISK(st.st_mode)) continue;
00162 
00163                 /* Make new list cell. */
00164         if ((new= (struct mtab *) malloc(sizeof(*new))) == NULL
00165           || (new->devname= (char *) malloc(strlen(devname) + 1)) == NULL
00166           || (new->mountpoint= (char *) malloc(strlen(mountpoint) + 1)) == NULL
00167         ) break;
00168 
00169         new->device= st.st_rdev;
00170         strcpy(new->devname, devname);
00171         strcpy(new->mountpoint, mountpoint);
00172 
00173         *amt= new;              /* Add the cell to the end. */
00174         amt= &new->next;
00175         *amt= NULL;
00176   }
00177   fclose(mtf);
00178 
00179 #else /* __minix */
00180   char devname[128], mountpoint[128], version[10], rw_flag[10];
00181 
00182   if (load_mtab("df") < 0) exit(1);
00183 
00184   while (get_mtab_entry(devname, mountpoint, version, rw_flag),
00185                                                           devname[0] != 0) {
00186         if (strcmp(type, "dev") == 0) {
00187                 if (strcmp(version, "1") != 0 && strcmp(version, "2") != 0 &&
00188                         strcmp(version, "3"))
00189                         continue;
00190         } else {
00191                 if (strcmp(type, version) != 0) continue;
00192         }
00193 
00194                 /* Skip bad entries, can't complain about everything. */
00195         if (stat(devname, &st) < 0 || !ISDISK(st.st_mode)) continue;
00196 
00197                 /* Make new list cell. */
00198         if ((new= (struct mtab *) malloc(sizeof(*new))) == NULL
00199           || (new->devname= (char *) malloc(strlen(devname) + 1)) == NULL
00200           || (new->mountpoint= (char *) malloc(strlen(mountpoint) + 1)) == NULL
00201         ) break;
00202 
00203         new->device= st.st_rdev;
00204         strcpy(new->devname, devname);
00205         strcpy(new->mountpoint, mountpoint);
00206 
00207         *amt= new;              /* Add the cell to the end. */
00208         amt= &new->next;
00209         *amt= NULL;
00210   }
00211 #endif
00212 }
00213 
00214 struct mtab *searchtab(char *name)
00215 /* See what we can do with a user supplied name, there are five possibilities:
00216  * 1. It's a device and it is in the mtab: Return mtab entry.
00217  * 2. It's a device and it is not in the mtab: Return device mounted on "".
00218  * 3. It's a file and lives on a device in the mtab: Return mtab entry.
00219  * 4. It's a file and it's not on an mtab device: Search /dev for the device
00220  *    and return this device as mounted on "???".
00221  * 5. It's junk: Return something df() will choke on.
00222  */
00223 {
00224   static struct mtab unknown;
00225   static char devname[5 + NAME_MAX + 1]= "/dev/";
00226   struct mtab *mt;
00227   struct stat st;
00228   DIR *dp;
00229   struct dirent *ent;
00230 
00231   unknown.devname= name;
00232   unknown.mountpoint= "";
00233 
00234   if (stat(name, &st) < 0) return &unknown;     /* Case 5. */
00235 
00236   unknown.device= ISDISK(st.st_mode) ? st.st_rdev : st.st_dev;
00237 
00238   for (mt= mtab; mt != NULL; mt= mt->next) {
00239         if (unknown.device == mt->device)
00240                 return mt;                      /* Case 1 & 3. */
00241   }
00242 
00243   if (ISDISK(st.st_mode)) {
00244         return &unknown;                        /* Case 2. */
00245   }
00246 
00247   if ((dp= opendir("/dev")) == NULL) return &unknown;   /* Disaster. */
00248 
00249   while ((ent= readdir(dp)) != NULL) {
00250         if (ent->d_name[0] == '.') continue;
00251         strcpy(devname + 5, ent->d_name);
00252         if (stat(devname, &st) >= 0 && ISDISK(st.st_mode)
00253                 && unknown.device == st.st_rdev
00254         ) {
00255                 unknown.devname= devname;
00256                 unknown.mountpoint= "???";
00257                 break;
00258         }
00259   }
00260   closedir(dp);
00261   return &unknown;                              /* Case 4. */
00262 }
00263 
00264 /* (num / tot) in percentages rounded up. */
00265 #define percent(num, tot)  ((int) ((100L * (num) + ((tot) - 1)) / (tot)))
00266 
00267 /* One must be careful printing all these _t types. */
00268 #define L(n)    ((long) (n))
00269 
00270 int df(const struct mtab *mt)
00271 {
00272   int fd;
00273   bit_t i_count, z_count;
00274   block_t totblocks, busyblocks;
00275   int n, block_size;
00276   struct v12_super_block super, *sp;
00277 
00278   /* Don't allow Joe User to df just any device. */
00279   seteuid(*mt->mountpoint == 0 ? ruid : euid);
00280   setegid(*mt->mountpoint == 0 ? rgid : egid);
00281 
00282   if ((fd = open(mt->devname, O_RDONLY)) < 0) {
00283         fprintf(stderr, "df: %s: %s\n", mt->devname, strerror(errno));
00284         return(1);
00285   }
00286   lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET);       /* skip boot block */
00287 
00288   if (read(fd, (char *) &super, sizeof(super)) != (int) sizeof(super)) {
00289         fprintf(stderr, "df: Can't read super block of %s\n", mt->devname);
00290         close(fd);
00291         return(1);
00292   }
00293 
00294   sp = &super;
00295   if (sp->s_magic != SUPER_V1 && sp->s_magic != SUPER_V2
00296       && sp->s_magic != SUPER_V3) {
00297         fprintf(stderr, "df: %s: Not a valid file system\n", mt->devname);
00298         close(fd);
00299         return(1);
00300   }
00301 
00302   if(sp->s_magic != SUPER_V3) block_size = _STATIC_BLOCK_SIZE;
00303   else block_size = super.s_block_size;
00304 
00305   if(block_size < _MIN_BLOCK_SIZE || block_size > _MAX_BLOCK_SIZE) {
00306         fprintf(stderr, "df: %s: funny block size (%d)\n",
00307                 mt->devname, block_size);
00308         close(fd);
00309         return(1);
00310   }
00311 
00312   if (sp->s_magic == SUPER_V1) sp->s_zones= sp->s_nzones;
00313 
00314   lseek(fd, (off_t) block_size * 2L, SEEK_SET); /* skip rest of super block */
00315 
00316   i_count = bit_count(sp->s_imap_blocks, (bit_t) (sp->s_ninodes+1),
00317         fd, block_size);
00318 
00319   if (i_count == -1) {
00320         fprintf(stderr, "df: Can't find bit maps of %s\n", mt->devname);
00321         close(fd);
00322         return(1);
00323   }
00324   i_count--;    /* There is no inode 0. */
00325 
00326   /* The first bit in the zone map corresponds with zone s_firstdatazone - 1
00327    * This means that there are s_zones - (s_firstdatazone - 1) bits in the map
00328    */
00329   z_count = bit_count(sp->s_zmap_blocks,
00330         (bit_t) (sp->s_zones - (sp->s_firstdatazone - 1)), fd, block_size);
00331 
00332   if (z_count == -1) {
00333         fprintf(stderr, "df: Can't find bit maps of %s\n", mt->devname);
00334         close(fd);
00335         return(1);
00336   }
00337   /* Don't forget those zones before sp->s_firstdatazone - 1 */
00338   z_count += sp->s_firstdatazone - 1;
00339 
00340 #ifdef __minix_vmd
00341   totblocks = sp->s_zones;
00342   busyblocks = z_count;
00343 #else
00344   totblocks = (block_t) sp->s_zones << sp->s_log_zone_size;
00345   busyblocks = (block_t) z_count << sp->s_log_zone_size;
00346 #endif
00347 
00348   busyblocks = busyblocks * (block_size/512) / (unitsize/512);
00349   totblocks = totblocks * (block_size/512) / (unitsize/512);
00350 
00351   /* Print results. */
00352   printf("%s", mt->devname);
00353   n= strlen(mt->devname);
00354   if (n > 15 && istty) { putchar('\n'); n= 0; }
00355   while (n < 15) { putchar(' '); n++; }
00356 
00357   if (!Pflag && !iflag) {
00358         printf(" %9ld  %9ld  %9ld %3d%%   %3d%%   %s\n",
00359                 L(totblocks),                           /* Blocks */
00360                 L(totblocks - busyblocks),              /* free */
00361                 L(busyblocks),                          /* used */
00362                 percent(busyblocks, totblocks),         /* % */
00363                 percent(i_count, sp->s_ninodes),        /* FUsed% */
00364                 mt->mountpoint                          /* Mounted on */
00365         );
00366   }
00367   if (!Pflag && iflag) {
00368         printf(" %9ld  %9ld  %9ld %3d%%   %3d%%   %s\n",
00369                 L(sp->s_ninodes),                       /* Files */
00370                 L(sp->s_ninodes - i_count),             /* free */
00371                 L(i_count),                             /* used */
00372                 percent(i_count, sp->s_ninodes),        /* % */
00373                 percent(busyblocks, totblocks),         /* BUsed% */
00374                 mt->mountpoint                          /* Mounted on */
00375         );
00376   }
00377   if (Pflag && !iflag) {
00378         printf(" %9ld   %9ld  %9ld     %4d%%    %s\n",
00379                 L(totblocks),                           /* Blocks */
00380                 L(busyblocks),                          /* Used */
00381                 totblocks - busyblocks,                 /* Available */
00382                 percent(busyblocks, totblocks),         /* Capacity */
00383                 mt->mountpoint                          /* Mounted on */
00384         );
00385   }
00386   if (Pflag && iflag) {
00387         printf(" %9ld   %9ld  %9ld     %4d%%    %s\n",
00388                 L(sp->s_ninodes),                       /* Inodes */
00389                 L(i_count),                             /* IUsed */
00390                 L(sp->s_ninodes - i_count),             /* IAvail */
00391                 percent(i_count, sp->s_ninodes),        /* Capacity */
00392                 mt->mountpoint                          /* Mounted on */
00393         );
00394   }
00395   close(fd);
00396   return(0);
00397 }
00398 
00399 bit_t bit_count(unsigned blocks, bit_t bits, int fd, int block_size)
00400 {
00401   char *wptr;
00402   int i, b;
00403   bit_t busy;
00404   char *wlim;
00405   static char buf[_MAX_BLOCK_SIZE];
00406   static char bits_in_char[1 << CHAR_BIT];
00407 
00408   /* Precalculate bitcount for each char. */
00409   if (bits_in_char[1] != 1) {
00410         for (b = (1 << 0); b < (1 << CHAR_BIT); b <<= 1)
00411                 for (i = 0; i < (1 << CHAR_BIT); i++)
00412                         if (i & b) bits_in_char[i]++;
00413   }
00414 
00415   /* Loop on blocks, reading one at a time and counting bits. */
00416   busy = 0;
00417   for (i = 0; i < blocks && bits != 0; i++) {
00418         if (read(fd, buf, block_size) != block_size) return(-1);
00419 
00420         wptr = &buf[0];
00421         if (bits >= CHAR_BIT * block_size) {
00422                 wlim = &buf[block_size];
00423                 bits -= CHAR_BIT * block_size;
00424         } else {
00425                 b = bits / CHAR_BIT;    /* whole chars in map */
00426                 wlim = &buf[b];
00427                 bits -= b * CHAR_BIT;   /* bits in last char, if any */
00428                 b = *wlim & ((1 << bits) - 1);  /* bit pattern from last ch */
00429                 busy += bits_in_char[b];
00430                 bits = 0;
00431         }
00432 
00433         /* Loop on the chars of a block. */
00434         while (wptr != wlim)
00435                 busy += bits_in_char[*wptr++ & ((1 << CHAR_BIT) - 1)];
00436   }
00437   return(busy);
00438 }
00439 
00440 /*
00441  * $PchId: df.c,v 1.7 1998/07/27 18:42:17 philip Exp $
00442  */

Generated on Fri Apr 14 22:57:05 2006 for minix by  doxygen 1.4.6