7286961b833104f66fb3650efca258c367db7fe2
[dragonfly.git] / bin / df / df.c
blob7286961b833104f66fb3650efca258c367db7fe2
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.
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.
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.
38 * @(#) Copyright (c) 1980, 1990, 1993, 1994 The Regents of the University of California. All rights reserved.
39 * @(#)df.c 8.9 (Berkeley) 5/8/95
40 * $FreeBSD: src/bin/df/df.c,v 1.23.2.9 2002/07/01 00:14:24 iedowse Exp $
41 * $DragonFly: src/bin/df/df.c,v 1.10 2008/06/01 20:52:21 dillon Exp $
44 #include <sys/cdefs.h>
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/mount.h>
48 #include <sys/sysctl.h>
49 #include <sys/statvfs.h>
51 #include <vfs/ufs/dinode.h>
52 #include <vfs/ufs/fs.h>
53 #include <vfs/ufs/ufsmount.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <fstab.h>
59 #include <math.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <sysexits.h>
64 #include <unistd.h>
66 #define UNITS_SI 1
67 #define UNITS_2 2
69 #define KILO_SZ(n) (n)
70 #define MEGA_SZ(n) ((n) * (n))
71 #define GIGA_SZ(n) ((n) * (n) * (n))
72 #define TERA_SZ(n) ((n) * (n) * (n) * (n))
73 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
75 #define KILO_2_SZ (KILO_SZ(1024ULL))
76 #define MEGA_2_SZ (MEGA_SZ(1024ULL))
77 #define GIGA_2_SZ (GIGA_SZ(1024ULL))
78 #define TERA_2_SZ (TERA_SZ(1024ULL))
79 #define PETA_2_SZ (PETA_SZ(1024ULL))
81 #define KILO_SI_SZ (KILO_SZ(1000ULL))
82 #define MEGA_SI_SZ (MEGA_SZ(1000ULL))
83 #define GIGA_SI_SZ (GIGA_SZ(1000ULL))
84 #define TERA_SI_SZ (TERA_SZ(1000ULL))
85 #define PETA_SI_SZ (PETA_SZ(1000ULL))
87 /* Maximum widths of various fields. */
88 struct maxwidths {
89 int mntfrom;
90 int total;
91 int used;
92 int avail;
93 int iused;
94 int ifree;
97 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
98 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
99 unsigned long long *valp;
101 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
103 unit_t unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
105 int bread(off_t, void *, int);
106 int checkvfsname(const char *, char **);
107 char *getmntpt(char *);
108 int quadwidth(int64_t);
109 char *makenetvfslist(void);
110 char **makevfslist(char *);
111 void prthuman(struct statvfs *, long);
112 void prthumanval(double);
113 void prtstat(struct statfs *, struct statvfs *, struct maxwidths *);
114 long regetmntinfo(struct statfs **, struct statvfs **, long, char **);
115 int ufs_df(char *, struct maxwidths *);
116 unit_t unit_adjust(double *);
117 void update_maxwidths(struct maxwidths *, struct statfs *, struct statvfs *);
118 void usage(void);
120 int aflag = 0, hflag, iflag, nflag;
121 struct ufs_args mdev;
123 static __inline int
124 imax(int a, int b)
126 return (a > b ? a : b);
129 static __inline int64_t
130 qmax(int64_t a, int64_t b)
132 return (a > b ? a : b);
136 main(int argc, char **argv)
138 struct stat stbuf;
139 struct statfs statfsbuf, *mntbuf;
140 struct statvfs statvfsbuf, *mntvbuf;
141 struct maxwidths maxwidths;
142 const char *fstype;
143 char *mntpath, *mntpt, **vfslist;
144 long mntsize;
145 int ch, i, rv;
147 fstype = "ufs";
149 vfslist = NULL;
150 while ((ch = getopt(argc, argv, "abgHhiklmnPt:")) != -1)
151 switch (ch) {
152 case 'a':
153 aflag = 1;
154 break;
155 case 'b':
156 /* FALLTHROUGH */
157 case 'P':
158 if (putenv("BLOCKSIZE=512") != 0)
159 warn("putenv: cannot set BLOCKSIZE=512");
160 hflag = 0;
161 break;
162 case 'g':
163 if (putenv("BLOCKSIZE=1g") != 0)
164 warn("putenv: cannot set BLOCKSIZE=1g");
165 hflag = 0;
166 break;
167 case 'H':
168 hflag = UNITS_SI;
169 valp = vals_si;
170 break;
171 case 'h':
172 hflag = UNITS_2;
173 valp = vals_base2;
174 break;
175 case 'i':
176 iflag = 1;
177 break;
178 case 'k':
179 if (putenv("BLOCKSIZE=1k") != 0)
180 warn("putenv: cannot set BLOCKSIZE=1k");
181 hflag = 0;
182 break;
183 case 'l':
184 if (vfslist != NULL)
185 errx(1, "-l and -t are mutually exclusive.");
186 vfslist = makevfslist(makenetvfslist());
187 break;
188 case 'm':
189 if (putenv("BLOCKSIZE=1m") != 0)
190 warn("putenv: cannot set BLOCKSIZE=1m");
191 hflag = 0;
192 break;
193 case 'n':
194 nflag = 1;
195 break;
196 case 't':
197 if (vfslist != NULL)
198 errx(1, "only one -t option may be specified");
199 fstype = optarg;
200 vfslist = makevfslist(optarg);
201 break;
202 case '?':
203 default:
204 usage();
206 argc -= optind;
207 argv += optind;
209 mntsize = getmntvinfo(&mntbuf, &mntvbuf, MNT_NOWAIT);
210 bzero(&maxwidths, sizeof(maxwidths));
211 for (i = 0; i < mntsize; i++)
212 update_maxwidths(&maxwidths, &mntbuf[i], &mntvbuf[i]);
214 rv = 0;
215 if (!*argv) {
216 mntsize = regetmntinfo(&mntbuf, &mntvbuf, mntsize, vfslist);
217 bzero(&maxwidths, sizeof(maxwidths));
218 for (i = 0; i < mntsize; i++)
219 update_maxwidths(&maxwidths, &mntbuf[i], &mntvbuf[i]);
220 for (i = 0; i < mntsize; i++) {
221 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0)
222 prtstat(&mntbuf[i], &mntvbuf[i], &maxwidths);
224 exit(rv);
227 for (; *argv; argv++) {
228 if (stat(*argv, &stbuf) < 0) {
229 if ((mntpt = getmntpt(*argv)) == 0) {
230 warn("%s", *argv);
231 rv = 1;
232 continue;
234 } else if (S_ISCHR(stbuf.st_mode)) {
235 if ((mntpt = getmntpt(*argv)) == 0) {
236 mdev.fspec = *argv;
237 mntpath = strdup("/tmp/df.XXXXXX");
238 if (mntpath == NULL) {
239 warn("strdup failed");
240 rv = 1;
241 continue;
243 mntpt = mkdtemp(mntpath);
244 if (mntpt == NULL) {
245 warn("mkdtemp(\"%s\") failed", mntpath);
246 rv = 1;
247 free(mntpath);
248 continue;
250 if (mount(fstype, mntpt, MNT_RDONLY,
251 &mdev) != 0) {
252 rv = ufs_df(*argv, &maxwidths) || rv;
253 rmdir(mntpt);
254 free(mntpath);
255 continue;
256 } else if (statfs(mntpt, &statfsbuf) == 0 &&
257 statvfs(mntpt, &statvfsbuf) == 0) {
258 statfsbuf.f_mntonname[0] = '\0';
259 prtstat(&statfsbuf, &statvfsbuf, &maxwidths);
260 } else {
261 warn("%s", *argv);
262 rv = 1;
264 unmount(mntpt, 0);
265 rmdir(mntpt);
266 free(mntpath);
267 continue;
269 } else
270 mntpt = *argv;
272 * Statfs does not take a `wait' flag, so we cannot
273 * implement nflag here.
275 if (statfs(mntpt, &statfsbuf) < 0) {
276 warn("%s", mntpt);
277 rv = 1;
278 continue;
280 if (statvfs(mntpt, &statvfsbuf) < 0) {
281 warn("%s", mntpt);
282 rv = 1;
283 continue;
285 if (argc == 1) {
286 bzero(&maxwidths, sizeof(maxwidths));
287 update_maxwidths(&maxwidths, &statfsbuf, &statvfsbuf);
289 prtstat(&statfsbuf, &statvfsbuf, &maxwidths);
291 return (rv);
294 char *
295 getmntpt(char *name)
297 long mntsize, i;
298 struct statfs *mntbuf;
299 struct statvfs *mntvbuf;
301 mntsize = getmntvinfo(&mntbuf, &mntvbuf, MNT_NOWAIT);
302 for (i = 0; i < mntsize; i++) {
303 if (!strcmp(mntbuf[i].f_mntfromname, name))
304 return (mntbuf[i].f_mntonname);
306 return (0);
310 * Make a pass over the filesystem info in ``mntbuf'' filtering out
311 * filesystem types not in vfslist and possibly re-stating to get
312 * current (not cached) info. Returns the new count of valid statfs bufs.
314 long
315 regetmntinfo(struct statfs **mntbufp, struct statvfs **mntvbufp, long mntsize, char **vfslist)
317 int i, j;
318 struct statfs *mntbuf;
319 struct statvfs *mntvbuf;
321 if (vfslist == NULL)
322 return (nflag ? mntsize : getmntvinfo(mntbufp, mntvbufp, MNT_WAIT));
324 mntbuf = *mntbufp;
325 mntvbuf = *mntvbufp;
326 for (j = 0, i = 0; i < mntsize; i++) {
327 if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
328 continue;
329 if (!nflag) {
330 statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
331 statvfs(mntbuf[i].f_mntonname,&mntvbuf[j]);
332 } else if (i != j) {
333 mntbuf[j] = mntbuf[i];
334 mntvbuf[j] = mntvbuf[i];
336 j++;
338 return (j);
342 * Output in "human-readable" format. Uses 3 digits max and puts
343 * unit suffixes at the end. Makes output compact and easy to read,
344 * especially on huge disks.
347 unit_t
348 unit_adjust(double *val)
350 double abval;
351 unit_t unit;
352 unsigned int unit_sz;
354 abval = fabs(*val);
356 unit_sz = abval ? ilogb(abval) / 10 : 0;
358 if (unit_sz >= UNIT_MAX) {
359 unit = NONE;
360 } else {
361 unit = unitp[unit_sz];
362 *val /= (double)valp[unit_sz];
365 return (unit);
368 void
369 prthuman(struct statvfs *vsfsp, long used)
371 prthumanval((double)vsfsp->f_blocks * (double)vsfsp->f_bsize);
372 prthumanval((double)used * (double)vsfsp->f_bsize);
373 prthumanval((double)vsfsp->f_bavail * (double)vsfsp->f_bsize);
376 void
377 prthumanval(double bytes)
380 unit_t unit;
381 unit = unit_adjust(&bytes);
383 if (bytes == 0)
384 printf(" 0B");
385 else if (bytes > 10)
386 printf(" %5.0f%c", bytes, "BKMGTPE"[unit]);
387 else
388 printf(" %5.1f%c", bytes, "BKMGTPE"[unit]);
392 * Convert statfs returned filesystem size into BLOCKSIZE units.
393 * Attempts to avoid overflow for large filesystems.
395 static
396 int64_t
397 fsbtoblk(int64_t num, long bsize, long reqbsize)
399 if (bsize && bsize < reqbsize)
400 num = num / (reqbsize / bsize);
401 else
402 num = num * (bsize / reqbsize);
403 return(num);
407 * Print out status about a filesystem.
409 void
410 prtstat(struct statfs *sfsp, struct statvfs *vsfsp, struct maxwidths *mwp)
412 static long blocksize;
413 static int headerlen, timesthrough;
414 static const char *header;
415 int64_t used, availblks, inodes;
417 if (++timesthrough == 1) {
418 mwp->mntfrom = imax(mwp->mntfrom, strlen("Filesystem"));
419 if (hflag) {
420 header = " Size";
421 mwp->total = mwp->used = mwp->avail = strlen(header);
422 } else {
423 header = getbsize(&headerlen, &blocksize);
424 mwp->total = imax(mwp->total, headerlen);
426 mwp->used = imax(mwp->used, strlen("Used"));
427 mwp->avail = imax(mwp->avail, strlen("Avail"));
429 printf("%-*s %-*s %*s %*s Capacity", mwp->mntfrom,
430 "Filesystem", mwp->total, header, mwp->used, "Used",
431 mwp->avail, "Avail");
432 if (iflag) {
433 mwp->iused = imax(mwp->iused, strlen(" iused"));
434 mwp->ifree = imax(mwp->ifree, strlen("ifree"));
435 printf(" %*s %*s %%iused", mwp->iused - 2,
436 "iused", mwp->ifree, "ifree");
438 printf(" Mounted on\n");
440 printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname);
441 used = vsfsp->f_blocks - vsfsp->f_bfree;
442 availblks = vsfsp->f_bavail + used;
443 if (hflag) {
444 prthuman(vsfsp, used);
445 } else {
446 printf(" %*lld %*lld %*lld", mwp->total,
447 fsbtoblk(vsfsp->f_blocks, vsfsp->f_bsize, blocksize),
448 mwp->used, fsbtoblk(used, vsfsp->f_bsize, blocksize),
449 mwp->avail, fsbtoblk(vsfsp->f_bavail, vsfsp->f_bsize,
450 blocksize));
452 printf(" %5.0f%%",
453 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
454 if (iflag) {
455 inodes = vsfsp->f_files;
456 used = inodes - vsfsp->f_ffree;
457 printf(" %*lld %*lld %4.0f%% ", mwp->iused, used,
458 mwp->ifree, (int64_t)vsfsp->f_ffree, inodes == 0 ? 100.0 :
459 (double)used / (double)inodes * 100.0);
460 } else
461 printf(" ");
462 printf(" %s\n", sfsp->f_mntonname);
466 * Update the maximum field-width information in `mwp' based on
467 * the filesystem specified by `sfsp'.
469 void
470 update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp, struct statvfs *vsfsp)
472 static long blocksize;
473 int dummy;
475 if (blocksize == 0)
476 getbsize(&dummy, &blocksize);
478 mwp->mntfrom = imax(mwp->mntfrom, strlen(sfsp->f_mntfromname));
479 mwp->total = imax(mwp->total, quadwidth(fsbtoblk(vsfsp->f_blocks,
480 vsfsp->f_bsize, blocksize)));
481 mwp->used = imax(mwp->used, quadwidth(fsbtoblk(vsfsp->f_blocks -
482 vsfsp->f_bfree, vsfsp->f_bsize, blocksize)));
483 mwp->avail = imax(mwp->avail, quadwidth(fsbtoblk(vsfsp->f_bavail,
484 vsfsp->f_bsize, blocksize)));
485 mwp->iused = imax(mwp->iused, quadwidth(vsfsp->f_files -
486 vsfsp->f_ffree));
487 mwp->ifree = imax(mwp->ifree, quadwidth(vsfsp->f_ffree));
490 /* Return the width in characters of the specified long. */
492 quadwidth(int64_t val)
494 int len;
496 len = 0;
497 /* Negative or zero values require one extra digit. */
498 if (val <= 0) {
499 val = -val;
500 len++;
502 while (val > 0) {
503 len++;
504 val /= 10;
506 return (len);
510 * This code constitutes the pre-system call Berkeley df code for extracting
511 * information from filesystem superblocks.
514 union {
515 struct fs iu_fs;
516 char dummy[SBSIZE];
517 } sb;
518 #define sblock sb.iu_fs
520 int rfd;
523 ufs_df(char *file, struct maxwidths *mwp)
525 struct statfs statfsbuf;
526 struct statvfs statvfsbuf;
527 struct statfs *sfsp;
528 struct statvfs *vsfsp;
529 const char *mntpt;
530 static int synced;
532 if (synced++ == 0)
533 sync();
535 if ((rfd = open(file, O_RDONLY)) < 0) {
536 warn("%s", file);
537 return (1);
539 if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) {
540 close(rfd);
541 return (1);
543 sfsp = &statfsbuf;
544 vsfsp = &statvfsbuf;
545 sfsp->f_type = 1;
546 strcpy(sfsp->f_fstypename, "ufs");
547 sfsp->f_flags = 0;
548 sfsp->f_bsize = vsfsp->f_bsize = sblock.fs_fsize;
549 sfsp->f_iosize = vsfsp->f_frsize = sblock.fs_bsize;
550 sfsp->f_blocks = vsfsp->f_blocks = sblock.fs_dsize;
551 sfsp->f_bfree = vsfsp->f_bfree =
552 sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
553 sblock.fs_cstotal.cs_nffree;
554 sfsp->f_bavail = vsfsp->f_bavail = freespace(&sblock, sblock.fs_minfree);
555 sfsp->f_files = vsfsp->f_files = sblock.fs_ncg * sblock.fs_ipg;
556 sfsp->f_ffree = vsfsp->f_ffree = sblock.fs_cstotal.cs_nifree;
557 sfsp->f_fsid.val[0] = 0;
558 sfsp->f_fsid.val[1] = 0;
559 if ((mntpt = getmntpt(file)) == 0)
560 mntpt = "";
561 memmove(&sfsp->f_mntonname[0], mntpt, (size_t)MNAMELEN);
562 memmove(&sfsp->f_mntfromname[0], file, (size_t)MNAMELEN);
563 prtstat(sfsp, vsfsp, mwp);
564 close(rfd);
565 return (0);
569 bread(off_t off, void *buf, int cnt)
571 ssize_t nr;
573 lseek(rfd, off, SEEK_SET);
574 if ((nr = read(rfd, buf, (size_t)cnt)) != (ssize_t)cnt) {
575 /* Probably a dismounted disk if errno == EIO. */
576 if (errno != EIO)
577 fprintf(stderr, "\ndf: %lld: %s\n",
578 (long long)off, strerror(nr > 0 ? EIO : errno));
579 return (0);
581 return (1);
584 void
585 usage(void)
588 fprintf(stderr,
589 "usage: df [-b | -H | -h | -k | -m | -P] [-ailn] [-t type] [file | filesystem ...]\n");
590 exit(EX_USAGE);
593 char *
594 makenetvfslist(void)
596 char *str, *strptr, **listptr;
597 int mib[3], maxvfsconf, cnt=0, i;
598 size_t miblen;
599 struct ovfsconf *ptr;
601 mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM;
602 miblen=sizeof(maxvfsconf);
603 if (sysctl(mib, (unsigned int)(sizeof(mib) / sizeof(mib[0])),
604 &maxvfsconf, &miblen, NULL, 0)) {
605 warnx("sysctl failed");
606 return (NULL);
609 if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) {
610 warnx("malloc failed");
611 return (NULL);
614 for (ptr = getvfsent(); ptr; ptr = getvfsent())
615 if (ptr->vfc_flags & VFCF_NETWORK) {
616 listptr[cnt++] = strdup(ptr->vfc_name);
617 if (listptr[cnt-1] == NULL) {
618 warnx("malloc failed");
619 return (NULL);
623 if (cnt == 0 ||
624 (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) {
625 if (cnt > 0)
626 warnx("malloc failed");
627 free(listptr);
628 return (NULL);
631 *str = 'n'; *(str + 1) = 'o';
632 for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) {
633 strncpy(strptr, listptr[i], 32);
634 strptr += strlen(listptr[i]);
635 *strptr = ',';
636 free(listptr[i]);
638 *(--strptr) = '\0';
640 free(listptr);
641 return (str);