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 40static 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 47static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95"; 48#else 49static 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#endif 57 58#include <sys/cdefs.h> 59#include <sys/param.h> 60#include <sys/stat.h> 61#include <sys/mount.h> 62#include <sys/sysctl.h> 63#include <err.h> 64#include <errno.h> 65#include <fcntl.h> 66#include <fstab.h> 67#include <math.h> 68#include <stdio.h> 69#include <stdlib.h> 70#include <string.h> 71#include <sysexits.h> 72#include <unistd.h> 73#include <libutil.h> 74 75#ifdef __APPLE__ 76#include "get_compat.h" 77#else 78#define COMPAT_MODE(func, mode) 1 79#endif 80 81#define UNITS_SI 1 82#define UNITS_2 2 83 84#define KILO_SZ(n) (n) 85#define MEGA_SZ(n) ((n) * (n)) 86#define GIGA_SZ(n) ((n) * (n) * (n)) 87#define TERA_SZ(n) ((n) * (n) * (n) * (n)) 88#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) 89 90#define KILO_2_SZ (KILO_SZ(1024ULL)) 91#define MEGA_2_SZ (MEGA_SZ(1024ULL)) 92#define GIGA_2_SZ (GIGA_SZ(1024ULL)) 93#define TERA_2_SZ (TERA_SZ(1024ULL)) 94#define PETA_2_SZ (PETA_SZ(1024ULL)) 95 96#define KILO_SI_SZ (KILO_SZ(1000ULL)) 97#define MEGA_SI_SZ (MEGA_SZ(1000ULL)) 98#define GIGA_SI_SZ (GIGA_SZ(1000ULL)) 99#define TERA_SI_SZ (TERA_SZ(1000ULL)) 100#define PETA_SI_SZ (PETA_SZ(1000ULL)) 101 102/* Maximum widths of various fields. */ 103struct maxwidths { 104 int mntfrom; 105 int total; 106 int used; 107 int avail; 108 int iused; 109 int ifree; 110}; 111 112unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; 113unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; 114unsigned long long *valp; 115 116typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; 117 118unit_t unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; 119 120int bread(off_t, void *, int); 121int checkvfsname(const char *, char **); 122char *getmntpt(char *); 123int longwidth(long long); 124char *makenetvfslist(void); 125char **makevfslist(const char *); 126void prthuman(struct statfs *, uint64_t); 127void prthumanval(int64_t); 128void prtstat(struct statfs *, struct maxwidths *); 129long regetmntinfo(struct statfs **, long, char **); 130unit_t unit_adjust(double *); 131void update_maxwidths(struct maxwidths *, struct statfs *); 132void usage(void); 133 134int aflag = 0, hflag, iflag, nflag; 135 136static __inline int imax(int a, int b) 137{ 138 return (a > b ? a : b); 139} 140 141int 142main(int argc, char *argv[]) 143{ 144 struct stat stbuf; 145 struct statfs statfsbuf, *mntbuf; 146 struct maxwidths maxwidths; 147 char *mntpt, **vfslist; 148 long mntsize; 149 int ch, i, rv, tflag = 0, kludge_tflag = 0; 150 int kflag = 0; 151 const char *options = "abgHhiklmnPt:T:"; 152 if (COMPAT_MODE("bin/df", "unix2003")) { 153 /* Unix2003 requires -t be "include total capacity". which df 154 already does, but it conflicts with the old -t so we need to 155 *not* expect a string after -t (we provide -T in both cases 156 to cover the old use of -t) */ 157 options = "abgHhiklmnPtT:"; 158 iflag = 1; 159 } 160 161 vfslist = NULL; 162 while ((ch = getopt(argc, argv, options)) != -1) 163 switch (ch) { 164 case 'a': 165 aflag = 1; 166 break; 167 case 'b': 168 /* FALLTHROUGH */ 169 case 'P': 170 if (COMPAT_MODE("bin/df", "unix2003")) { 171 if (!kflag) { 172 /* -k overrides -P */ 173 putenv("BLOCKSIZE=512"); 174 } 175 iflag = 0; 176 } else { 177 putenv("BLOCKSIZE=512"); 178 } 179 hflag = 0; 180 break; 181 case 'g': 182 putenv("BLOCKSIZE=1g"); 183 hflag = 0; 184 break; 185 case 'H': 186 hflag = UNITS_SI; 187 valp = vals_si; 188 break; 189 case 'h': 190 hflag = UNITS_2; 191 valp = vals_base2; 192 break; 193 case 'i': 194 iflag = 1; 195 break; 196 case 'k': 197 if (COMPAT_MODE("bin/df", "unix2003")) { 198 putenv("BLOCKSIZE=1024"); 199 } else { 200 putenv("BLOCKSIZE=1k"); 201 } 202 kflag = 1; 203 hflag = 0; 204 break; 205 case 'l': 206 if (tflag) 207 errx(1, "-l and -T are mutually exclusive."); 208 if (vfslist != NULL) 209 break; 210 vfslist = makevfslist(makenetvfslist()); 211 break; 212 case 'm': 213 putenv("BLOCKSIZE=1m"); 214 hflag = 0; 215 break; 216 case 'n': 217 nflag = 1; 218 break; 219 case 't': 220 /* Unix2003 uses -t for something we do by default */ 221 if (COMPAT_MODE("bin/df", "unix2003")) { 222 kludge_tflag = 1; 223 break; 224 } 225 case 'T': 226 if (vfslist != NULL) { 227 if (tflag) 228 errx(1, "only one -%c option may be specified", ch); 229 else 230 errx(1, "-l and -%c are mutually exclusive.", ch); 231 } 232 tflag++; 233 vfslist = makevfslist(optarg); 234 break; 235 case '?': 236 default: 237 usage(); 238 } 239 argc -= optind; 240 argv += optind; 241 242 /* If we are in unix2003 mode, have seen a -t but no -T and the first 243 non switch arg isn't a file, let's pretend they used -T on it. 244 This makes the Lexmark printer installer happy (PR-3918471) */ 245 if (tflag == 0 && kludge_tflag && *argv && stat(*argv, &stbuf) < 0 246 && errno == ENOENT) { 247 vfslist = makevfslist(*argv++); 248 } 249 250 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 251 bzero(&maxwidths, sizeof(maxwidths)); 252 for (i = 0; i < mntsize; i++) 253 update_maxwidths(&maxwidths, &mntbuf[i]); 254 255 rv = 0; 256 if (!*argv) { 257 mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); 258 bzero(&maxwidths, sizeof(maxwidths)); 259 for (i = 0; i < mntsize; i++) 260 update_maxwidths(&maxwidths, &mntbuf[i]); 261 for (i = 0; i < mntsize; i++) { 262 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) 263 prtstat(&mntbuf[i], &maxwidths); 264 } 265 exit(rv); 266 } 267 268 for (; *argv; argv++) { 269 if (stat(*argv, &stbuf) < 0) { 270 if ((mntpt = getmntpt(*argv)) == 0) { 271 warn("%s", *argv); 272 rv = 1; 273 continue; 274 } 275 } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) { 276 warnx("%s: Raw devices not supported", *argv); 277 rv = 1; 278 continue; 279 } else 280 mntpt = *argv; 281 /* 282 * Statfs does not take a `wait' flag, so we cannot 283 * implement nflag here. 284 */ 285 if (statfs(mntpt, &statfsbuf) < 0) { 286 warn("%s", mntpt); 287 rv = 1; 288 continue; 289 } 290 /* Check to make sure the arguments we've been 291 * given are satisfied. Return an error if we 292 * have been asked to list a mount point that does 293 * not match the other args we've been given (-l, -t, etc.) 294 */ 295 if (checkvfsname(statfsbuf.f_fstypename, vfslist)) { 296 rv++; 297 continue; 298 } 299 300 if (argc == 1) { 301 bzero(&maxwidths, sizeof(maxwidths)); 302 update_maxwidths(&maxwidths, &statfsbuf); 303 } 304 prtstat(&statfsbuf, &maxwidths); 305 } 306 return (rv); 307} 308 309char * 310getmntpt(char *name) 311{ 312 long mntsize, i; 313 struct statfs *mntbuf; 314 315 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 316 for (i = 0; i < mntsize; i++) { 317 if (!strcmp(mntbuf[i].f_mntfromname, name)) 318 return (mntbuf[i].f_mntonname); 319 } 320 return (0); 321} 322 323/* 324 * Make a pass over the filesystem info in ``mntbuf'' filtering out 325 * filesystem types not in vfslist and possibly re-stating to get 326 * current (not cached) info. Returns the new count of valid statfs bufs. 327 */ 328long 329regetmntinfo(struct statfs **mntbufp, long mntsize, char **vfslist) 330{ 331 int i, j; 332 struct statfs *mntbuf; 333 334 if (vfslist == NULL) 335 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 336 337 mntbuf = *mntbufp; 338 for (j = 0, i = 0; i < mntsize; i++) { 339 if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 340 continue; 341 if (!nflag) 342 (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); 343 else if (i != j) 344 mntbuf[j] = mntbuf[i]; 345 j++; 346 } 347 return (j); 348} 349 350/* 351 * Output in "human-readable" format. Uses 3 digits max and puts 352 * unit suffixes at the end. Makes output compact and easy to read, 353 * especially on huge disks. 354 * 355 */ 356unit_t 357unit_adjust(double *val) 358{ 359 double abval; 360 unit_t unit; 361 unsigned int unit_sz; 362 363 abval = fabs(*val); 364 365 unit_sz = abval ? ilogb(abval) / 10 : 0; 366 367 if (unit_sz >= UNIT_MAX) { 368 unit = NONE; 369 } else { 370 unit = unitp[unit_sz]; 371 *val /= (double)valp[unit_sz]; 372 } 373 374 return (unit); 375} 376 377void 378prthuman(struct statfs *sfsp, uint64_t used) 379{ 380 int64_t value; 381 382 value = sfsp->f_blocks; 383 value *= sfsp->f_bsize; 384 prthumanval(value); 385 value = used; 386 value *= sfsp->f_bsize; 387 prthumanval(value); 388 value = sfsp->f_bavail; 389 value *= sfsp->f_bsize; 390 prthumanval(value); 391} 392 393void 394prthumanval(int64_t bytes) 395{ 396 char buf[6]; 397 int flags; 398 399 flags = HN_B | HN_NOSPACE | HN_DECIMAL; 400 if (hflag == UNITS_SI) 401 flags |= HN_DIVISOR_1000; 402 403 humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), 404 bytes, "", HN_AUTOSCALE, flags); 405 406 if (hflag == UNITS_SI) 407 (void)printf(" %6s", buf); 408 else 409 (void)printf("%6si", buf); 410 411} 412 413/* 414 * Convert statfs returned filesystem size into BLOCKSIZE units. 415 * Attempts to avoid overflow for large filesystems. 416 */ 417static intmax_t fsbtoblk(int64_t num, uint64_t fsbs, u_long bs, char *fs) 418{ 419 if (num < 0) { 420 warnx("negative filesystem block count/size from fs %s", fs); 421 return 0; 422 } else if ((fsbs != 0) && (fsbs < bs)) { 423 return (num / (intmax_t) (bs / fsbs)); 424 } else { 425 return (num * (intmax_t) (fsbs / bs)); 426 } 427} 428 429/* 430 * Print out status about a filesystem. 431 */ 432void 433prtstat(struct statfs *sfsp, struct maxwidths *mwp) 434{ 435 static long blocksize; 436 static int headerlen, timesthrough; 437 static const char *header; 438 uint64_t used, availblks, inodes; 439 char * avail_str; 440 441 if (++timesthrough == 1) { 442 mwp->mntfrom = imax(mwp->mntfrom, (int)strlen("Filesystem")); 443 if (hflag) { 444 header = " Size"; 445 mwp->total = mwp->used = mwp->avail = (int)strlen(header); 446 } else { 447 header = getbsize(&headerlen, &blocksize); 448 mwp->total = imax(mwp->total, headerlen); 449 } 450 mwp->used = imax(mwp->used, (int)strlen("Used")); 451 if (COMPAT_MODE("bin/df", "unix2003") && !hflag) { 452 avail_str = "Available"; 453 } else { 454 avail_str = "Avail"; 455 } 456 mwp->avail = imax(mwp->avail, (int)strlen(avail_str)); 457 458 (void)printf("%-*s %*s %*s %*s Capacity", mwp->mntfrom, 459 "Filesystem", mwp->total, header, mwp->used, "Used", 460 mwp->avail, avail_str); 461 if (iflag) { 462 mwp->iused = imax(mwp->iused, (int)strlen(" iused")); 463 mwp->ifree = imax(mwp->ifree, (int)strlen("ifree")); 464 (void)printf(" %*s %*s %%iused", mwp->iused - 2, 465 "iused", mwp->ifree, "ifree"); 466 } 467 (void)printf(" Mounted on\n"); 468 } 469 470 (void)printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname); 471 if (sfsp->f_blocks > sfsp->f_bfree) 472 used = sfsp->f_blocks - sfsp->f_bfree; 473 else 474 used = 0; 475 availblks = sfsp->f_bavail + used; 476 if (hflag) { 477 prthuman(sfsp, used); 478 } else { 479 (void)printf(" %*jd %*jd %*jd", mwp->total, 480 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize, sfsp->f_mntonname), 481 mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize, sfsp->f_mntonname), 482 mwp->avail, fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize, sfsp->f_mntonname)); 483 } 484 if (COMPAT_MODE("bin/df", "unix2003")) { 485 /* Standard says percentage must be rounded UP to next 486 integer value, not truncated */ 487 double value; 488 if (availblks == 0) 489 value = 100.0; 490 else { 491 value = (double)used / (double)availblks * 100.0; 492 if ((value-(int)value) > 0.0) value = value + 1.0; 493 } 494 (void)printf(" %5.0f%%", trunc(value)); 495 } else { 496 (void)printf(" %5.0f%%", 497 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); 498 } 499 if (iflag) { 500 inodes = sfsp->f_files; 501 used = inodes - sfsp->f_ffree; 502 (void)printf(" %*llu %*lu %4.0f%% ", mwp->iused, used, 503 mwp->ifree, (unsigned long)sfsp->f_ffree, inodes == 0 ? 100.0 : 504 (double)used / (double)inodes * 100.0); 505 } else 506 (void)printf(" "); 507 (void)printf(" %s\n", sfsp->f_mntonname); 508} 509 510/* 511 * Update the maximum field-width information in `mwp' based on 512 * the filesystem specified by `sfsp'. 513 */ 514void 515update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp) 516{ 517 static long blocksize; 518 int dummy; 519 520 if (blocksize == 0) 521 getbsize(&dummy, &blocksize); 522 523 mwp->mntfrom = imax(mwp->mntfrom, (int)strlen(sfsp->f_mntfromname)); 524 mwp->total = imax(mwp->total, longwidth(fsbtoblk(sfsp->f_blocks, 525 sfsp->f_bsize, blocksize, sfsp->f_mntonname))); 526 if (sfsp->f_blocks >= sfsp->f_bfree) 527 mwp->used = imax(mwp->used, longwidth(fsbtoblk(sfsp->f_blocks - 528 sfsp->f_bfree, sfsp->f_bsize, blocksize, sfsp->f_mntonname))); 529 mwp->avail = imax(mwp->avail, longwidth(fsbtoblk(sfsp->f_bavail, 530 sfsp->f_bsize, blocksize, sfsp->f_mntonname))); 531 mwp->iused = imax(mwp->iused, longwidth((unsigned)(sfsp->f_files - sfsp->f_ffree))); 532 mwp->ifree = imax(mwp->ifree, longwidth((unsigned)(sfsp->f_ffree))); 533} 534 535/* Return the width in characters of the specified long. */ 536int 537longwidth(long long val) 538{ 539 int len; 540 541 len = 0; 542 /* Negative or zero values require one extra digit. */ 543 if (val <= 0) { 544 val = -val; 545 len++; 546 } 547 while (val > 0) { 548 len++; 549 val /= 10; 550 } 551 552 return (len); 553} 554 555void 556usage(void) 557{ 558 559 char *t_flag = COMPAT_MODE("bin/df", "unix2003") ? "[-t]" : "[-t type]"; 560 (void)fprintf(stderr, 561 "usage: df [-b | -H | -h | -k | -m | -g | -P] [-ailn] [-T type] %s [filesystem ...]\n", t_flag); 562 exit(EX_USAGE); 563} 564 565char * 566makenetvfslist(void) 567{ 568 char *str, *strptr, **listptr; 569#ifndef __APPLE__ 570 int mib[3], maxvfsconf, cnt=0, i; 571 size_t miblen; 572 struct ovfsconf *ptr; 573#else 574 int mib[4], maxvfsconf, cnt=0, i; 575 size_t miblen; 576 struct vfsconf vfc; 577#endif 578 579 mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM; 580 miblen=sizeof(maxvfsconf); 581 if (sysctl(mib, (unsigned int)(sizeof(mib) / sizeof(mib[0])), 582 &maxvfsconf, &miblen, NULL, 0)) { 583 warnx("sysctl failed"); 584 return (NULL); 585 } 586 587 if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) { 588 warnx("malloc failed"); 589 return (NULL); 590 } 591 592#ifndef __APPLE__ 593 for (ptr = getvfsent(); ptr; ptr = getvfsent()) 594 if (ptr->vfc_flags & VFCF_NETWORK) { 595 listptr[cnt++] = strdup(ptr->vfc_name); 596 if (listptr[cnt-1] == NULL) { 597 warnx("malloc failed"); 598 return (NULL); 599 } 600 } 601#else 602 miblen = sizeof (struct vfsconf); 603 mib[2] = VFS_CONF; 604 for (i = 0; i < maxvfsconf; i++) { 605 mib[3] = i; 606 if (sysctl(mib, 4, &vfc, &miblen, NULL, 0) == 0) { 607 if (!(vfc.vfc_flags & MNT_LOCAL)) { 608 listptr[cnt++] = strdup(vfc.vfc_name); 609 if (listptr[cnt-1] == NULL) { 610 warnx("malloc failed"); 611 return (NULL); 612 } 613 } 614 } 615 } 616#endif 617 618 if (cnt == 0 || 619 (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) { 620 if (cnt > 0) 621 warnx("malloc failed"); 622 free(listptr); 623 return (NULL); 624 } 625 626 *str = 'n'; *(str + 1) = 'o'; 627 for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) { 628 strncpy(strptr, listptr[i], 32); 629 strptr += strlen(listptr[i]); 630 *strptr = ','; 631 free(listptr[i]); 632 } 633 *(--strptr) = '\0'; 634 635 free(listptr); 636 return (str); 637} 638