1 /* $OpenBSD: df.c,v 1.59 2016/08/14 21:07:40 krw 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 50 int bread(int, off_t, void *, int); 51 static void bsdprint(struct statfs *, long, int); 52 char *getmntpt(char *); 53 static void maketypelist(char *); 54 static void posixprint(struct statfs *, long, int); 55 static void prthuman(struct statfs *sfsp, unsigned long long); 56 static void prthumanval(long long); 57 static void prtstat(struct statfs *, int, int, int); 58 static long regetmntinfo(struct statfs **, long); 59 static int selected(const char *); 60 static __dead void usage(void); 61 62 extern int e2fs_df(int, char *, struct statfs *); 63 extern int ffs_df(int, char *, struct statfs *); 64 static int raw_df(char *, struct statfs *); 65 66 int hflag, iflag, kflag, lflag, nflag, Pflag; 67 char **typelist = NULL; 68 69 int 70 main(int argc, char *argv[]) 71 { 72 struct stat stbuf; 73 struct statfs *mntbuf; 74 long mntsize; 75 int ch, i; 76 int width, maxwidth; 77 char *mntpt; 78 79 if (pledge("stdio rpath", NULL) == -1) 80 err(1, "pledge"); 81 82 while ((ch = getopt(argc, argv, "hiklnPt:")) != -1) 83 switch (ch) { 84 case 'h': 85 hflag = 1; 86 kflag = 0; 87 break; 88 case 'i': 89 iflag = 1; 90 break; 91 case 'k': 92 kflag = 1; 93 hflag = 0; 94 break; 95 case 'l': 96 lflag = 1; 97 break; 98 case 'n': 99 nflag = 1; 100 break; 101 case 'P': 102 Pflag = 1; 103 break; 104 case 't': 105 if (typelist != NULL) 106 errx(1, "only one -t option may be specified."); 107 maketypelist(optarg); 108 break; 109 default: 110 usage(); 111 } 112 argc -= optind; 113 argv += optind; 114 115 if ((iflag || hflag) && Pflag) { 116 warnx("-h and -i are incompatible with -P"); 117 usage(); 118 } 119 120 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 121 if (mntsize == 0) 122 err(1, "retrieving information on mounted file systems"); 123 124 if (!*argv) { 125 mntsize = regetmntinfo(&mntbuf, mntsize); 126 } else { 127 mntbuf = calloc(argc, sizeof(struct statfs)); 128 if (mntbuf == NULL) 129 err(1, NULL); 130 mntsize = 0; 131 for (; *argv; argv++) { 132 if (stat(*argv, &stbuf) < 0) { 133 if ((mntpt = getmntpt(*argv)) == 0) { 134 warn("%s", *argv); 135 continue; 136 } 137 } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) { 138 if (!raw_df(*argv, &mntbuf[mntsize])) 139 ++mntsize; 140 continue; 141 } else 142 mntpt = *argv; 143 /* 144 * Statfs does not take a `wait' flag, so we cannot 145 * implement nflag here. 146 */ 147 if (!statfs(mntpt, &mntbuf[mntsize])) 148 if (lflag && (mntbuf[mntsize].f_flags & MNT_LOCAL) == 0) 149 warnx("%s is not a local file system", 150 *argv); 151 else if (!selected(mntbuf[mntsize].f_fstypename)) 152 warnx("%s mounted as a %s file system", 153 *argv, mntbuf[mntsize].f_fstypename); 154 else 155 ++mntsize; 156 else 157 warn("%s", *argv); 158 } 159 } 160 161 if (mntsize) { 162 maxwidth = 11; 163 for (i = 0; i < mntsize; i++) { 164 width = strlen(mntbuf[i].f_mntfromname); 165 if (width > maxwidth) 166 maxwidth = width; 167 } 168 169 if (Pflag) 170 posixprint(mntbuf, mntsize, maxwidth); 171 else 172 bsdprint(mntbuf, mntsize, maxwidth); 173 } 174 175 return (mntsize ? 0 : 1); 176 } 177 178 char * 179 getmntpt(char *name) 180 { 181 long mntsize, i; 182 struct statfs *mntbuf; 183 184 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 185 for (i = 0; i < mntsize; i++) { 186 if (!strcmp(mntbuf[i].f_mntfromname, name)) 187 return (mntbuf[i].f_mntonname); 188 } 189 return (0); 190 } 191 192 static enum { IN_LIST, NOT_IN_LIST } which; 193 194 static int 195 selected(const char *type) 196 { 197 char **av; 198 199 /* If no type specified, it's always selected. */ 200 if (typelist == NULL) 201 return (1); 202 for (av = typelist; *av != NULL; ++av) 203 if (!strncmp(type, *av, MFSNAMELEN)) 204 return (which == IN_LIST ? 1 : 0); 205 return (which == IN_LIST ? 0 : 1); 206 } 207 208 static void 209 maketypelist(char *fslist) 210 { 211 int i; 212 char *nextcp, **av; 213 214 if ((fslist == NULL) || (fslist[0] == '\0')) 215 errx(1, "empty type list"); 216 217 /* 218 * XXX 219 * Note: the syntax is "noxxx,yyy" for no xxx's and 220 * no yyy's, not the more intuitive "noyyy,noyyy". 221 */ 222 if (fslist[0] == 'n' && fslist[1] == 'o') { 223 fslist += 2; 224 which = NOT_IN_LIST; 225 } else 226 which = IN_LIST; 227 228 /* Count the number of types. */ 229 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')) != NULL; i++) 230 ++nextcp; 231 232 /* Build an array of that many types. */ 233 if ((av = typelist = calloc(i + 1, sizeof(char *))) == NULL) 234 err(1, NULL); 235 av[0] = fslist; 236 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')) != NULL; i++) { 237 *nextcp = '\0'; 238 av[i] = ++nextcp; 239 } 240 /* Terminate the array. */ 241 av[i] = NULL; 242 } 243 244 /* 245 * Make a pass over the filesystem info in ``mntbuf'' filtering out 246 * filesystem types not in ``fsmask'' and possibly re-stating to get 247 * current (not cached) info. Returns the new count of valid statfs bufs. 248 */ 249 static long 250 regetmntinfo(struct statfs **mntbufp, long mntsize) 251 { 252 int i, j; 253 struct statfs *mntbuf; 254 255 if (!lflag && typelist == NULL) 256 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 257 258 mntbuf = *mntbufp; 259 j = 0; 260 for (i = 0; i < mntsize; i++) { 261 if (lflag && (mntbuf[i].f_flags & MNT_LOCAL) == 0) 262 continue; 263 if (!selected(mntbuf[i].f_fstypename)) 264 continue; 265 if (nflag) 266 mntbuf[j] = mntbuf[i]; 267 else 268 (void)statfs(mntbuf[i].f_mntonname, &mntbuf[j]); 269 j++; 270 } 271 return (j); 272 } 273 274 /* 275 * "human-readable" output: use 3 digits max.--put unit suffixes at 276 * the end. Makes output compact and easy-to-read esp. on huge disks. 277 * Code moved into libutil; this is now just a wrapper. 278 */ 279 static void 280 prthumanval(long long bytes) 281 { 282 char ret[FMT_SCALED_STRSIZE]; 283 284 if (fmt_scaled(bytes, ret) == -1) { 285 (void)printf(" %lld", bytes); 286 return; 287 } 288 (void)printf(" %7s", ret); 289 } 290 291 static void 292 prthuman(struct statfs *sfsp, unsigned long long used) 293 { 294 prthumanval(sfsp->f_blocks * sfsp->f_bsize); 295 prthumanval(used * sfsp->f_bsize); 296 prthumanval(sfsp->f_bavail * sfsp->f_bsize); 297 } 298 299 /* 300 * Convert statfs returned filesystem size into BLOCKSIZE units. 301 * Attempts to avoid overflow for large filesystems. 302 */ 303 #define fsbtoblk(num, fsbs, bs) \ 304 (((fsbs) != 0 && (fsbs) < (bs)) ? \ 305 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) 306 307 /* 308 * Print out status about a filesystem. 309 */ 310 static void 311 prtstat(struct statfs *sfsp, int maxwidth, int headerlen, int blocksize) 312 { 313 u_int64_t used, inodes; 314 int64_t availblks; 315 316 (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); 317 used = sfsp->f_blocks - sfsp->f_bfree; 318 availblks = sfsp->f_bavail + used; 319 if (hflag) 320 prthuman(sfsp, used); 321 else 322 (void)printf(" %*llu %9llu %9lld", headerlen, 323 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 324 fsbtoblk(used, sfsp->f_bsize, blocksize), 325 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize)); 326 (void)printf(" %5.0f%%", 327 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); 328 if (iflag) { 329 inodes = sfsp->f_files; 330 used = inodes - sfsp->f_ffree; 331 (void)printf(" %7llu %7llu %5.0f%% ", used, sfsp->f_ffree, 332 inodes == 0 ? 100.0 : (double)used / (double)inodes * 100.0); 333 } else 334 (void)printf(" "); 335 (void)printf(" %s\n", sfsp->f_mntonname); 336 } 337 338 /* 339 * Print in traditional BSD format. 340 */ 341 static void 342 bsdprint(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 375 /* 376 * Print in format defined by POSIX 1002.2, invoke with -P option. 377 */ 378 static void 379 posixprint(struct statfs *mntbuf, long mntsize, int maxwidth) 380 { 381 int i; 382 int blocksize; 383 char *blockstr; 384 struct statfs *sfsp; 385 long long used, avail; 386 double percentused; 387 388 if (kflag) { 389 blocksize = 1024; 390 blockstr = "1024-blocks"; 391 } else { 392 blocksize = 512; 393 blockstr = " 512-blocks"; 394 } 395 396 (void)printf( 397 "%-*.*s %s Used Available Capacity Mounted on\n", 398 maxwidth, maxwidth, "Filesystem", blockstr); 399 400 for (i = 0; i < mntsize; i++) { 401 sfsp = &mntbuf[i]; 402 used = sfsp->f_blocks - sfsp->f_bfree; 403 avail = sfsp->f_bavail + used; 404 if (avail == 0) 405 percentused = 100.0; 406 else 407 percentused = (double)used / (double)avail * 100.0; 408 409 (void) printf ("%-*.*s %*lld %10lld %11lld %5.0f%% %s\n", 410 maxwidth, maxwidth, sfsp->f_mntfromname, 411 (int)strlen(blockstr), 412 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 413 fsbtoblk(used, sfsp->f_bsize, blocksize), 414 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize), 415 percentused, sfsp->f_mntonname); 416 } 417 } 418 419 static int 420 raw_df(char *file, struct statfs *sfsp) 421 { 422 int rfd, ret = -1; 423 424 if ((rfd = open(file, O_RDONLY)) < 0) { 425 warn("%s", file); 426 return (-1); 427 } 428 429 if (ffs_df(rfd, file, sfsp) == 0) { 430 ret = 0; 431 } else if (e2fs_df(rfd, file, sfsp) == 0) { 432 ret = 0; 433 } 434 435 close (rfd); 436 return (ret); 437 } 438 439 int 440 bread(int rfd, off_t off, void *buf, int cnt) 441 { 442 int nr; 443 444 if ((nr = pread(rfd, buf, cnt, off)) != cnt) { 445 /* Probably a dismounted disk if errno == EIO. */ 446 if (errno != EIO) 447 (void)fprintf(stderr, "\ndf: %lld: %s\n", 448 (long long)off, strerror(nr > 0 ? EIO : errno)); 449 return (0); 450 } 451 return (1); 452 } 453 454 static __dead void 455 usage(void) 456 { 457 (void)fprintf(stderr, 458 "usage: %s [-hiklnP] [-t type] [[file | file_system] ...]\n", 459 getprogname()); 460 exit(1); 461 } 462