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