1 /* $NetBSD: df.c,v 1.95 2019/09/22 22:59:37 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.95 2019/09/22 22:59:37 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 prtstat(const struct statvfs *, int); 71 static int selected(const char *, size_t); 72 static void maketypelist(char *); 73 static size_t regetmntinfo(struct statvfs **, size_t); 74 __dead static void usage(void); 75 static void prthumanval(int64_t, const char *); 76 static void prthuman(const struct statvfs *, int64_t, int64_t); 77 78 static int aflag, gflag, hflag, iflag, lflag, nflag, Pflag, Wflag; 79 static long usize; 80 static char **typelist; 81 82 int 83 main(int argc, char *argv[]) 84 { 85 struct stat stbuf; 86 struct statvfs *mntbuf; 87 int ch, maxwidth, width; 88 size_t i, mntcount; 89 char *mntpt; 90 91 setprogname(argv[0]); 92 (void)setlocale(LC_ALL, ""); 93 94 while ((ch = getopt(argc, argv, "aGghiklmnPt:W")) != -1) 95 switch (ch) { 96 case 'a': 97 aflag = 1; 98 break; 99 case 'g': 100 hflag = 0; 101 usize = 1024 * 1024 * 1024; 102 break; 103 case 'G': 104 gflag = 1; 105 break; 106 case 'h': 107 hflag = 1; 108 usize = 0; 109 break; 110 case 'i': 111 iflag = 1; 112 break; 113 case 'k': 114 hflag = 0; 115 usize = 1024; 116 break; 117 case 'l': 118 lflag = 1; 119 break; 120 case 'm': 121 hflag = 0; 122 usize = 1024 * 1024; 123 break; 124 case 'n': 125 nflag = 1; 126 break; 127 case 'P': 128 Pflag = 1; 129 break; 130 case 'W': 131 Wflag = 1; 132 break; 133 case 't': 134 if (typelist != NULL) 135 errx(EXIT_FAILURE, 136 "only one -t option may be specified."); 137 maketypelist(optarg); 138 break; 139 case '?': 140 default: 141 usage(); 142 } 143 144 if (gflag && (Pflag || iflag)) 145 errx(EXIT_FAILURE, 146 "only one of -G and -P or -i may be specified"); 147 if (Pflag && iflag) 148 errx(EXIT_FAILURE, 149 "only one of -P and -i may be specified"); 150 #if 0 151 /* 152 * The block size cannot be checked until after getbsize() is called. 153 */ 154 if (Pflag && (hflag || (usize != 1024 && usize != 512))) 155 errx(EXIT_FAILURE, 156 "non-standard block size incompatible with -P"); 157 #endif 158 argc -= optind; 159 argv += optind; 160 161 mntcount = getmntinfo(&mntbuf, MNT_NOWAIT); 162 if (mntcount == 0) 163 err(EXIT_FAILURE, 164 "retrieving information on mounted file systems"); 165 166 if (*argv == NULL) { 167 mntcount = regetmntinfo(&mntbuf, mntcount); 168 } else { 169 if ((mntbuf = calloc(argc, sizeof(*mntbuf))) == NULL) 170 err(EXIT_FAILURE, "can't allocate statvfs array"); 171 mntcount = 0; 172 for (/*EMPTY*/; *argv != NULL; argv++) { 173 if (stat(*argv, &stbuf) < 0) { 174 if ((mntpt = getmntpt(*argv)) == 0) { 175 warn("%s", *argv); 176 continue; 177 } 178 } else if (S_ISBLK(stbuf.st_mode)) { 179 if ((mntpt = getmntpt(*argv)) == 0) 180 mntpt = *argv; 181 } else 182 mntpt = *argv; 183 /* 184 * Statfs does not take a `wait' flag, so we cannot 185 * implement nflag here. 186 */ 187 if (!statvfs(mntpt, &mntbuf[mntcount])) 188 if (lflag && 189 (mntbuf[mntcount].f_flag & MNT_LOCAL) == 0) 190 warnx("Warning: %s is not a local %s", 191 *argv, "file system"); 192 else if 193 (!selected(mntbuf[mntcount].f_fstypename, 194 sizeof(mntbuf[mntcount].f_fstypename))) 195 warnx("Warning: %s mounted as a %s %s", 196 *argv, 197 mntbuf[mntcount].f_fstypename, 198 "file system"); 199 else 200 ++mntcount; 201 else 202 warn("%s", *argv); 203 } 204 } 205 206 maxwidth = 0; 207 for (i = 0; i < mntcount; i++) { 208 width = (int)strlen(Wflag && mntbuf[i].f_mntfromlabel[0] ? 209 mntbuf[i].f_mntfromlabel : mntbuf[i].f_mntfromname); 210 if (width > maxwidth) 211 maxwidth = width; 212 } 213 for (i = 0; i < mntcount; i++) 214 prtstat(&mntbuf[i], maxwidth); 215 return 0; 216 } 217 218 static char * 219 getmntpt(const char *name) 220 { 221 size_t mntcount, i; 222 struct statvfs *mntbuf; 223 224 mntcount = getmntinfo(&mntbuf, MNT_NOWAIT); 225 if (mntcount == 0) 226 err(EXIT_FAILURE, "Can't get mount information"); 227 for (i = 0; i < mntcount; i++) { 228 if (!strcmp(mntbuf[i].f_mntfromname, name)) 229 return mntbuf[i].f_mntonname; 230 } 231 return 0; 232 } 233 234 static enum { IN_LIST, NOT_IN_LIST } which; 235 236 static int 237 selected(const char *type, size_t len) 238 { 239 char **av; 240 241 /* If no type specified, it's always selected. */ 242 if (typelist == NULL) 243 return 1; 244 for (av = typelist; *av != NULL; ++av) 245 if (!strncmp(type, *av, len)) 246 return which == IN_LIST ? 1 : 0; 247 return which == IN_LIST ? 0 : 1; 248 } 249 250 static void 251 maketypelist(char *fslist) 252 { 253 size_t i; 254 char *nextcp, **av; 255 256 if ((fslist == NULL) || (fslist[0] == '\0')) 257 errx(EXIT_FAILURE, "empty type list"); 258 259 /* 260 * XXX 261 * Note: the syntax is "noxxx,yyy" for no xxx's and 262 * no yyy's, not the more intuitive "noyyy,noyyy". 263 */ 264 if (fslist[0] == 'n' && fslist[1] == 'o') { 265 fslist += 2; 266 which = NOT_IN_LIST; 267 } else 268 which = IN_LIST; 269 270 /* Count the number of types. */ 271 for (i = 1, nextcp = fslist; 272 (nextcp = strchr(nextcp, ',')) != NULL; i++) 273 ++nextcp; 274 275 /* Build an array of that many types. */ 276 if ((av = typelist = calloc((i + 1), sizeof(*av))) == NULL) 277 err(EXIT_FAILURE, "can't allocate type array"); 278 av[0] = fslist; 279 for (i = 1, nextcp = fslist; 280 (nextcp = strchr(nextcp, ',')) != NULL; i++) { 281 *nextcp = '\0'; 282 av[i] = ++nextcp; 283 } 284 /* Terminate the array. */ 285 av[i] = NULL; 286 } 287 288 /* 289 * Make a pass over the filesystem info in ``mntbuf'' filtering out 290 * filesystem types not in ``fsmask'' and possibly re-stating to get 291 * current (not cached) info. Returns the new count of valid statvfs bufs. 292 */ 293 static size_t 294 regetmntinfo(struct statvfs **mntbufp, size_t mntcount) 295 { 296 size_t i, j; 297 struct statvfs *mntbuf; 298 299 if (!lflag && typelist == NULL && aflag) 300 return nflag ? mntcount : (size_t)getmntinfo(mntbufp, MNT_WAIT); 301 302 mntbuf = *mntbufp; 303 j = 0; 304 for (i = 0; i < mntcount; i++) { 305 if (!aflag && (mntbuf[i].f_flag & MNT_IGNORE) != 0) 306 continue; 307 if (lflag && (mntbuf[i].f_flag & MNT_LOCAL) == 0) 308 continue; 309 if (!selected(mntbuf[i].f_fstypename, 310 sizeof(mntbuf[i].f_fstypename))) 311 continue; 312 if (nflag) 313 mntbuf[j] = mntbuf[i]; 314 else { 315 struct statvfs layerbuf = mntbuf[i]; 316 (void)statvfs(mntbuf[i].f_mntonname, &mntbuf[j]); 317 /* 318 * If the FS name changed, then new data is for 319 * a different layer and we don't want it. 320 */ 321 if (memcmp(layerbuf.f_mntfromname, 322 mntbuf[j].f_mntfromname, MNAMELEN)) 323 mntbuf[j] = layerbuf; 324 } 325 j++; 326 } 327 return j; 328 } 329 330 static void 331 prthumanval(int64_t bytes, const char *pad) 332 { 333 char buf[6]; 334 335 (void)humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), 336 bytes, "", HN_AUTOSCALE, 337 HN_B | HN_NOSPACE | HN_DECIMAL); 338 339 (void)printf("%s %6s", pad, buf); 340 } 341 342 static void 343 prthuman(const struct statvfs *sfsp, int64_t used, int64_t bavail) 344 { 345 346 prthumanval((int64_t)(sfsp->f_blocks * sfsp->f_frsize), " "); 347 prthumanval((int64_t)(used * sfsp->f_frsize), " "); 348 prthumanval((int64_t)(bavail * sfsp->f_frsize), " "); 349 } 350 351 /* 352 * Convert statvfs returned filesystem size into BLOCKSIZE units. 353 * Attempts to avoid overflow for large filesystems. 354 */ 355 #define fsbtoblk(num, fsbs, bs) \ 356 (((fsbs) != 0 && (uint64_t)(fsbs) < (uint64_t)(bs)) ? \ 357 (int64_t)(num) / (int64_t)((bs) / (fsbs)) : \ 358 (int64_t)(num) * (int64_t)((fsbs) / (bs))) 359 360 /* 361 * Print out status about a filesystem. 362 */ 363 static void 364 prtstat(const struct statvfs *sfsp, int maxwidth) 365 { 366 static long blocksize; 367 static int headerlen, timesthrough; 368 static const char *header; 369 static const char full[] = "100"; 370 static const char empty[] = " 0"; 371 int64_t used, availblks, inodes; 372 int64_t bavail; 373 char pb[64]; 374 char mntfromname[sizeof(sfsp->f_mntfromname) + 10]; 375 376 if (Wflag && sfsp->f_mntfromlabel[0]) { 377 snprintf(mntfromname, sizeof(mntfromname), "NAME=%s", 378 sfsp->f_mntfromlabel); 379 } else { 380 strlcpy(mntfromname, sfsp->f_mntfromname, sizeof(mntfromname)); 381 } 382 383 if (gflag) { 384 /* 385 * From SunOS-5.6: 386 * 387 * /var (/dev/dsk/c0t0d0s3 ): 8192 block size 1024 frag size 388 * 984242 total blocks 860692 free blocks 859708 available 249984 total files 389 * 248691 free files 8388611 filesys id 390 * ufs fstype 0x00000004 flag 255 filename length 391 * 392 */ 393 (void)printf("%10s (%-12s): %7ld block size %12ld frag size\n", 394 sfsp->f_mntonname, mntfromname, 395 sfsp->f_bsize, /* On UFS/FFS systems this is 396 * also called the "optimal 397 * transfer block size" but it 398 * is of course the file 399 * system's block size too. 400 */ 401 sfsp->f_frsize); /* not so surprisingly the 402 * "fundamental file system 403 * block size" is the frag 404 * size. 405 */ 406 (void)printf("%10" PRId64 " total blocks %10" PRId64 407 " free blocks %10" PRId64 " available\n", 408 (uint64_t)sfsp->f_blocks, (uint64_t)sfsp->f_bfree, 409 (uint64_t)sfsp->f_bavail); 410 (void)printf("%10" PRId64 " total files %10" PRId64 411 " free files %12lx filesys id\n", 412 (uint64_t)sfsp->f_ffree, (uint64_t)sfsp->f_files, 413 sfsp->f_fsid); 414 (void)printf("%10s fstype %#15lx flag %17ld filename " 415 "length\n", sfsp->f_fstypename, sfsp->f_flag, 416 sfsp->f_namemax); 417 (void)printf("%10lu owner %17" PRId64 " syncwrites %12" PRId64 418 " asyncwrites\n\n", (unsigned long)sfsp->f_owner, 419 sfsp->f_syncwrites, sfsp->f_asyncwrites); 420 421 /* 422 * a concession by the structured programming police to the 423 * indentation police.... 424 */ 425 return; 426 } 427 if (maxwidth < 12) 428 maxwidth = 12; 429 if (++timesthrough == 1) { 430 switch (blocksize = usize) { 431 case 1024: 432 header = Pflag ? "1024-blocks" : "1K-blocks"; 433 headerlen = (int)strlen(header); 434 break; 435 case 1024 * 1024: 436 header = "1M-blocks"; 437 headerlen = (int)strlen(header); 438 break; 439 case 1024 * 1024 * 1024: 440 header = "1G-blocks"; 441 headerlen = (int)strlen(header); 442 break; 443 default: 444 if (hflag) { 445 header = "Size"; 446 headerlen = (int)strlen(header); 447 } else 448 header = getbsize(&headerlen, &blocksize); 449 break; 450 } 451 if (Pflag) { 452 /* 453 * either: 454 * "Filesystem 1024-blocks Used Available Capacity Mounted on\n" 455 * or: 456 * "Filesystem 512-blocks Used Available Capacity Mounted on\n" 457 */ 458 if (blocksize != 1024 && blocksize != 512) 459 errx(EXIT_FAILURE, 460 "non-standard block size incompatible with -P"); 461 (void)printf("Filesystem %s Used Available Capacity " 462 "Mounted on\n", header); 463 } else { 464 (void)printf("%-*.*s %s Used Avail %%Cap", 465 maxwidth - (headerlen - 10), 466 maxwidth - (headerlen - 10), 467 "Filesystem", header); 468 if (iflag) 469 (void)printf(" iUsed iAvail %%iCap"); 470 (void)printf(" Mounted on\n"); 471 } 472 } 473 used = sfsp->f_blocks - sfsp->f_bfree; 474 bavail = sfsp->f_bfree - sfsp->f_bresvd; 475 availblks = bavail + used; 476 if (Pflag) { 477 assert(hflag == 0); 478 assert(blocksize > 0); 479 /* 480 * "%s %d %d %d %s %s\n", <file system name>, <total space>, 481 * <space used>, <space free>, <percentage used>, 482 * <file system root> 483 */ 484 (void)printf("%s %" PRId64 " %" PRId64 " %" PRId64 " %s%% %s\n", 485 mntfromname, 486 fsbtoblk(sfsp->f_blocks, sfsp->f_frsize, blocksize), 487 fsbtoblk(used, sfsp->f_frsize, blocksize), 488 fsbtoblk(bavail, sfsp->f_frsize, blocksize), 489 availblks == 0 ? full : strspct(pb, sizeof(pb), used, 490 availblks, 0), sfsp->f_mntonname); 491 /* 492 * another concession by the structured programming police to 493 * the indentation police.... 494 * 495 * Note iflag cannot be set when Pflag is set. 496 */ 497 return; 498 } 499 500 (void)printf("%-*.*s ", maxwidth, maxwidth, mntfromname); 501 502 if (hflag) 503 prthuman(sfsp, used, bavail); 504 else 505 (void)printf("%10" PRId64 " %10" PRId64 " %10" PRId64, 506 fsbtoblk(sfsp->f_blocks, sfsp->f_frsize, blocksize), 507 fsbtoblk(used, sfsp->f_frsize, blocksize), 508 fsbtoblk(bavail, sfsp->f_frsize, blocksize)); 509 (void)printf(" %3s%%", 510 availblks == 0 ? full : 511 strspct(pb, sizeof(pb), used, availblks, 0)); 512 if (iflag) { 513 inodes = sfsp->f_files; 514 used = inodes - sfsp->f_ffree; 515 (void)printf(" %8jd %8jd %4s%%", 516 (intmax_t)used, (intmax_t)sfsp->f_ffree, 517 inodes == 0 ? (used == 0 ? empty : full) : 518 strspct(pb, sizeof(pb), used, inodes, 0)); 519 } 520 (void)printf(" %s\n", sfsp->f_mntonname); 521 } 522 523 static void 524 usage(void) 525 { 526 527 (void)fprintf(stderr, 528 "Usage: %s [-aglnW] [-Ghkm|-ihkm|-Pk] [-t type] [file | " 529 "file_system ...]\n", 530 getprogname()); 531 exit(1); 532 /* NOTREACHED */ 533 } 534