1 /* df - disk free block printout Author: Andy Tanenbaum
3 * 91/04/30 Kees J. Bot (kjb@cs.vu.nl)
4 * Map filename arguments to the devices they live on.
5 * Changed output to show percentages.
8 * Posixized. (Almost, the normal output is in kilobytes, it should
9 * be 512-byte units. 'df -P' and 'df -kP' are as it should be.)
14 #include <sys/types.h>
25 #include <sys/mnttab.h>
27 #include <minix/minlib.h>
30 #include <minix/config.h>
31 #include <minix/const.h>
32 #include <minix/type.h>
33 #include <servers/mfs/const.h>
34 #include <servers/mfs/type.h>
35 #include <servers/mfs/super.h>
39 /* Map Minix-vmd names to Minix names. */
40 #define v12_super_block super_block
41 #define SUPER_V1 SUPER_MAGIC
45 #define ISDISK(mode) S_ISBLK(mode) /* || S_ISCHR for raw device??? */
48 char MTAB
[] = "/etc/mtab";
50 struct mtab
{ /* List of mounted devices from /etc/mtab. */
57 struct mtab
*searchtab(char *name
);
58 static void readmtab(const char *type
);
59 int df(const struct mtab
*mt
);
60 bit_t
bit_count(unsigned blocks
, bit_t bits
, int fd
, int bs
);
62 int iflag
= 0; /* Focus on inodes instead of blocks. */
63 int Pflag
= 0; /* Posix standard output. */
64 int kflag
= 0; /* Output in kilobytes instead of 512 byte units for -P. */
65 int istty
; /* isatty(1) */
66 uid_t ruid
, euid
; /* To sometimes change identities. */
71 fprintf(stderr
, "Usage: df [-ikP] [-t type] [device]...\n");
77 int main(int argc
, char *argv
[])
84 while (argc
> 1 && argv
[1][0] == '-') {
89 case 'i': iflag
= 1; break;
90 case 'k': kflag
= 1; break;
91 case 'P': Pflag
= 1; break;
93 if (argc
< 3) usage();
107 ruid
= getuid(); euid
= geteuid();
108 rgid
= getgid(); egid
= getegid();
112 if(!Pflag
|| (Pflag
&& kflag
)) unitsize
= 1024;
117 Filesystem %4d-blocks Used Available Capacity Mounted on\n" : "\
118 Filesystem Inodes IUsed IFree %%IUsed Mounted on\n",
121 printf("%s\n", !iflag
? "\
122 Filesystem Size (kB) Free Used % Files% Mounted on" : "\
123 Filesystem Files Free Used % BUsed% Mounted on"
128 for (mt
= mtab
; mt
!= NULL
; mt
= mt
->next
) ex
|= df(mt
);
130 for (i
= 1; i
< argc
; i
++) ex
|= df(searchtab(argv
[i
]));
135 static void readmtab(const char *type
)
136 /* Turn the mounted file table into a list. */
138 struct mtab
**amt
= &mtab
, *new;
142 char *devname
, *mountpoint
;
144 struct mnttab mte
, look
;
146 if ((mtf
= fopen(MTAB
, "r")) == NULL
) {
147 fprintf(stderr
, "df: can't open %s\n", MTAB
);
151 look
.mnt_special
= NULL
;
152 look
.mnt_mountp
= NULL
;
153 look
.mnt_fstype
= type
;
154 look
.mnt_mntopts
= NULL
;
156 while (getmntany(mtf
, &mte
, &look
) >= 0) {
157 devname
= mte
.mnt_special
;
158 mountpoint
= mte
.mnt_mountp
;
160 /* Skip bad entries, can't complain about everything. */
161 if (stat(devname
, &st
) < 0 || !ISDISK(st
.st_mode
)) continue;
163 /* Make new list cell. */
164 if ((new= (struct mtab
*) malloc(sizeof(*new))) == NULL
165 || (new->devname
= (char *) malloc(strlen(devname
) + 1)) == NULL
166 || (new->mountpoint
= (char *) malloc(strlen(mountpoint
) + 1)) == NULL
169 new->device
= st
.st_rdev
;
170 strcpy(new->devname
, devname
);
171 strcpy(new->mountpoint
, mountpoint
);
173 *amt
= new; /* Add the cell to the end. */
180 char devname
[128], mountpoint
[128], version
[10], rw_flag
[10];
182 if (load_mtab("df") < 0) exit(1);
184 while (get_mtab_entry(devname
, mountpoint
, version
, rw_flag
),
186 if (strcmp(type
, "dev") == 0) {
187 if (strcmp(version
, "1") != 0 && strcmp(version
, "2") != 0 &&
188 strcmp(version
, "3") && strcmp(version
, "MFSv3"))
191 if (strcmp(type
, version
) != 0) continue;
194 /* Skip bad entries, can't complain about everything. */
195 if (stat(devname
, &st
) < 0 || !ISDISK(st
.st_mode
)) continue;
197 /* Make new list cell. */
198 if ((new= (struct mtab
*) malloc(sizeof(*new))) == NULL
199 || (new->devname
= (char *) malloc(strlen(devname
) + 1)) == NULL
200 || (new->mountpoint
= (char *) malloc(strlen(mountpoint
) + 1)) == NULL
203 new->device
= st
.st_rdev
;
204 strcpy(new->devname
, devname
);
205 strcpy(new->mountpoint
, mountpoint
);
207 *amt
= new; /* Add the cell to the end. */
214 struct mtab
*searchtab(char *name
)
215 /* See what we can do with a user supplied name, there are five possibilities:
216 * 1. It's a device and it is in the mtab: Return mtab entry.
217 * 2. It's a device and it is not in the mtab: Return device mounted on "".
218 * 3. It's a file and lives on a device in the mtab: Return mtab entry.
219 * 4. It's a file and it's not on an mtab device: Search /dev for the device
220 * and return this device as mounted on "???".
221 * 5. It's junk: Return something df() will choke on.
224 static struct mtab unknown
;
225 static char devname
[5 + NAME_MAX
+ 1]= "/dev/";
231 unknown
.devname
= name
;
232 unknown
.mountpoint
= "";
234 if (stat(name
, &st
) < 0) return &unknown
; /* Case 5. */
236 unknown
.device
= ISDISK(st
.st_mode
) ? st
.st_rdev
: st
.st_dev
;
238 for (mt
= mtab
; mt
!= NULL
; mt
= mt
->next
) {
239 if (unknown
.device
== mt
->device
)
240 return mt
; /* Case 1 & 3. */
243 if (ISDISK(st
.st_mode
)) {
244 return &unknown
; /* Case 2. */
247 if ((dp
= opendir("/dev")) == NULL
) return &unknown
; /* Disaster. */
249 while ((ent
= readdir(dp
)) != NULL
) {
250 if (ent
->d_name
[0] == '.') continue;
251 strcpy(devname
+ 5, ent
->d_name
);
252 if (stat(devname
, &st
) >= 0 && ISDISK(st
.st_mode
)
253 && unknown
.device
== st
.st_rdev
255 unknown
.devname
= devname
;
256 unknown
.mountpoint
= "???";
261 return &unknown
; /* Case 4. */
264 /* (num / tot) in percentages rounded up. */
265 #define percent(num, tot) ((int) ((100L * (num) + ((tot) - 1)) / (tot)))
267 /* One must be careful printing all these _t types. */
268 #define L(n) ((long) (n))
270 int df(const struct mtab
*mt
)
273 bit_t i_count
, z_count
;
274 block_t totblocks
, busyblocks
, offset
;
276 struct v12_super_block super
, *sp
;
278 /* Don't allow Joe User to df just any device. */
279 seteuid(*mt
->mountpoint
== 0 ? ruid
: euid
);
280 setegid(*mt
->mountpoint
== 0 ? rgid
: egid
);
282 if ((fd
= open(mt
->devname
, O_RDONLY
)) < 0) {
283 fprintf(stderr
, "df: %s: %s\n", mt
->devname
, strerror(errno
));
286 lseek(fd
, (off_t
) SUPER_BLOCK_BYTES
, SEEK_SET
); /* skip boot block */
288 if (read(fd
, (char *) &super
, sizeof(super
)) != (int) sizeof(super
)) {
289 fprintf(stderr
, "df: Can't read super block of %s\n", mt
->devname
);
295 if (sp
->s_magic
!= SUPER_V1
&& sp
->s_magic
!= SUPER_V2
296 && sp
->s_magic
!= SUPER_V3
) {
297 fprintf(stderr
, "df: %s: Not a valid file system\n", mt
->devname
);
302 if(sp
->s_magic
!= SUPER_V3
) block_size
= _STATIC_BLOCK_SIZE
;
303 else block_size
= sp
->s_block_size
;
305 if(block_size
< _MIN_BLOCK_SIZE
) {
306 fprintf(stderr
, "df: %s: funny block size (%d)\n",
307 mt
->devname
, block_size
);
312 if (sp
->s_magic
== SUPER_V1
) {
313 sp
->s_zones
= sp
->s_nzones
;
314 sp
->s_inodes_per_block
= V1_INODES_PER_BLOCK
;
316 sp
->s_inodes_per_block
= V2_INODES_PER_BLOCK(block_size
);
319 /* If the s_firstdatazone_old field is zero, we have to compute the value. */
320 if (sp
->s_firstdatazone_old
== 0) {
321 offset
= START_BLOCK
+ sp
->s_imap_blocks
+ sp
->s_zmap_blocks
;
322 offset
+= (sp
->s_ninodes
+ sp
->s_inodes_per_block
- 1) /
323 sp
->s_inodes_per_block
;
325 sp
->s_firstdatazone
= (offset
+ (1 << sp
->s_log_zone_size
) - 1) >>
328 sp
->s_firstdatazone
= sp
->s_firstdatazone_old
;
331 lseek(fd
, (off_t
) block_size
* 2L, SEEK_SET
); /* skip rest of super block */
333 i_count
= bit_count(sp
->s_imap_blocks
, (bit_t
) (sp
->s_ninodes
+1),
337 fprintf(stderr
, "df: Can't find bit maps of %s\n", mt
->devname
);
341 i_count
--; /* There is no inode 0. */
343 /* The first bit in the zone map corresponds with zone s_firstdatazone - 1
344 * This means that there are s_zones - (s_firstdatazone - 1) bits in the map
346 z_count
= bit_count(sp
->s_zmap_blocks
,
347 (bit_t
) (sp
->s_zones
- (sp
->s_firstdatazone
- 1)), fd
, block_size
);
350 fprintf(stderr
, "df: Can't find bit maps of %s\n", mt
->devname
);
354 /* Don't forget those zones before sp->s_firstdatazone - 1 */
355 z_count
+= sp
->s_firstdatazone
- 1;
358 totblocks
= sp
->s_zones
;
359 busyblocks
= z_count
;
361 totblocks
= (block_t
) sp
->s_zones
<< sp
->s_log_zone_size
;
362 busyblocks
= (block_t
) z_count
<< sp
->s_log_zone_size
;
365 busyblocks
= busyblocks
* (block_size
/512) / (unitsize
/512);
366 totblocks
= totblocks
* (block_size
/512) / (unitsize
/512);
369 printf("%s", mt
->devname
);
370 n
= strlen(mt
->devname
);
371 if (n
> 15 && istty
) { putchar('\n'); n
= 0; }
372 while (n
< 15) { putchar(' '); n
++; }
374 if (!Pflag
&& !iflag
) {
375 printf(" %9ld %9ld %9ld %3d%% %3d%% %s\n",
376 L(totblocks
), /* Blocks */
377 L(totblocks
- busyblocks
), /* free */
378 L(busyblocks
), /* used */
379 percent(busyblocks
, totblocks
), /* % */
380 percent(i_count
, sp
->s_ninodes
), /* FUsed% */
381 mt
->mountpoint
/* Mounted on */
384 if (!Pflag
&& iflag
) {
385 printf(" %9ld %9ld %9ld %3d%% %3d%% %s\n",
386 L(sp
->s_ninodes
), /* Files */
387 L(sp
->s_ninodes
- i_count
), /* free */
388 L(i_count
), /* used */
389 percent(i_count
, sp
->s_ninodes
), /* % */
390 percent(busyblocks
, totblocks
), /* BUsed% */
391 mt
->mountpoint
/* Mounted on */
394 if (Pflag
&& !iflag
) {
395 printf(" %9ld %9ld %9ld %4d%% %s\n",
396 L(totblocks
), /* Blocks */
397 L(busyblocks
), /* Used */
398 totblocks
- busyblocks
, /* Available */
399 percent(busyblocks
, totblocks
), /* Capacity */
400 mt
->mountpoint
/* Mounted on */
403 if (Pflag
&& iflag
) {
404 printf(" %9ld %9ld %9ld %4d%% %s\n",
405 L(sp
->s_ninodes
), /* Inodes */
406 L(i_count
), /* IUsed */
407 L(sp
->s_ninodes
- i_count
), /* IAvail */
408 percent(i_count
, sp
->s_ninodes
), /* Capacity */
409 mt
->mountpoint
/* Mounted on */
416 bit_t
bit_count(unsigned blocks
, bit_t bits
, int fd
, int block_size
)
422 static char *buf
= NULL
;
423 static char bits_in_char
[1 << CHAR_BIT
];
424 static int bufsize
= 0;
426 if(bufsize
< block_size
) {
428 if(!(buf
= malloc(block_size
))) {
429 fprintf(stderr
, "df: malloc failed\n");
432 bufsize
= block_size
;
435 /* Precalculate bitcount for each char. */
436 if (bits_in_char
[1] != 1) {
437 for (b
= (1 << 0); b
< (1 << CHAR_BIT
); b
<<= 1)
438 for (i
= 0; i
< (1 << CHAR_BIT
); i
++)
439 if (i
& b
) bits_in_char
[i
]++;
442 /* Loop on blocks, reading one at a time and counting bits. */
444 for (i
= 0; i
< blocks
&& bits
!= 0; i
++) {
445 if (read(fd
, buf
, block_size
) != block_size
) return(-1);
448 if (bits
>= CHAR_BIT
* block_size
) {
449 wlim
= &buf
[block_size
];
450 bits
-= CHAR_BIT
* block_size
;
452 b
= bits
/ CHAR_BIT
; /* whole chars in map */
454 bits
-= b
* CHAR_BIT
; /* bits in last char, if any */
455 b
= *wlim
& ((1 << bits
) - 1); /* bit pattern from last ch */
456 busy
+= bits_in_char
[b
];
460 /* Loop on the chars of a block. */
462 busy
+= bits_in_char
[*wptr
++ & ((1 << CHAR_BIT
) - 1)];
468 * $PchId: df.c,v 1.7 1998/07/27 18:42:17 philip Exp $