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