1/* $OpenBSD: df.c,v 1.61 2023/01/01 16:31:20 millert 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 * Convert statfs returned filesystem size into BLOCKSIZE units. 302 * Attempts to avoid overflow for large filesystems. 303 */ 304#define fsbtoblk(num, fsbs, bs) \ 305 (((fsbs) != 0 && (fsbs) < (bs)) ? \ 306 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) 307 308/* 309 * Print out status about a filesystem. 310 */ 311static void 312prtstat(struct statfs *sfsp, int maxwidth, int headerlen, int blocksize) 313{ 314 u_int64_t used, inodes; 315 int64_t availblks; 316 317 (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); 318 used = sfsp->f_blocks - sfsp->f_bfree; 319 availblks = sfsp->f_bavail + used; 320 if (hflag) 321 prthuman(sfsp, used); 322 else 323 (void)printf(" %*llu %9llu %9lld", headerlen, 324 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 325 fsbtoblk(used, sfsp->f_bsize, blocksize), 326 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize)); 327 (void)printf(" %5d%%", percent(used, availblks)); 328 if (iflag) { 329 inodes = sfsp->f_files; 330 used = inodes - sfsp->f_ffree; 331 (void)printf(" %7llu %7llu %5d%% ", used, sfsp->f_ffree, 332 percent(used, inodes)); 333 } else 334 (void)printf(" "); 335 (void)printf(" %s\n", sfsp->f_mntonname); 336} 337 338/* 339 * Print in traditional BSD format. 340 */ 341static void 342bsdprint(struct statfs *mntbuf, long mntsize, int maxwidth) 343{ 344 int i; 345 char *header; 346 int headerlen; 347 long blocksize; 348 349 /* Print the header line */ 350 if (hflag) { 351 header = " Size"; 352 headerlen = strlen(header); 353 (void)printf("%-*.*s %s Used Avail Capacity", 354 maxwidth, maxwidth, "Filesystem", header); 355 } else { 356 if (kflag) { 357 blocksize = 1024; 358 header = "1K-blocks"; 359 headerlen = strlen(header); 360 } else 361 header = getbsize(&headerlen, &blocksize); 362 (void)printf("%-*.*s %s Used Avail Capacity", 363 maxwidth, maxwidth, "Filesystem", header); 364 } 365 if (iflag) 366 (void)printf(" iused ifree %%iused"); 367 (void)printf(" Mounted on\n"); 368 369 370 for (i = 0; i < mntsize; i++) 371 prtstat(&mntbuf[i], maxwidth, headerlen, blocksize); 372 return; 373} 374 375static int 376percent(u_int64_t used, u_int64_t avail) 377{ 378 return avail ? (100 * used + (avail - 1)) / avail : 100; 379} 380 381/* 382 * Print in format defined by POSIX 1002.2, invoke with -P option. 383 */ 384static void 385posixprint(struct statfs *mntbuf, long mntsize, int maxwidth) 386{ 387 int i; 388 int blocksize; 389 char *blockstr; 390 struct statfs *sfsp; 391 long long used, avail; 392 393 if (kflag) { 394 blocksize = 1024; 395 blockstr = "1024-blocks"; 396 } else { 397 blocksize = 512; 398 blockstr = " 512-blocks"; 399 } 400 401 (void)printf( 402 "%-*.*s %s Used Available Capacity Mounted on\n", 403 maxwidth, maxwidth, "Filesystem", blockstr); 404 405 for (i = 0; i < mntsize; i++) { 406 sfsp = &mntbuf[i]; 407 used = sfsp->f_blocks - sfsp->f_bfree; 408 avail = sfsp->f_bavail + used; 409 410 (void) printf ("%-*.*s %*lld %10lld %11lld %5d%% %s\n", 411 maxwidth, maxwidth, sfsp->f_mntfromname, 412 (int)strlen(blockstr), 413 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 414 fsbtoblk(used, sfsp->f_bsize, blocksize), 415 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize), 416 percent(used, avail), sfsp->f_mntonname); 417 } 418} 419 420static int 421raw_df(char *file, struct statfs *sfsp) 422{ 423 int rfd, ret = -1; 424 425 if ((rfd = open(file, O_RDONLY)) == -1) { 426 warn("%s", file); 427 return (-1); 428 } 429 430 if (ffs_df(rfd, file, sfsp) == 0) { 431 ret = 0; 432 } else if (e2fs_df(rfd, file, sfsp) == 0) { 433 ret = 0; 434 } 435 436 close (rfd); 437 return (ret); 438} 439 440int 441bread(int rfd, off_t off, void *buf, int cnt) 442{ 443 int nr; 444 445 if ((nr = pread(rfd, buf, cnt, off)) != cnt) { 446 /* Probably a dismounted disk if errno == EIO. */ 447 if (errno != EIO) 448 (void)fprintf(stderr, "\ndf: %lld: %s\n", 449 (long long)off, strerror(nr > 0 ? EIO : errno)); 450 return (0); 451 } 452 return (1); 453} 454 455static __dead void 456usage(void) 457{ 458 (void)fprintf(stderr, 459 "usage: %s [-hiklnP] [-t type] [[file | file_system] ...]\n", 460 getprogname()); 461 exit(1); 462} 463