jcs's openbsd hax
openbsd
at jcs 528 lines 13 kB view raw
1/* $OpenBSD: df.c,v 1.63 2026/01/09 19:54:57 job Exp $ */ 2/* $NetBSD: df.c,v 1.21.2.1 1995/11/01 00:06:11 jtc Exp $ */ 3 4/* 5 * Copyright (c) 1980, 1990, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * (c) UNIX System Laboratories, Inc. 8 * All or some portions of this file are derived from material licensed 9 * to the University of California by American Telephone and Telegraph 10 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 11 * the permission of UNIX System Laboratories, Inc. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#include <sys/stat.h> 39#include <sys/mount.h> 40 41#include <err.h> 42#include <errno.h> 43#include <fcntl.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <unistd.h> 48#include <util.h> 49 50int bread(int, off_t, void *, int); 51static void bsdprint(struct statfs *, long, int); 52char *getmntpt(char *); 53static void maketypelist(char *); 54static int percent(u_int64_t, u_int64_t); 55static void posixprint(struct statfs *, long, int); 56static void prthuman(struct statfs *sfsp, unsigned long long); 57static void prthumanval(long long); 58static void prtstat(struct statfs *, int, int, int); 59static long regetmntinfo(struct statfs **, long); 60static int selected(const char *); 61static __dead void usage(void); 62 63extern int e2fs_df(int, char *, struct statfs *); 64extern int ffs_df(int, char *, struct statfs *); 65static int raw_df(char *, struct statfs *); 66 67int hflag, iflag, kflag, lflag, nflag, Pflag; 68char **typelist = NULL; 69 70int 71main(int argc, char *argv[]) 72{ 73 struct stat stbuf; 74 struct statfs *mntbuf; 75 long mntsize; 76 int ch, i; 77 int width, maxwidth; 78 char *mntpt; 79 80 if (pledge("stdio rpath", NULL) == -1) 81 err(1, "pledge"); 82 83 while ((ch = getopt(argc, argv, "hiklnPt:")) != -1) 84 switch (ch) { 85 case 'h': 86 hflag = 1; 87 kflag = 0; 88 break; 89 case 'i': 90 iflag = 1; 91 break; 92 case 'k': 93 kflag = 1; 94 hflag = 0; 95 break; 96 case 'l': 97 lflag = 1; 98 break; 99 case 'n': 100 nflag = 1; 101 break; 102 case 'P': 103 Pflag = 1; 104 break; 105 case 't': 106 if (typelist != NULL) 107 errx(1, "only one -t option may be specified."); 108 maketypelist(optarg); 109 break; 110 default: 111 usage(); 112 } 113 argc -= optind; 114 argv += optind; 115 116 if ((iflag || hflag) && Pflag) { 117 warnx("-h and -i are incompatible with -P"); 118 usage(); 119 } 120 121 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 122 if (mntsize == 0) 123 err(1, "retrieving information on mounted file systems"); 124 125 if (!*argv) { 126 mntsize = regetmntinfo(&mntbuf, mntsize); 127 } else { 128 mntbuf = calloc(argc, sizeof(struct statfs)); 129 if (mntbuf == NULL) 130 err(1, NULL); 131 mntsize = 0; 132 for (; *argv; argv++) { 133 if (stat(*argv, &stbuf) == -1) { 134 if ((mntpt = getmntpt(*argv)) == 0) { 135 warn("%s", *argv); 136 continue; 137 } 138 } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) { 139 if (!raw_df(*argv, &mntbuf[mntsize])) 140 ++mntsize; 141 continue; 142 } else 143 mntpt = *argv; 144 /* 145 * Statfs does not take a `wait' flag, so we cannot 146 * implement nflag here. 147 */ 148 if (!statfs(mntpt, &mntbuf[mntsize])) 149 if (lflag && (mntbuf[mntsize].f_flags & MNT_LOCAL) == 0) 150 warnx("%s is not a local file system", 151 *argv); 152 else if (!selected(mntbuf[mntsize].f_fstypename)) 153 warnx("%s mounted as a %s file system", 154 *argv, mntbuf[mntsize].f_fstypename); 155 else 156 ++mntsize; 157 else 158 warn("%s", *argv); 159 } 160 } 161 162 if (mntsize) { 163 maxwidth = 11; 164 for (i = 0; i < mntsize; i++) { 165 width = strlen(mntbuf[i].f_mntfromname); 166 if (width > maxwidth) 167 maxwidth = width; 168 } 169 170 if (Pflag) 171 posixprint(mntbuf, mntsize, maxwidth); 172 else 173 bsdprint(mntbuf, mntsize, maxwidth); 174 } 175 176 return (mntsize ? 0 : 1); 177} 178 179char * 180getmntpt(char *name) 181{ 182 long mntsize, i; 183 struct statfs *mntbuf; 184 185 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 186 for (i = 0; i < mntsize; i++) { 187 if (!strcmp(mntbuf[i].f_mntfromname, name)) 188 return (mntbuf[i].f_mntonname); 189 } 190 return (0); 191} 192 193static enum { IN_LIST, NOT_IN_LIST } which; 194 195static int 196selected(const char *type) 197{ 198 char **av; 199 200 /* If no type specified, it's always selected. */ 201 if (typelist == NULL) 202 return (1); 203 for (av = typelist; *av != NULL; ++av) 204 if (!strncmp(type, *av, MFSNAMELEN)) 205 return (which == IN_LIST ? 1 : 0); 206 return (which == IN_LIST ? 0 : 1); 207} 208 209static void 210maketypelist(char *fslist) 211{ 212 int i; 213 char *nextcp, **av; 214 215 if ((fslist == NULL) || (fslist[0] == '\0')) 216 errx(1, "empty type list"); 217 218 /* 219 * XXX 220 * Note: the syntax is "noxxx,yyy" for no xxx's and 221 * no yyy's, not the more intuitive "noyyy,noyyy". 222 */ 223 if (fslist[0] == 'n' && fslist[1] == 'o') { 224 fslist += 2; 225 which = NOT_IN_LIST; 226 } else 227 which = IN_LIST; 228 229 /* Count the number of types. */ 230 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')) != NULL; i++) 231 ++nextcp; 232 233 /* Build an array of that many types. */ 234 if ((av = typelist = calloc(i + 1, sizeof(char *))) == NULL) 235 err(1, NULL); 236 av[0] = fslist; 237 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')) != NULL; i++) { 238 *nextcp = '\0'; 239 av[i] = ++nextcp; 240 } 241 /* Terminate the array. */ 242 av[i] = NULL; 243} 244 245/* 246 * Make a pass over the filesystem info in ``mntbuf'' filtering out 247 * filesystem types not in ``fsmask'' and possibly re-stating to get 248 * current (not cached) info. Returns the new count of valid statfs bufs. 249 */ 250static long 251regetmntinfo(struct statfs **mntbufp, long mntsize) 252{ 253 int i, j; 254 struct statfs *mntbuf; 255 256 if (!lflag && typelist == NULL) 257 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 258 259 mntbuf = *mntbufp; 260 j = 0; 261 for (i = 0; i < mntsize; i++) { 262 if (lflag && (mntbuf[i].f_flags & MNT_LOCAL) == 0) 263 continue; 264 if (!selected(mntbuf[i].f_fstypename)) 265 continue; 266 if (nflag) 267 mntbuf[j] = mntbuf[i]; 268 else 269 (void)statfs(mntbuf[i].f_mntonname, &mntbuf[j]); 270 j++; 271 } 272 return (j); 273} 274 275/* 276 * "human-readable" output: use 3 digits max.--put unit suffixes at 277 * the end. Makes output compact and easy-to-read esp. on huge disks. 278 * Code moved into libutil; this is now just a wrapper. 279 */ 280static void 281prthumanval(long long bytes) 282{ 283 char ret[FMT_SCALED_STRSIZE]; 284 285 if (fmt_scaled(bytes, ret) == -1) { 286 (void)printf(" %lld", bytes); 287 return; 288 } 289 (void)printf(" %7s", ret); 290} 291 292static void 293prthuman(struct statfs *sfsp, unsigned long long used) 294{ 295 prthumanval(sfsp->f_blocks * sfsp->f_bsize); 296 prthumanval(used * sfsp->f_bsize); 297 prthumanval(sfsp->f_bavail * sfsp->f_bsize); 298} 299 300/* 301 * Human readable output using SI symbols and multiples of thousand. 302 */ 303static const struct scale { 304 char symbol; 305 unsigned long long factor; 306} scale[] = { 307 { ' ', 1LL }, 308 { 'k', 1000LL }, /* kilo */ 309 { 'M', 1000LL * 1000 }, /* mega */ 310 { 'G', 1000LL * 1000 * 1000 }, /* giga */ 311 { 'T', 1000LL * 1000 * 1000 * 1000 }, /* tera */ 312 { 'P', 1000LL * 1000 * 1000 * 1000 * 1000 }, /* peta */ 313 { 'E', 1000LL * 1000 * 1000 * 1000 * 1000 * 1000 } /* exa */ 314}; 315#define SCALE_LENGTH (sizeof(scale) / sizeof(scale[0])) 316 317static void 318prtscaleddecimal(unsigned long long num) 319{ 320 long long fract = 0; 321 unsigned long i; 322 int unit = 0; 323 324 if (num < 1000 || num / 1000 >= scale[SCALE_LENGTH - 1].factor) { 325 (void)printf(" %6llu", num); 326 return; 327 } 328 329 for (i = 0; i < SCALE_LENGTH; i++) { 330 if (num / 1000 < scale[i].factor) { 331 unit = i; 332 fract = (i == 0) ? 0 : num % scale[i].factor; 333 num /= scale[i].factor; 334 if (i > 0) 335 fract /= scale[i - 1].factor; 336 break; 337 } 338 } 339 340 fract = (10 * fract + 500) / 1000; 341 if (fract >= 10) { 342 num++; 343 fract = 0; 344 } 345 346 if (num >= 100) { 347 if (fract >= 5) 348 num++; 349 (void)printf(" %5llu%c", num, scale[unit].symbol); 350 } else 351 (void)printf(" %3llu.%1llu%c", num, fract, scale[unit].symbol); 352} 353 354/* 355 * Convert statfs returned filesystem size into BLOCKSIZE units. 356 * Attempts to avoid overflow for large filesystems. 357 */ 358#define fsbtoblk(num, fsbs, bs) \ 359 (((fsbs) != 0 && (fsbs) < (bs)) ? \ 360 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) 361 362/* 363 * Print out status about a filesystem. 364 */ 365static void 366prtstat(struct statfs *sfsp, int maxwidth, int headerlen, int blocksize) 367{ 368 u_int64_t used, inodes; 369 int64_t availblks; 370 371 (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); 372 used = sfsp->f_blocks - sfsp->f_bfree; 373 availblks = sfsp->f_bavail + used; 374 if (hflag) 375 prthuman(sfsp, used); 376 else 377 (void)printf(" %*llu %9llu %9lld", headerlen, 378 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 379 fsbtoblk(used, sfsp->f_bsize, blocksize), 380 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize)); 381 (void)printf(" %5d%%", percent(used, availblks)); 382 if (iflag) { 383 inodes = sfsp->f_files; 384 used = inodes - sfsp->f_ffree; 385 386 if (hflag) { 387 (void)printf(" "); 388 prtscaleddecimal(used); 389 prtscaleddecimal(sfsp->f_ffree); 390 (void)printf(" %4d%% ", percent(used, inodes)); 391 } else { 392 (void)printf(" %8llu %8llu %4d%% ", used, sfsp->f_ffree, 393 percent(used, inodes)); 394 } 395 } else 396 (void)printf(" "); 397 (void)printf(" %s\n", sfsp->f_mntonname); 398} 399 400/* 401 * Print in traditional BSD format. 402 */ 403static void 404bsdprint(struct statfs *mntbuf, long mntsize, int maxwidth) 405{ 406 int i; 407 char *header; 408 int headerlen; 409 long blocksize; 410 411 /* Print the header line */ 412 if (hflag) { 413 header = " Size"; 414 headerlen = strlen(header); 415 (void)printf("%-*.*s %s Used Avail Capacity", 416 maxwidth, maxwidth, "Filesystem", header); 417 } else { 418 if (kflag) { 419 blocksize = 1024; 420 header = "1K-blocks"; 421 headerlen = strlen(header); 422 } else 423 header = getbsize(&headerlen, &blocksize); 424 (void)printf("%-*.*s %s Used Avail Capacity", 425 maxwidth, maxwidth, "Filesystem", header); 426 } 427 if (iflag) { 428 if (hflag) 429 (void)printf(" iused ifree %%iused"); 430 else 431 (void)printf(" iused ifree %%iused"); 432 } 433 (void)printf(" Mounted on\n"); 434 435 436 for (i = 0; i < mntsize; i++) 437 prtstat(&mntbuf[i], maxwidth, headerlen, blocksize); 438 return; 439} 440 441static int 442percent(u_int64_t used, u_int64_t avail) 443{ 444 return avail ? (100 * used + (avail - 1)) / avail : 100; 445} 446 447/* 448 * Print in format defined by POSIX 1002.2, invoke with -P option. 449 */ 450static void 451posixprint(struct statfs *mntbuf, long mntsize, int maxwidth) 452{ 453 int i; 454 int blocksize; 455 char *blockstr; 456 struct statfs *sfsp; 457 long long used, avail; 458 459 if (kflag) { 460 blocksize = 1024; 461 blockstr = "1024-blocks"; 462 } else { 463 blocksize = 512; 464 blockstr = " 512-blocks"; 465 } 466 467 (void)printf( 468 "%-*.*s %s Used Available Capacity Mounted on\n", 469 maxwidth, maxwidth, "Filesystem", blockstr); 470 471 for (i = 0; i < mntsize; i++) { 472 sfsp = &mntbuf[i]; 473 used = sfsp->f_blocks - sfsp->f_bfree; 474 avail = sfsp->f_bavail + used; 475 476 (void) printf ("%-*.*s %*lld %10lld %11lld %5d%% %s\n", 477 maxwidth, maxwidth, sfsp->f_mntfromname, 478 (int)strlen(blockstr), 479 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 480 fsbtoblk(used, sfsp->f_bsize, blocksize), 481 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize), 482 percent(used, avail), sfsp->f_mntonname); 483 } 484} 485 486static int 487raw_df(char *file, struct statfs *sfsp) 488{ 489 int rfd, ret = -1; 490 491 if ((rfd = open(file, O_RDONLY)) == -1) { 492 warn("%s", file); 493 return (-1); 494 } 495 496 if (ffs_df(rfd, file, sfsp) == 0) { 497 ret = 0; 498 } else if (e2fs_df(rfd, file, sfsp) == 0) { 499 ret = 0; 500 } 501 502 close (rfd); 503 return (ret); 504} 505 506int 507bread(int rfd, off_t off, void *buf, int cnt) 508{ 509 int nr; 510 511 if ((nr = pread(rfd, buf, cnt, off)) != cnt) { 512 /* Probably a dismounted disk if errno == EIO. */ 513 if (errno != EIO) 514 (void)fprintf(stderr, "\ndf: %lld: %s\n", 515 (long long)off, strerror(nr > 0 ? EIO : errno)); 516 return (0); 517 } 518 return (1); 519} 520 521static __dead void 522usage(void) 523{ 524 (void)fprintf(stderr, 525 "usage: %s [-hiklnP] [-t type] [[file | file_system] ...]\n", 526 getprogname()); 527 exit(1); 528}