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