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