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 <libutil.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 /* Maximum widths of various fields. */ 69 struct maxwidths { 70 int mntfrom; 71 int fstype; 72 int total; 73 int used; 74 int avail; 75 int iused; 76 int ifree; 77 }; 78 79 static void addstat(struct statfs *, struct statfs *); 80 static char *getmntpt(const char *); 81 static int int64width(int64_t); 82 static char *makenetvfslist(void); 83 static void prthuman(const struct statfs *, int64_t); 84 static void prthumanval(int64_t); 85 static intmax_t fsbtoblk(int64_t, uint64_t, u_long); 86 static void prtstat(struct statfs *, struct maxwidths *); 87 static size_t regetmntinfo(struct statfs **, long, const char **); 88 static void update_maxwidths(struct maxwidths *, const struct statfs *); 89 static void usage(void); 90 91 static __inline int 92 imax(int a, int b) 93 { 94 return (a > b ? a : b); 95 } 96 97 static int aflag = 0, cflag, hflag, iflag, kflag, lflag = 0, nflag, Tflag; 98 static struct ufs_args mdev; 99 100 int 101 main(int argc, char *argv[]) 102 { 103 struct stat stbuf; 104 struct statfs statfsbuf, totalbuf; 105 struct maxwidths maxwidths; 106 struct statfs *mntbuf; 107 const char *fstype; 108 char *mntpath, *mntpt; 109 const char **vfslist; 110 size_t i, mntsize; 111 int ch, rv; 112 113 fstype = "ufs"; 114 115 memset(&totalbuf, 0, sizeof(totalbuf)); 116 totalbuf.f_bsize = DEV_BSIZE; 117 strlcpy(totalbuf.f_mntfromname, "total", MNAMELEN); 118 vfslist = NULL; 119 while ((ch = getopt(argc, argv, "abcgHhiklmnPt:T")) != -1) 120 switch (ch) { 121 case 'a': 122 aflag = 1; 123 break; 124 case 'b': 125 /* FALLTHROUGH */ 126 case 'P': 127 /* 128 * POSIX specifically discusses the the behavior of 129 * both -k and -P. It states that the blocksize should 130 * be set to 1024. Thus, if this occurs, simply break 131 * rather than clobbering the old blocksize. 132 */ 133 if (kflag) 134 break; 135 setenv("BLOCKSIZE", "512", 1); 136 hflag = 0; 137 break; 138 case 'c': 139 cflag = 1; 140 break; 141 case 'g': 142 setenv("BLOCKSIZE", "1g", 1); 143 hflag = 0; 144 break; 145 case 'H': 146 hflag = UNITS_SI; 147 break; 148 case 'h': 149 hflag = UNITS_2; 150 break; 151 case 'i': 152 iflag = 1; 153 break; 154 case 'k': 155 kflag++; 156 setenv("BLOCKSIZE", "1024", 1); 157 hflag = 0; 158 break; 159 case 'l': 160 if (vfslist != NULL) 161 errx(1, "-l and -t are mutually exclusive."); 162 vfslist = makevfslist(makenetvfslist()); 163 lflag = 1; 164 break; 165 case 'm': 166 setenv("BLOCKSIZE", "1m", 1); 167 hflag = 0; 168 break; 169 case 'n': 170 nflag = 1; 171 break; 172 case 't': 173 if (lflag) 174 errx(1, "-l and -t are mutually exclusive."); 175 if (vfslist != NULL) 176 errx(1, "only one -t option may be specified"); 177 fstype = optarg; 178 vfslist = makevfslist(optarg); 179 break; 180 case 'T': 181 Tflag = 1; 182 break; 183 case '?': 184 default: 185 usage(); 186 } 187 argc -= optind; 188 argv += optind; 189 190 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 191 bzero(&maxwidths, sizeof(maxwidths)); 192 for (i = 0; i < mntsize; i++) 193 update_maxwidths(&maxwidths, &mntbuf[i]); 194 195 rv = 0; 196 if (!*argv) { 197 mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); 198 bzero(&maxwidths, sizeof(maxwidths)); 199 for (i = 0; i < mntsize; i++) { 200 if (cflag) 201 addstat(&totalbuf, &mntbuf[i]); 202 update_maxwidths(&maxwidths, &mntbuf[i]); 203 } 204 if (cflag) 205 update_maxwidths(&maxwidths, &totalbuf); 206 for (i = 0; i < mntsize; i++) 207 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) 208 prtstat(&mntbuf[i], &maxwidths); 209 if (cflag) 210 prtstat(&totalbuf, &maxwidths); 211 exit(rv); 212 } 213 214 for (; *argv; argv++) { 215 if (stat(*argv, &stbuf) < 0) { 216 if ((mntpt = getmntpt(*argv)) == 0) { 217 warn("%s", *argv); 218 rv = 1; 219 continue; 220 } 221 } else if (S_ISCHR(stbuf.st_mode)) { 222 if ((mntpt = getmntpt(*argv)) == 0) { 223 mdev.fspec = *argv; 224 mntpath = strdup("/tmp/df.XXXXXX"); 225 if (mntpath == NULL) { 226 warn("strdup failed"); 227 rv = 1; 228 continue; 229 } 230 mntpt = mkdtemp(mntpath); 231 if (mntpt == NULL) { 232 warn("mkdtemp(\"%s\") failed", mntpath); 233 rv = 1; 234 free(mntpath); 235 continue; 236 } 237 if (mount(fstype, mntpt, MNT_RDONLY, 238 &mdev) != 0) { 239 warn("%s", *argv); 240 rv = 1; 241 (void)rmdir(mntpt); 242 free(mntpath); 243 continue; 244 } else if (statfs(mntpt, &statfsbuf) == 0) { 245 statfsbuf.f_mntonname[0] = '\0'; 246 prtstat(&statfsbuf, &maxwidths); 247 if (cflag) 248 addstat(&totalbuf, &statfsbuf); 249 } else { 250 warn("%s", *argv); 251 rv = 1; 252 } 253 (void)unmount(mntpt, 0); 254 (void)rmdir(mntpt); 255 free(mntpath); 256 continue; 257 } 258 } else 259 mntpt = *argv; 260 261 /* 262 * Statfs does not take a `wait' flag, so we cannot 263 * implement nflag here. 264 */ 265 if (statfs(mntpt, &statfsbuf) < 0) { 266 warn("%s", mntpt); 267 rv = 1; 268 continue; 269 } 270 271 /* 272 * Check to make sure the arguments we've been given are 273 * satisfied. Return an error if we have been asked to 274 * list a mount point that does not match the other args 275 * we've been given (-l, -t, etc.). 276 */ 277 if (checkvfsname(statfsbuf.f_fstypename, vfslist)) { 278 rv = 1; 279 continue; 280 } 281 282 if (argc == 1) { 283 bzero(&maxwidths, sizeof(maxwidths)); 284 update_maxwidths(&maxwidths, &statfsbuf); 285 } 286 prtstat(&statfsbuf, &maxwidths); 287 if (cflag) 288 addstat(&totalbuf, &statfsbuf); 289 } 290 if (cflag) 291 prtstat(&totalbuf, &maxwidths); 292 return (rv); 293 } 294 295 static char * 296 getmntpt(const char *name) 297 { 298 size_t mntsize, i; 299 struct statfs *mntbuf; 300 301 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 302 for (i = 0; i < mntsize; i++) { 303 if (!strcmp(mntbuf[i].f_mntfromname, name)) 304 return (mntbuf[i].f_mntonname); 305 } 306 return (0); 307 } 308 309 /* 310 * Make a pass over the file system info in ``mntbuf'' filtering out 311 * file system types not in vfslist and possibly re-stating to get 312 * current (not cached) info. Returns the new count of valid statfs bufs. 313 */ 314 static size_t 315 regetmntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist) 316 { 317 int error, i, j; 318 struct statfs *mntbuf; 319 320 if (vfslist == NULL) 321 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 322 323 mntbuf = *mntbufp; 324 for (j = 0, i = 0; i < mntsize; i++) { 325 if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 326 continue; 327 /* 328 * XXX statfs(2) can fail for various reasons. It may be 329 * possible that the user does not have access to the 330 * pathname, if this happens, we will fall back on 331 * "stale" filesystem statistics. 332 */ 333 error = statfs(mntbuf[i].f_mntonname, &mntbuf[j]); 334 if (nflag || error < 0) 335 if (i != j) { 336 if (error < 0) 337 warnx("%s stats possibly stale", 338 mntbuf[i].f_mntonname); 339 mntbuf[j] = mntbuf[i]; 340 } 341 j++; 342 } 343 return (j); 344 } 345 346 static void 347 prthuman(const struct statfs *sfsp, int64_t used) 348 { 349 350 prthumanval(sfsp->f_blocks * sfsp->f_bsize); 351 prthumanval(used * sfsp->f_bsize); 352 prthumanval(sfsp->f_bavail * sfsp->f_bsize); 353 } 354 355 static void 356 prthumanval(int64_t bytes) 357 { 358 char buf[6]; 359 int flags; 360 361 flags = HN_B | HN_NOSPACE | HN_DECIMAL; 362 if (hflag == UNITS_SI) 363 flags |= HN_DIVISOR_1000; 364 365 humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), 366 bytes, "", HN_AUTOSCALE, flags); 367 368 (void)printf(" %6s", buf); 369 } 370 371 /* 372 * Print an inode count in "human-readable" format. 373 */ 374 static void 375 prthumanvalinode(int64_t bytes) 376 { 377 char buf[6]; 378 int flags; 379 380 flags = HN_NOSPACE | HN_DECIMAL | HN_DIVISOR_1000; 381 382 humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), 383 bytes, "", HN_AUTOSCALE, flags); 384 385 (void)printf(" %5s", buf); 386 } 387 388 /* 389 * Convert statfs returned file system size into BLOCKSIZE units. 390 * Attempts to avoid overflow for large file systems. 391 */ 392 static intmax_t 393 fsbtoblk(int64_t num, uint64_t fsbs, u_long bs) 394 { 395 396 if (fsbs != 0 && fsbs < bs) 397 return (num / (intmax_t)(bs / fsbs)); 398 else 399 return (num * (intmax_t)(fsbs / bs)); 400 } 401 402 /* 403 * Print out status about a file system. 404 */ 405 static void 406 prtstat(struct statfs *sfsp, struct maxwidths *mwp) 407 { 408 static long blocksize; 409 static int headerlen, timesthrough = 0; 410 static const char *header; 411 int64_t used, availblks, inodes; 412 413 if (++timesthrough == 1) { 414 mwp->mntfrom = imax(mwp->mntfrom, (int)strlen("Filesystem")); 415 mwp->fstype = imax(mwp->fstype, (int)strlen("Type")); 416 if (hflag) { 417 header = " Size"; 418 mwp->total = mwp->used = mwp->avail = 419 (int)strlen(header); 420 } else { 421 header = getbsize(&headerlen, &blocksize); 422 mwp->total = imax(mwp->total, headerlen); 423 } 424 mwp->used = imax(mwp->used, (int)strlen("Used")); 425 mwp->avail = imax(mwp->avail, (int)strlen("Avail")); 426 427 (void)printf("%-*s", mwp->mntfrom, "Filesystem"); 428 if (Tflag) 429 (void)printf(" %-*s", mwp->fstype, "Type"); 430 (void)printf(" %-*s %*s %*s Capacity", mwp->total, header, 431 mwp->used, "Used", mwp->avail, "Avail"); 432 if (iflag) { 433 mwp->iused = imax(hflag ? 0 : mwp->iused, 434 (int)strlen(" iused")); 435 mwp->ifree = imax(hflag ? 0 : mwp->ifree, 436 (int)strlen("ifree")); 437 (void)printf(" %*s %*s %%iused", 438 mwp->iused - 2, "iused", mwp->ifree, "ifree"); 439 } 440 (void)printf(" Mounted on\n"); 441 } 442 (void)printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname); 443 if (Tflag) 444 (void)printf(" %-*s", mwp->fstype, sfsp->f_fstypename); 445 used = sfsp->f_blocks - sfsp->f_bfree; 446 availblks = sfsp->f_bavail + used; 447 if (hflag) { 448 prthuman(sfsp, used); 449 } else { 450 (void)printf(" %*jd %*jd %*jd", 451 mwp->total, fsbtoblk(sfsp->f_blocks, 452 sfsp->f_bsize, blocksize), 453 mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize), 454 mwp->avail, fsbtoblk(sfsp->f_bavail, 455 sfsp->f_bsize, blocksize)); 456 } 457 (void)printf(" %5.0f%%", 458 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); 459 if (iflag) { 460 inodes = sfsp->f_files; 461 used = inodes - sfsp->f_ffree; 462 if (hflag) { 463 (void)printf(" "); 464 prthumanvalinode(used); 465 prthumanvalinode(sfsp->f_ffree); 466 } else { 467 (void)printf(" %*jd %*jd", mwp->iused, (intmax_t)used, 468 mwp->ifree, (intmax_t)sfsp->f_ffree); 469 } 470 (void)printf(" %4.0f%% ", inodes == 0 ? 100.0 : 471 (double)used / (double)inodes * 100.0); 472 } else 473 (void)printf(" "); 474 if (strncmp(sfsp->f_mntfromname, "total", MNAMELEN) != 0) 475 (void)printf(" %s", sfsp->f_mntonname); 476 (void)printf("\n"); 477 } 478 479 static void 480 addstat(struct statfs *totalfsp, struct statfs *statfsp) 481 { 482 uint64_t bsize; 483 484 bsize = statfsp->f_bsize / totalfsp->f_bsize; 485 totalfsp->f_blocks += statfsp->f_blocks * bsize; 486 totalfsp->f_bfree += statfsp->f_bfree * bsize; 487 totalfsp->f_bavail += statfsp->f_bavail * bsize; 488 totalfsp->f_files += statfsp->f_files; 489 totalfsp->f_ffree += statfsp->f_ffree; 490 } 491 492 /* 493 * Update the maximum field-width information in `mwp' based on 494 * the file system specified by `sfsp'. 495 */ 496 static void 497 update_maxwidths(struct maxwidths *mwp, const struct statfs *sfsp) 498 { 499 static long blocksize = 0; 500 int dummy; 501 502 if (blocksize == 0) 503 getbsize(&dummy, &blocksize); 504 505 mwp->mntfrom = imax(mwp->mntfrom, (int)strlen(sfsp->f_mntfromname)); 506 mwp->fstype = imax(mwp->fstype, (int)strlen(sfsp->f_fstypename)); 507 mwp->total = imax(mwp->total, int64width( 508 fsbtoblk((int64_t)sfsp->f_blocks, sfsp->f_bsize, blocksize))); 509 mwp->used = imax(mwp->used, 510 int64width(fsbtoblk((int64_t)sfsp->f_blocks - 511 (int64_t)sfsp->f_bfree, sfsp->f_bsize, blocksize))); 512 mwp->avail = imax(mwp->avail, int64width(fsbtoblk(sfsp->f_bavail, 513 sfsp->f_bsize, blocksize))); 514 mwp->iused = imax(mwp->iused, int64width((int64_t)sfsp->f_files - 515 sfsp->f_ffree)); 516 mwp->ifree = imax(mwp->ifree, int64width(sfsp->f_ffree)); 517 } 518 519 /* Return the width in characters of the specified value. */ 520 static int 521 int64width(int64_t val) 522 { 523 int len; 524 525 len = 0; 526 /* Negative or zero values require one extra digit. */ 527 if (val <= 0) { 528 val = -val; 529 len++; 530 } 531 while (val > 0) { 532 len++; 533 val /= 10; 534 } 535 536 return (len); 537 } 538 539 static void 540 usage(void) 541 { 542 543 (void)fprintf(stderr, 544 "usage: df [-b | -g | -H | -h | -k | -m | -P] [-acilnT] [-t type] [file | filesystem ...]\n"); 545 exit(EX_USAGE); 546 } 547 548 static char * 549 makenetvfslist(void) 550 { 551 char *str, *strptr, **listptr; 552 struct xvfsconf *xvfsp, *keep_xvfsp; 553 size_t buflen; 554 int cnt, i, maxvfsconf; 555 556 if (sysctlbyname("vfs.conflist", NULL, &buflen, NULL, 0) < 0) { 557 warn("sysctl(vfs.conflist)"); 558 return (NULL); 559 } 560 xvfsp = malloc(buflen); 561 if (xvfsp == NULL) { 562 warnx("malloc failed"); 563 return (NULL); 564 } 565 keep_xvfsp = xvfsp; 566 if (sysctlbyname("vfs.conflist", xvfsp, &buflen, NULL, 0) < 0) { 567 warn("sysctl(vfs.conflist)"); 568 free(keep_xvfsp); 569 return (NULL); 570 } 571 maxvfsconf = buflen / sizeof(struct xvfsconf); 572 573 if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) { 574 warnx("malloc failed"); 575 free(keep_xvfsp); 576 return (NULL); 577 } 578 579 for (cnt = 0, i = 0; i < maxvfsconf; i++) { 580 if (xvfsp->vfc_flags & VFCF_NETWORK) { 581 listptr[cnt++] = strdup(xvfsp->vfc_name); 582 if (listptr[cnt-1] == NULL) { 583 warnx("malloc failed"); 584 free(listptr); 585 free(keep_xvfsp); 586 return (NULL); 587 } 588 } 589 xvfsp++; 590 } 591 592 if (cnt == 0 || 593 (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) { 594 if (cnt > 0) 595 warnx("malloc failed"); 596 free(listptr); 597 free(keep_xvfsp); 598 return (NULL); 599 } 600 601 *str = 'n'; *(str + 1) = 'o'; 602 for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) { 603 strlcpy(strptr, listptr[i], 32); 604 strptr += strlen(listptr[i]); 605 *strptr = ','; 606 free(listptr[i]); 607 } 608 *(--strptr) = '\0'; 609 610 free(keep_xvfsp); 611 free(listptr); 612 return (str); 613 } 614