1 /* $NetBSD: df.c,v 1.104 2024/10/06 17:03:28 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1990, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * (c) UNIX System Laboratories, Inc. 7 * All or some portions of this file are derived from material licensed 8 * to the University of California by American Telephone and Telegraph 9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 * the permission of UNIX System Laboratories, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 __COPYRIGHT( 40 "@(#) Copyright (c) 1980, 1990, 1993, 1994\ 41 The Regents of the University of California. All rights reserved."); 42 #endif /* not lint */ 43 44 #ifndef lint 45 #if 0 46 static char sccsid[] = "@(#)df.c 8.7 (Berkeley) 4/2/94"; 47 #else 48 __RCSID("$NetBSD: df.c,v 1.104 2024/10/06 17:03:28 christos Exp $"); 49 #endif 50 #endif /* not lint */ 51 52 #include <sys/param.h> 53 #include <sys/stat.h> 54 #include <sys/mount.h> 55 56 #include <assert.h> 57 #include <err.h> 58 #include <errno.h> 59 #include <stdbool.h> 60 #include <fcntl.h> 61 #include <locale.h> 62 #include <util.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <unistd.h> 67 #include <util.h> 68 69 static char *getmntpt(const char *); 70 static void addstat(struct statvfs *, const struct statvfs *); 71 static void prtstat(const struct statvfs *, int); 72 static int selected(const char *, size_t); 73 static void maketypelist(char *); 74 static size_t regetmntinfo(struct statvfs **, size_t); 75 __dead static void usage(void); 76 static void prthumanval(int64_t, int); 77 static void prthuman(const struct statvfs *, int64_t, int64_t); 78 79 static int aflag, cflag, fflag, gflag, hflag, iflag, lflag; 80 static int Mflag, Nflag, nflag, Pflag, qflag, Wflag; 81 static long usize; 82 static char **typelist; 83 static size_t mntcount; 84 85 #define WIDTH_INODE 10 86 #define WIDTH_BLKSIZE 12 87 static int blksize_width = WIDTH_BLKSIZE; 88 89 static int fudgeunits = 0; 90 91 #define streq(a, b) (strcmp((a), (b)) == 0) 92 #define warnq(args) do { if (!qflag) warnx args; } while (0) 93 94 int 95 main(int argc, char *argv[]) 96 { 97 struct stat stbuf; 98 struct statvfs *mntbuf, totals; 99 int ch, maxwidth, width; 100 size_t i; 101 char *mntpt; 102 103 setprogname(argv[0]); 104 (void)setlocale(LC_ALL, ""); 105 106 while ((ch = getopt(argc, argv, "abcfGgHhiklMmNnPqt:W")) != -1) 107 switch (ch) { 108 case 'a': 109 aflag = 1; 110 break; 111 case 'b': 112 hflag = 0; 113 usize = 512; 114 break; 115 case 'c': 116 cflag = 1; 117 break; 118 case 'f': 119 fflag = 1; 120 break; 121 case 'g': 122 hflag = 0; 123 usize = 1024 * 1024 * 1024; 124 break; 125 case 'G': 126 gflag = 1; 127 break; 128 case 'H': 129 fudgeunits = HN_DIVISOR_1000; 130 /* FALL THROUGH */ 131 case 'h': 132 hflag = 1; 133 usize = 0; 134 break; 135 case 'i': 136 iflag = 1; 137 break; 138 case 'k': 139 hflag = 0; 140 usize = 1024; 141 break; 142 case 'l': 143 lflag = 1; 144 break; 145 case 'M': 146 Mflag = 1; 147 break; 148 case 'm': 149 hflag = 0; 150 usize = 1024 * 1024; 151 break; 152 case 'N': 153 Nflag = 1; 154 break; 155 case 'n': 156 nflag = 1; 157 break; 158 case 'P': 159 Pflag = 1; 160 break; 161 case 'q': 162 qflag = 1; 163 break; 164 case 'W': 165 Wflag = 1; 166 break; 167 case 't': 168 if (typelist != NULL) 169 errx(EXIT_FAILURE, 170 "only one -t option may be specified."); 171 maketypelist(optarg); 172 break; 173 case '?': 174 default: 175 usage(); 176 } 177 178 if (fflag && (Pflag || gflag)) 179 errx(EXIT_FAILURE, 180 "only one of -f -G and -P may be specified"); 181 if (gflag && (Pflag || iflag)) 182 errx(EXIT_FAILURE, 183 "only one of -G and -P or -i may be specified"); 184 if (Pflag && iflag) 185 errx(EXIT_FAILURE, 186 "only one of -P and -i may be specified"); 187 if (fflag) 188 Nflag = 1; 189 190 #if 0 191 /* 192 * The block size cannot be checked until after getbsize() is called. 193 */ 194 if (Pflag && (hflag || (usize != 1024 && usize != 512))) 195 errx(EXIT_FAILURE, 196 "non-standard block size incompatible with -P"); 197 #endif 198 argc -= optind; 199 argv += optind; 200 201 mntcount = getmntinfo(&mntbuf, MNT_NOWAIT); 202 if (mntcount == 0) 203 err(EXIT_FAILURE, 204 "retrieving information on mounted file systems"); 205 206 if (*argv == NULL) { 207 mntcount = regetmntinfo(&mntbuf, mntcount); 208 } else { 209 if ((mntbuf = calloc(argc, sizeof(*mntbuf))) == NULL) 210 err(EXIT_FAILURE, "can't allocate statvfs array"); 211 mntcount = 0; 212 for (/*EMPTY*/; *argv != NULL; argv++) { 213 if (stat(*argv, &stbuf) < 0) { 214 if ((mntpt = getmntpt(*argv)) == 0) { 215 if (!qflag) 216 warn("%s", *argv); 217 continue; 218 } 219 } else if (S_ISBLK(stbuf.st_mode)) { 220 if ((mntpt = getmntpt(*argv)) == 0) 221 mntpt = *argv; 222 } else 223 mntpt = *argv; 224 225 /* 226 * Statfs does not take a `wait' flag, so we cannot 227 * implement nflag here. 228 */ 229 if (!statvfs(mntpt, &mntbuf[mntcount])) { 230 if (Mflag && 231 !streq(mntpt, mntbuf[mntcount].f_mntonname)) 232 warnq(("%s is not a mount point", 233 mntpt)); 234 else if (lflag && 235 (mntbuf[mntcount].f_flag & MNT_LOCAL) == 0) 236 warnq(("Warning: %s is not a local %s", 237 *argv, "file system")); 238 else if 239 (!selected(mntbuf[mntcount].f_fstypename, 240 sizeof(mntbuf[mntcount].f_fstypename))) 241 warnq(("Warning: %s mounted as a %s %s", 242 *argv, 243 mntbuf[mntcount].f_fstypename, 244 "file system")); 245 else 246 ++mntcount; 247 } else if (!qflag) 248 warn("%s", *argv); 249 } 250 } 251 252 if (cflag) { 253 memset(&totals, 0, sizeof(totals)); 254 totals.f_frsize = DEV_BSIZE; 255 strlcpy(totals.f_mntfromname, "total", 256 sizeof(totals.f_mntfromname)); 257 } 258 259 maxwidth = 0; 260 for (i = 0; i < mntcount; i++) { 261 width = 0; 262 if (Wflag && mntbuf[i].f_mntfromlabel[0]) { 263 /* +5 is for "NAME=" added later */ 264 width = (int)strlen(mntbuf[i].f_mntfromlabel) + 5; 265 } 266 if (width == 0) 267 width = (int)strlen(mntbuf[i].f_mntfromname); 268 if (width > maxwidth) 269 maxwidth = width; 270 if (cflag) 271 addstat(&totals, &mntbuf[i]); 272 } 273 274 if (cflag == 0 || fflag == 0) 275 for (i = 0; i < mntcount; i++) 276 prtstat(&mntbuf[i], maxwidth); 277 278 mntcount = fflag; 279 if (cflag) 280 prtstat(&totals, maxwidth); 281 282 return 0; 283 } 284 285 static char * 286 getmntpt(const char *name) 287 { 288 size_t count, i; 289 struct statvfs *mntbuf; 290 291 count = getmntinfo(&mntbuf, MNT_NOWAIT); 292 if (count == 0) 293 err(EXIT_FAILURE, "Can't get mount information"); 294 for (i = 0; i < count; i++) { 295 if (streq(mntbuf[i].f_mntfromname, name)) 296 return mntbuf[i].f_mntonname; 297 } 298 return 0; 299 } 300 301 static enum { IN_LIST, NOT_IN_LIST } which; 302 303 static int 304 selected(const char *type, size_t len) 305 { 306 char **av; 307 308 /* If no type specified, it's always selected. */ 309 if (typelist == NULL) 310 return 1; 311 for (av = typelist; *av != NULL; ++av) 312 if (!strncmp(type, *av, len)) 313 return which == IN_LIST ? 1 : 0; 314 return which == IN_LIST ? 0 : 1; 315 } 316 317 static void 318 maketypelist(char *fslist) 319 { 320 size_t i; 321 char *nextcp, **av; 322 323 if ((fslist == NULL) || (fslist[0] == '\0')) 324 errx(EXIT_FAILURE, "empty type list"); 325 326 /* 327 * XXX 328 * Note: the syntax is "noxxx,yyy" for no xxx's and 329 * no yyy's, not the more intuitive "noyyy,noyyy". 330 */ 331 if (fslist[0] == 'n' && fslist[1] == 'o') { 332 fslist += 2; 333 which = NOT_IN_LIST; 334 } else 335 which = IN_LIST; 336 337 /* Count the number of types. */ 338 for (i = 1, nextcp = fslist; 339 (nextcp = strchr(nextcp, ',')) != NULL; i++) 340 ++nextcp; 341 342 /* Build an array of that many types. */ 343 if ((av = typelist = calloc((i + 1), sizeof(*av))) == NULL) 344 err(EXIT_FAILURE, "can't allocate type array"); 345 av[0] = fslist; 346 for (i = 1, nextcp = fslist; 347 (nextcp = strchr(nextcp, ',')) != NULL; i++) { 348 *nextcp = '\0'; 349 av[i] = ++nextcp; 350 } 351 /* Terminate the array. */ 352 av[i] = NULL; 353 } 354 355 /* 356 * Make a pass over the filesystem info in ``mntbuf'' filtering out 357 * filesystem types not in ``fsmask'' and possibly re-stating to get 358 * current (not cached) info. Returns the new count of valid statvfs bufs. 359 */ 360 static size_t 361 regetmntinfo(struct statvfs **mntbufp, size_t count) 362 { 363 size_t i, j; 364 struct statvfs *mntbuf; 365 366 if (!lflag && typelist == NULL && aflag) 367 return nflag ? count : (size_t)getmntinfo(mntbufp, MNT_WAIT); 368 369 mntbuf = *mntbufp; 370 j = 0; 371 for (i = 0; i < count; i++) { 372 if (!aflag && (mntbuf[i].f_flag & MNT_IGNORE) != 0) 373 continue; 374 if (lflag && (mntbuf[i].f_flag & MNT_LOCAL) == 0) 375 continue; 376 if (!selected(mntbuf[i].f_fstypename, 377 sizeof(mntbuf[i].f_fstypename))) 378 continue; 379 if (nflag) 380 mntbuf[j] = mntbuf[i]; 381 else { 382 struct statvfs layerbuf = mntbuf[i]; 383 (void)statvfs(mntbuf[i].f_mntonname, &mntbuf[j]); 384 /* 385 * If the FS name changed, then new data is for 386 * a different layer and we don't want it. 387 */ 388 if (memcmp(layerbuf.f_mntfromname, 389 mntbuf[j].f_mntfromname, MNAMELEN)) 390 mntbuf[j] = layerbuf; 391 } 392 j++; 393 } 394 return j; 395 } 396 397 static void 398 prthumanval(int64_t bytes, int width) 399 { 400 char buf[6]; 401 402 (void)humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), 403 bytes, "", HN_AUTOSCALE, 404 HN_B | HN_NOSPACE | HN_DECIMAL | fudgeunits); 405 406 (void)printf("%*s", width, buf); 407 } 408 409 static void 410 prthuman(const struct statvfs *sfsp, int64_t used, int64_t bavail) 411 { 412 413 prthumanval((int64_t)(sfsp->f_blocks * sfsp->f_frsize), blksize_width); 414 prthumanval((int64_t)(used * sfsp->f_frsize), 1 + blksize_width); 415 prthumanval((int64_t)(bavail * sfsp->f_frsize), 1 + blksize_width); 416 } 417 418 /* 419 * Convert statvfs returned filesystem size into BLOCKSIZE units. 420 * Attempts to avoid overflow for large filesystems. 421 */ 422 #define fsbtoblk(num, fsbs, bs) \ 423 (((fsbs) != 0 && (uint64_t)(fsbs) < (uint64_t)(bs)) ? \ 424 (int64_t)(num) / (int64_t)((bs) / (fsbs)) : \ 425 (int64_t)(num) * (int64_t)((fsbs) / (bs))) 426 427 static void 428 addstat(struct statvfs *totalfsp, const struct statvfs *sfsp) 429 { 430 uint64_t frsize; 431 432 frsize = sfsp->f_frsize / totalfsp->f_frsize; 433 totalfsp->f_blocks += sfsp->f_blocks * frsize; 434 totalfsp->f_bfree += sfsp->f_bfree * frsize; 435 totalfsp->f_bavail += sfsp->f_bavail * frsize; 436 totalfsp->f_bresvd += sfsp->f_bresvd * frsize; 437 totalfsp->f_files += sfsp->f_files; 438 totalfsp->f_ffree += sfsp->f_ffree; 439 totalfsp->f_favail += sfsp->f_favail; 440 totalfsp->f_fresvd += sfsp->f_fresvd; 441 } 442 443 /* 444 * Print out status about a filesystem. 445 */ 446 static void 447 prtstat(const struct statvfs *sfsp, int maxwidth) 448 { 449 static long blocksize; 450 static int headerlen, timesthrough; 451 static const char *header; 452 static const char full[] = "100"; 453 static const char empty[] = " 0"; 454 int64_t used, availblks, inodes; 455 int64_t bavail; 456 char pb[64]; 457 char mntfromname[sizeof(sfsp->f_mntfromname) + 10]; 458 459 if (Wflag && sfsp->f_mntfromlabel[0]) { 460 snprintf(mntfromname, sizeof(mntfromname), "NAME=%s", 461 sfsp->f_mntfromlabel); 462 } else { 463 strlcpy(mntfromname, sfsp->f_mntfromname, sizeof(mntfromname)); 464 } 465 466 if (gflag) { 467 /* 468 * From SunOS-5.6: 469 * 470 * /var (/dev/dsk/c0t0d0s3 ): 8192 block size 1024 frag size 471 * 984242 total blocks 860692 free blocks 859708 available 249984 total files 472 * 248691 free files 8388611 filesys id 473 * ufs fstype 0x00000004 flag 255 filename length 474 * 475 */ 476 (void)printf("%10s (%-12s): %7ld block size %12ld frag size\n", 477 sfsp->f_mntonname, mntfromname, 478 sfsp->f_bsize, /* On UFS/FFS systems this is 479 * also called the "optimal 480 * transfer block size" but it 481 * is of course the file 482 * system's block size too. 483 */ 484 sfsp->f_frsize); /* not so surprisingly the 485 * "fundamental file system 486 * block size" is the frag 487 * size. 488 */ 489 (void)printf("%10" PRId64 " total blocks %10" PRId64 490 " free blocks %10" PRId64 " available\n", 491 (uint64_t)sfsp->f_blocks, (uint64_t)sfsp->f_bfree, 492 (uint64_t)sfsp->f_bavail); 493 (void)printf("%10" PRId64 " total files %10" PRId64 494 " free files %12lx filesys id\n", 495 (uint64_t)sfsp->f_files, (uint64_t)sfsp->f_ffree, 496 sfsp->f_fsid); 497 (void)printf("%10s fstype %#15lx flag %17ld filename " 498 "length\n", sfsp->f_fstypename, sfsp->f_flag, 499 sfsp->f_namemax); 500 (void)printf("%10lu owner %17" PRId64 " syncwrites %12" PRId64 501 " asyncwrites\n\n", (unsigned long)sfsp->f_owner, 502 sfsp->f_syncwrites, sfsp->f_asyncwrites); 503 504 /* 505 * a concession by the structured programming police to the 506 * indentation police.... 507 */ 508 return; 509 } 510 if (maxwidth < 12) 511 maxwidth = 12; 512 if (++timesthrough == 1) { 513 switch (blocksize = usize) { 514 case 512: 515 header = "512-blocks"; 516 headerlen = (int)strlen(header); 517 break; 518 case 1024: 519 header = Pflag ? "1024-blocks" : "1K-blocks"; 520 headerlen = (int)strlen(header); 521 break; 522 case 1024 * 1024: 523 header = "1M-blocks"; 524 headerlen = (int)strlen(header); 525 break; 526 case 1024 * 1024 * 1024: 527 header = "1G-blocks"; 528 headerlen = (int)strlen(header); 529 break; 530 default: 531 if (hflag) { 532 header = "Size"; 533 headerlen = (int)strlen(header); 534 blksize_width = 6; 535 } else 536 header = getbsize(&headerlen, &blocksize); 537 break; 538 } 539 540 if (blocksize >= 1024 * 1024) 541 blksize_width -= 3; 542 if (blocksize >= 1024 * 1024 * 1024) 543 blksize_width -= 3; 544 if (blksize_width < headerlen) 545 blksize_width = headerlen; 546 547 if (Pflag) { 548 /* 549 * either: 550 * "Filesystem 1024-blocks Used Available Capacity Mounted on\n" 551 * or: 552 * "Filesystem 512-blocks Used Available Capacity Mounted on\n" 553 */ 554 if (blocksize != 1024 && blocksize != 512) 555 errx(EXIT_FAILURE, 556 "non-standard block size incompatible with -P"); 557 (void)printf("Filesystem %s Used Available Capacity " 558 "Mounted on\n", header); 559 } else if (!Nflag) { 560 (void)printf("%-*.*s %*s %*s %*s %%Cap", 561 maxwidth, maxwidth, "Filesystem", 562 blksize_width, header, 563 blksize_width, "Used", 564 blksize_width, "Avail"); 565 if (iflag) { 566 (void)printf(" %*s %*s %%iCap", 567 WIDTH_INODE, "iUsed", 568 WIDTH_INODE, "iAvail"); 569 } 570 (void)printf(" Mounted on\n"); 571 } 572 } 573 used = sfsp->f_blocks - sfsp->f_bfree; 574 bavail = sfsp->f_bfree - sfsp->f_bresvd; 575 availblks = bavail + used; 576 if (Pflag) { 577 assert(hflag == 0); 578 assert(blocksize > 0); 579 /* 580 * "%s %d %d %d %s %s\n", <file system name>, <total space>, 581 * <space used>, <space free>, <percentage used>, 582 * <file system root> 583 */ 584 (void)printf("%s %" PRId64 " %" PRId64 " %" PRId64 " %s%% %s\n", 585 mntfromname, 586 fsbtoblk(sfsp->f_blocks, sfsp->f_frsize, blocksize), 587 fsbtoblk(used, sfsp->f_frsize, blocksize), 588 fsbtoblk(bavail, sfsp->f_frsize, blocksize), 589 availblks == 0 ? full : strspct(pb, sizeof(pb), used, 590 availblks, 0), sfsp->f_mntonname); 591 /* 592 * another concession by the structured programming police to 593 * the indentation police.... 594 * 595 * Note iflag cannot be set when Pflag is set. 596 */ 597 return; 598 } 599 600 if (fflag) { 601 if (iflag) 602 (void)printf("%jd", sfsp->f_ffree); 603 else if (hflag) 604 prthumanval(bavail * sfsp->f_frsize, 1); 605 else 606 (void)printf("%jd", fsbtoblk(bavail, 607 sfsp->f_frsize, blocksize)); 608 609 if (mntcount != 1) 610 (void)printf(" %s\n", sfsp->f_mntonname); 611 else 612 (void)printf("\n"); 613 return; 614 } 615 616 (void)printf("%-*.*s ", maxwidth, maxwidth, mntfromname); 617 618 if (hflag) 619 prthuman(sfsp, used, bavail); 620 else 621 (void)printf("%*" PRId64 " %*" PRId64 " %*" PRId64, 622 blksize_width, 623 fsbtoblk(sfsp->f_blocks, sfsp->f_frsize, blocksize), 624 blksize_width, fsbtoblk(used, sfsp->f_frsize, blocksize), 625 blksize_width, fsbtoblk(bavail, sfsp->f_frsize, blocksize)); 626 (void)printf(" %3s%%", 627 availblks == 0 ? full : 628 strspct(pb, sizeof(pb), used, availblks, 0)); 629 if (iflag) { 630 inodes = sfsp->f_files; 631 used = inodes - sfsp->f_ffree; 632 (void)printf(" %*jd %*jd %4s%%", 633 WIDTH_INODE, (intmax_t)used, 634 WIDTH_INODE, (intmax_t)sfsp->f_ffree, 635 inodes == 0 ? (used == 0 ? empty : full) : 636 strspct(pb, sizeof(pb), used, inodes, 0)); 637 } 638 (void)printf(" %s\n", sfsp->f_mntonname); 639 } 640 641 static void 642 usage(void) 643 { 644 645 (void)fprintf(stderr, 646 "Usage: %s [-aclMnqW] [-G|-bkP|-bfgHhikmN] [-t type] [file | " 647 "file_system]...\n", 648 getprogname()); 649 exit(1); 650 /* NOTREACHED */ 651 } 652