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