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