file_cmds-82.tar.gz
[apple/file_cmds.git] / df / df.c
1 /*
2 * Copyright (c) 1980, 1990, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * (c) UNIX System Laboratories, Inc.
5 * All or some portions of this file are derived from material licensed
6 * to the University of California by American Telephone and Telegraph
7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8 * the permission of UNIX System Laboratories, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #ifndef lint
40 static const char copyright[] =
41 "@(#) Copyright (c) 1980, 1990, 1993, 1994\n\
42 The Regents of the University of California. All rights reserved.\n";
43 #endif /* not lint */
44
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95";
48 #else
49 static const char rcsid[] =
50 "$FreeBSD: src/bin/df/df.c,v 1.23.2.9 2002/07/01 00:14:24 iedowse Exp $";
51 #endif
52 #endif /* not lint */
53
54 #ifdef __APPLE__
55 #define MNT_IGNORE 0
56 #include <sys/types.h>
57 typedef int32_t ufs_daddr_t;
58 #endif
59
60 #include <sys/cdefs.h>
61 #include <sys/param.h>
62 #include <sys/stat.h>
63 #include <sys/mount.h>
64 #include <sys/sysctl.h>
65 #include <ufs/ufs/ufsmount.h>
66 #include <ufs/ffs/fs.h>
67
68 #include <err.h>
69 #include <errno.h>
70 #include <fcntl.h>
71 #include <fstab.h>
72 #include <math.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <sysexits.h>
77 #include <unistd.h>
78
79 #define UNITS_SI 1
80 #define UNITS_2 2
81
82 #define KILO_SZ(n) (n)
83 #define MEGA_SZ(n) ((n) * (n))
84 #define GIGA_SZ(n) ((n) * (n) * (n))
85 #define TERA_SZ(n) ((n) * (n) * (n) * (n))
86 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
87
88 #define KILO_2_SZ (KILO_SZ(1024ULL))
89 #define MEGA_2_SZ (MEGA_SZ(1024ULL))
90 #define GIGA_2_SZ (GIGA_SZ(1024ULL))
91 #define TERA_2_SZ (TERA_SZ(1024ULL))
92 #define PETA_2_SZ (PETA_SZ(1024ULL))
93
94 #define KILO_SI_SZ (KILO_SZ(1000ULL))
95 #define MEGA_SI_SZ (MEGA_SZ(1000ULL))
96 #define GIGA_SI_SZ (GIGA_SZ(1000ULL))
97 #define TERA_SI_SZ (TERA_SZ(1000ULL))
98 #define PETA_SI_SZ (PETA_SZ(1000ULL))
99
100 /* Maximum widths of various fields. */
101 struct maxwidths {
102 int mntfrom;
103 int total;
104 int used;
105 int avail;
106 int iused;
107 int ifree;
108 };
109
110 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
111 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
112 unsigned long long *valp;
113
114 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
115
116 unit_t unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
117
118 int bread(off_t, void *, int);
119 int checkvfsname(const char *, char **);
120 char *getmntpt(char *);
121 int longwidth(long long);
122 char *makenetvfslist(void);
123 char **makevfslist(char *);
124 void prthuman(struct statfs *, long);
125 void prthumanval(double);
126 void prtstat(struct statfs *, struct maxwidths *);
127 long regetmntinfo(struct statfs **, long, char **);
128 int ufs_df(char *, struct maxwidths *);
129 unit_t unit_adjust(double *);
130 void update_maxwidths(struct maxwidths *, struct statfs *);
131 void usage(void);
132
133 int aflag = 0, hflag, iflag, nflag;
134 struct ufs_args mdev;
135
136 static __inline int imax(int a, int b)
137 {
138 return (a > b ? a : b);
139 }
140
141 int
142 main(int argc, char *argv[])
143 {
144 struct stat stbuf;
145 struct statfs statfsbuf, *mntbuf;
146 struct maxwidths maxwidths;
147 const char *fstype;
148 char *mntpath, *mntpt, **vfslist;
149 long mntsize;
150 int ch, i, rv, tflag = 0;
151
152 fstype = "ufs";
153
154 vfslist = NULL;
155 while ((ch = getopt(argc, argv, "abgHhiklmnPt:")) != -1)
156 switch (ch) {
157 case 'a':
158 aflag = 1;
159 break;
160 case 'b':
161 /* FALLTHROUGH */
162 case 'P':
163 putenv("BLOCKSIZE=512");
164 hflag = 0;
165 break;
166 case 'g':
167 putenv("BLOCKSIZE=1g");
168 hflag = 0;
169 break;
170 case 'H':
171 hflag = UNITS_SI;
172 valp = vals_si;
173 break;
174 case 'h':
175 hflag = UNITS_2;
176 valp = vals_base2;
177 break;
178 case 'i':
179 iflag = 1;
180 break;
181 case 'k':
182 putenv("BLOCKSIZE=1k");
183 hflag = 0;
184 break;
185 case 'l':
186 if (tflag)
187 errx(1, "-l and -t are mutually exclusive.");
188 if (vfslist != NULL)
189 break;
190 vfslist = makevfslist(makenetvfslist());
191 break;
192 case 'm':
193 putenv("BLOCKSIZE=1m");
194 hflag = 0;
195 break;
196 case 'n':
197 nflag = 1;
198 break;
199 case 't':
200 if (vfslist != NULL) {
201 if (tflag)
202 errx(1, "only one -t option may be specified");
203 else
204 errx(1, "-l and -t are mutually exclusive.");
205 }
206 tflag++;
207 fstype = optarg;
208 vfslist = makevfslist(optarg);
209 break;
210 case '?':
211 default:
212 usage();
213 }
214 argc -= optind;
215 argv += optind;
216
217 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
218 bzero(&maxwidths, sizeof(maxwidths));
219 for (i = 0; i < mntsize; i++)
220 update_maxwidths(&maxwidths, &mntbuf[i]);
221
222 rv = 0;
223 if (!*argv) {
224 mntsize = regetmntinfo(&mntbuf, mntsize, vfslist);
225 bzero(&maxwidths, sizeof(maxwidths));
226 for (i = 0; i < mntsize; i++)
227 update_maxwidths(&maxwidths, &mntbuf[i]);
228 for (i = 0; i < mntsize; i++) {
229 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0)
230 prtstat(&mntbuf[i], &maxwidths);
231 }
232 exit(rv);
233 }
234
235 for (; *argv; argv++) {
236 if (stat(*argv, &stbuf) < 0) {
237 if ((mntpt = getmntpt(*argv)) == 0) {
238 warn("%s", *argv);
239 rv = 1;
240 continue;
241 }
242 } else if (S_ISCHR(stbuf.st_mode)) {
243 if ((mntpt = getmntpt(*argv)) == 0) {
244 mdev.fspec = *argv;
245 mntpath = strdup("/tmp/df.XXXXXX");
246 if (mntpath == NULL) {
247 warn("strdup failed");
248 rv = 1;
249 continue;
250 }
251 mntpt = mkdtemp(mntpath);
252 if (mntpt == NULL) {
253 warn("mkdtemp(\"%s\") failed", mntpath);
254 rv = 1;
255 free(mntpath);
256 continue;
257 }
258 if (mount(fstype, mntpt, MNT_RDONLY,
259 &mdev) != 0) {
260 rv = ufs_df(*argv, &maxwidths) || rv;
261 (void)rmdir(mntpt);
262 free(mntpath);
263 continue;
264 } else if (statfs(mntpt, &statfsbuf) == 0) {
265 statfsbuf.f_mntonname[0] = '\0';
266 prtstat(&statfsbuf, &maxwidths);
267 } else {
268 warn("%s", *argv);
269 rv = 1;
270 }
271 (void)unmount(mntpt, 0);
272 (void)rmdir(mntpt);
273 free(mntpath);
274 continue;
275 }
276 } else
277 mntpt = *argv;
278 /*
279 * Statfs does not take a `wait' flag, so we cannot
280 * implement nflag here.
281 */
282 if (statfs(mntpt, &statfsbuf) < 0) {
283 warn("%s", mntpt);
284 rv = 1;
285 continue;
286 }
287 /* Check to make sure the arguments we've been
288 * given are satisfied. Return an error if we
289 * have been asked to list a mount point that does
290 * not match the other args we've been given (-l, -t, etc.)
291 */
292 if (checkvfsname(statfsbuf.f_fstypename, vfslist)) {
293 rv++;
294 continue;
295 }
296 if (argc == 1) {
297 bzero(&maxwidths, sizeof(maxwidths));
298 update_maxwidths(&maxwidths, &statfsbuf);
299 }
300 prtstat(&statfsbuf, &maxwidths);
301 }
302 return (rv);
303 }
304
305 char *
306 getmntpt(char *name)
307 {
308 long mntsize, i;
309 struct statfs *mntbuf;
310
311 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
312 for (i = 0; i < mntsize; i++) {
313 if (!strcmp(mntbuf[i].f_mntfromname, name))
314 return (mntbuf[i].f_mntonname);
315 }
316 return (0);
317 }
318
319 /*
320 * Make a pass over the filesystem info in ``mntbuf'' filtering out
321 * filesystem types not in vfslist and possibly re-stating to get
322 * current (not cached) info. Returns the new count of valid statfs bufs.
323 */
324 long
325 regetmntinfo(struct statfs **mntbufp, long mntsize, char **vfslist)
326 {
327 int i, j;
328 struct statfs *mntbuf;
329
330 if (vfslist == NULL)
331 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT));
332
333 mntbuf = *mntbufp;
334 for (j = 0, i = 0; i < mntsize; i++) {
335 if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
336 continue;
337 if (!nflag)
338 (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
339 else if (i != j)
340 mntbuf[j] = mntbuf[i];
341 j++;
342 }
343 return (j);
344 }
345
346 /*
347 * Output in "human-readable" format. Uses 3 digits max and puts
348 * unit suffixes at the end. Makes output compact and easy to read,
349 * especially on huge disks.
350 *
351 */
352 unit_t
353 unit_adjust(double *val)
354 {
355 double abval;
356 unit_t unit;
357 unsigned int unit_sz;
358
359 abval = fabs(*val);
360
361 unit_sz = abval ? ilogb(abval) / 10 : 0;
362
363 if (unit_sz >= UNIT_MAX) {
364 unit = NONE;
365 } else {
366 unit = unitp[unit_sz];
367 *val /= (double)valp[unit_sz];
368 }
369
370 return (unit);
371 }
372
373 void
374 prthuman(struct statfs *sfsp, long used)
375 {
376
377 prthumanval((double)sfsp->f_blocks * (double)sfsp->f_bsize);
378 prthumanval((double)used * (double)sfsp->f_bsize);
379 prthumanval((double)sfsp->f_bavail * (double)sfsp->f_bsize);
380 }
381
382 void
383 prthumanval(double bytes)
384 {
385
386 unit_t unit;
387 unit = unit_adjust(&bytes);
388
389 if (bytes == 0)
390 (void)printf(" 0B");
391 else if (bytes > 10)
392 (void)printf(" %5.0f%c", bytes, "BKMGTPE"[unit]);
393 else
394 (void)printf(" %5.1f%c", bytes, "BKMGTPE"[unit]);
395 }
396
397 /*
398 * Convert statfs returned filesystem size into BLOCKSIZE units.
399 * Attempts to avoid overflow for large filesystems.
400 */
401 #define fsbtoblk(num, fsbs, bs) \
402 (((fsbs) != 0 && (fsbs) < (bs)) ? \
403 ((off_t)((unsigned)num)) / ((unsigned)(bs) / ((off_t)((unsigned)fsbs))) : ((off_t)((unsigned)num)) * ((off_t)((unsigned)fsbs)) / (bs))
404
405 /*
406 * Print out status about a filesystem.
407 */
408 void
409 prtstat(struct statfs *sfsp, struct maxwidths *mwp)
410 {
411 static long blocksize;
412 static int headerlen, timesthrough;
413 static const char *header;
414 unsigned long used, availblks, inodes;
415
416 if (++timesthrough == 1) {
417 mwp->mntfrom = imax(mwp->mntfrom, strlen("Filesystem"));
418 if (hflag) {
419 header = " Size";
420 mwp->total = mwp->used = mwp->avail = strlen(header);
421 } else {
422 header = getbsize(&headerlen, &blocksize);
423 mwp->total = imax(mwp->total, headerlen);
424 }
425 mwp->used = imax(mwp->used, strlen("Used"));
426 mwp->avail = imax(mwp->avail, strlen("Avail"));
427
428 (void)printf("%-*s %-*s %*s %*s Capacity", mwp->mntfrom,
429 "Filesystem", mwp->total, header, mwp->used, "Used",
430 mwp->avail, "Avail");
431 if (iflag) {
432 mwp->iused = imax(mwp->iused, strlen(" iused"));
433 mwp->ifree = imax(mwp->ifree, strlen("ifree"));
434 (void)printf(" %*s %*s %%iused", mwp->iused - 2,
435 "iused", mwp->ifree, "ifree");
436 }
437 (void)printf(" Mounted on\n");
438 }
439
440 (void)printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname);
441 used = (off_t)((unsigned)sfsp->f_blocks) - (off_t)((unsigned)sfsp->f_bfree);
442 availblks = sfsp->f_bavail + used;
443 if (hflag) {
444 prthuman(sfsp, used);
445 } else {
446 (void)printf(" %*lld %*lld %*lld", mwp->total,
447 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
448 mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize),
449 mwp->avail, fsbtoblk(sfsp->f_bavail, sfsp->f_bsize,
450 blocksize));
451 }
452 (void)printf(" %5.0f%%",
453 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
454 if (iflag) {
455 inodes = (unsigned)sfsp->f_files;
456 used = inodes - sfsp->f_ffree;
457 (void)printf(" %*lu %*lu %4.0f%% ", mwp->iused, used,
458 mwp->ifree, (unsigned long)sfsp->f_ffree, inodes == 0 ? 100.0 :
459 (double)used / (double)inodes * 100.0);
460 } else
461 (void)printf(" ");
462 (void)printf(" %s\n", sfsp->f_mntonname);
463 }
464
465 /*
466 * Update the maximum field-width information in `mwp' based on
467 * the filesystem specified by `sfsp'.
468 */
469 void
470 update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp)
471 {
472 static long blocksize;
473 int dummy;
474
475 if (blocksize == 0)
476 getbsize(&dummy, &blocksize);
477
478 mwp->mntfrom = imax(mwp->mntfrom, strlen(sfsp->f_mntfromname));
479 mwp->total = imax(mwp->total, longwidth(fsbtoblk(sfsp->f_blocks,
480 sfsp->f_bsize, blocksize)));
481 mwp->used = imax(mwp->used, longwidth(fsbtoblk(sfsp->f_blocks -
482 sfsp->f_bfree, sfsp->f_bsize, blocksize)));
483 mwp->avail = imax(mwp->avail, longwidth(fsbtoblk(sfsp->f_bavail,
484 sfsp->f_bsize, blocksize)));
485 mwp->iused = imax(mwp->iused, longwidth((unsigned)(sfsp->f_files -
486 sfsp->f_ffree)));
487 mwp->ifree = imax(mwp->ifree, longwidth((unsigned)(sfsp->f_ffree)));
488 }
489
490 /* Return the width in characters of the specified long. */
491 int
492 longwidth(long long val)
493 {
494 int len;
495
496 len = 0;
497 /* Negative or zero values require one extra digit. */
498 if (val <= 0) {
499 val = -val;
500 len++;
501 }
502 while (val > 0) {
503 len++;
504 val /= 10;
505 }
506
507 return (len);
508 }
509
510 /*
511 * This code constitutes the pre-system call Berkeley df code for extracting
512 * information from filesystem superblocks.
513 */
514
515 union {
516 struct fs iu_fs;
517 char dummy[SBSIZE];
518 } sb;
519 #define sblock sb.iu_fs
520
521 int rfd;
522
523 int
524 ufs_df(char *file, struct maxwidths *mwp)
525 {
526 struct statfs statfsbuf;
527 struct statfs *sfsp;
528 const char *mntpt;
529 static int synced;
530
531 if (synced++ == 0)
532 sync();
533
534 if ((rfd = open(file, O_RDONLY)) < 0) {
535 warn("%s", file);
536 return (1);
537 }
538 if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) {
539 (void)close(rfd);
540 return (1);
541 }
542 sfsp = &statfsbuf;
543 sfsp->f_type = 1;
544 strcpy(sfsp->f_fstypename, "ufs");
545 sfsp->f_flags = 0;
546 sfsp->f_bsize = sblock.fs_fsize;
547 sfsp->f_iosize = sblock.fs_bsize;
548 sfsp->f_blocks = sblock.fs_dsize;
549 sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
550 sblock.fs_cstotal.cs_nffree;
551 sfsp->f_bavail = freespace(&sblock, sblock.fs_minfree);
552 sfsp->f_files = sblock.fs_ncg * sblock.fs_ipg;
553 sfsp->f_ffree = sblock.fs_cstotal.cs_nifree;
554 sfsp->f_fsid.val[0] = 0;
555 sfsp->f_fsid.val[1] = 0;
556 if ((mntpt = getmntpt(file)) == 0)
557 mntpt = "";
558 memmove(&sfsp->f_mntonname[0], mntpt, (size_t)MNAMELEN);
559 memmove(&sfsp->f_mntfromname[0], file, (size_t)MNAMELEN);
560 prtstat(sfsp, mwp);
561 (void)close(rfd);
562 return (0);
563 }
564
565 int
566 bread(off_t off, void *buf, int cnt)
567 {
568 ssize_t nr;
569
570 (void)lseek(rfd, off, SEEK_SET);
571 if ((nr = read(rfd, buf, (size_t)cnt)) != (ssize_t)cnt) {
572 /* Probably a dismounted disk if errno == EIO. */
573 if (errno != EIO)
574 (void)fprintf(stderr, "\ndf: %lld: %s\n",
575 (long long)off, strerror(nr > 0 ? EIO : errno));
576 return (0);
577 }
578 return (1);
579 }
580
581 void
582 usage(void)
583 {
584
585 (void)fprintf(stderr,
586 "usage: df [-b | -H | -h | -k | -m | -P] [-ailn] [-t type] [file | filesystem ...]\n");
587 exit(EX_USAGE);
588 }
589
590 char *
591 makenetvfslist(void)
592 {
593 char *str, *strptr, **listptr;
594 #ifndef __APPLE__
595 int mib[3], maxvfsconf, cnt=0, i;
596 size_t miblen;
597 struct ovfsconf *ptr;
598 #else
599 int mib[4], maxvfsconf, cnt=0, i;
600 size_t miblen;
601 struct vfsconf vfc;
602 #endif
603
604 mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM;
605 miblen=sizeof(maxvfsconf);
606 if (sysctl(mib, (unsigned int)(sizeof(mib) / sizeof(mib[0])),
607 &maxvfsconf, &miblen, NULL, 0)) {
608 warnx("sysctl failed");
609 return (NULL);
610 }
611
612 if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) {
613 warnx("malloc failed");
614 return (NULL);
615 }
616
617 #ifndef __APPLE__
618 for (ptr = getvfsent(); ptr; ptr = getvfsent())
619 if (ptr->vfc_flags & VFCF_NETWORK) {
620 listptr[cnt++] = strdup(ptr->vfc_name);
621 if (listptr[cnt-1] == NULL) {
622 warnx("malloc failed");
623 return (NULL);
624 }
625 }
626 #else
627 miblen = sizeof (struct vfsconf);
628 mib[2] = VFS_CONF;
629 for (i = 0; i < maxvfsconf; i++) {
630 mib[3] = i;
631 if (sysctl(mib, 4, &vfc, &miblen, NULL, 0) == 0) {
632 if (!(vfc.vfc_flags & MNT_LOCAL)) {
633 listptr[cnt++] = strdup(vfc.vfc_name);
634 if (listptr[cnt-1] == NULL) {
635 warnx("malloc failed");
636 return (NULL);
637 }
638 }
639 }
640 }
641 #endif
642
643 if (cnt == 0 ||
644 (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) {
645 if (cnt > 0)
646 warnx("malloc failed");
647 free(listptr);
648 return (NULL);
649 }
650
651 *str = 'n'; *(str + 1) = 'o';
652 for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) {
653 strncpy(strptr, listptr[i], 32);
654 strptr += strlen(listptr[i]);
655 *strptr = ',';
656 free(listptr[i]);
657 }
658 *(--strptr) = NULL;
659
660 free(listptr);
661 return (str);
662 }