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