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