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