1 /* 2 * Copyright (c) 1980, 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #ifndef lint 40 static const char copyright[] = 41 "@(#) Copyright (c) 1980, 1990, 1993, 1994\n\ 42 The Regents of the University of California. All rights reserved.\n"; 43 #endif /* not lint */ 44 45 #ifndef lint 46 #if 0 47 static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95"; 48 #else 49 static const char rcsid[] = 50 "$FreeBSD$"; 51 #endif 52 #endif /* not lint */ 53 54 #include <sys/param.h> 55 #include <sys/stat.h> 56 #include <sys/mount.h> 57 #include <ufs/ufs/ufsmount.h> 58 59 #include <err.h> 60 #include <errno.h> 61 #include <fcntl.h> 62 #include <math.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <sysexits.h> 67 #include <unistd.h> 68 69 #define UNITS_SI 1 70 #define UNITS_2 2 71 72 #define KILO_SZ(n) (n) 73 #define MEGA_SZ(n) ((n) * (n)) 74 #define GIGA_SZ(n) ((n) * (n) * (n)) 75 #define TERA_SZ(n) ((n) * (n) * (n) * (n)) 76 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) 77 78 #define KILO_2_SZ (KILO_SZ(1024ULL)) 79 #define MEGA_2_SZ (MEGA_SZ(1024ULL)) 80 #define GIGA_2_SZ (GIGA_SZ(1024ULL)) 81 #define TERA_2_SZ (TERA_SZ(1024ULL)) 82 #define PETA_2_SZ (PETA_SZ(1024ULL)) 83 84 #define KILO_SI_SZ (KILO_SZ(1000ULL)) 85 #define MEGA_SI_SZ (MEGA_SZ(1000ULL)) 86 #define GIGA_SI_SZ (GIGA_SZ(1000ULL)) 87 #define TERA_SI_SZ (TERA_SZ(1000ULL)) 88 #define PETA_SI_SZ (PETA_SZ(1000ULL)) 89 90 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; 91 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; 92 unsigned long long *valp; 93 94 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; 95 96 int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; 97 98 int checkvfsname __P((const char *, char **)); 99 char **makevfslist __P((char *)); 100 long regetmntinfo __P((struct statfs **, long, char **)); 101 int bread __P((off_t, void *, int)); 102 char *getmntpt __P((char *)); 103 void prthuman __P((struct statfs *, long)); 104 void prthumanval __P((double)); 105 void prtstat __P((struct statfs *, int)); 106 int ufs_df __P((char *, int)); 107 unit_t unit_adjust __P((double *)); 108 void usage __P((void)); 109 110 int aflag = 0, hflag, iflag, nflag; 111 struct ufs_args mdev; 112 113 int 114 main(argc, argv) 115 int argc; 116 char *argv[]; 117 { 118 struct stat stbuf; 119 struct statfs statfsbuf, *mntbuf; 120 long mntsize; 121 int ch, err, i, maxwidth, rv, width; 122 char *mntpt, *mntpath, **vfslist; 123 124 vfslist = NULL; 125 while ((ch = getopt(argc, argv, "abHhikmnPt:")) != -1) 126 switch (ch) { 127 case 'a': 128 aflag = 1; 129 break; 130 case 'b': 131 /* FALLTHROUGH */ 132 case 'P': 133 putenv("BLOCKSIZE=512"); 134 hflag = 0; 135 break; 136 case 'H': 137 hflag = UNITS_SI; 138 valp = vals_si; 139 break; 140 case 'h': 141 hflag = UNITS_2; 142 valp = vals_base2; 143 break; 144 case 'i': 145 iflag = 1; 146 break; 147 case 'k': 148 putenv("BLOCKSIZE=1k"); 149 hflag = 0; 150 break; 151 case 'm': 152 putenv("BLOCKSIZE=1m"); 153 hflag = 0; 154 break; 155 case 'n': 156 nflag = 1; 157 break; 158 case 't': 159 if (vfslist != NULL) 160 errx(1, "only one -t option may be specified."); 161 vfslist = makevfslist(optarg); 162 break; 163 case '?': 164 default: 165 usage(); 166 } 167 argc -= optind; 168 argv += optind; 169 170 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 171 maxwidth = 0; 172 for (i = 0; i < mntsize; i++) { 173 width = strlen(mntbuf[i].f_mntfromname); 174 if (width > maxwidth) 175 maxwidth = width; 176 } 177 178 rv = 0; 179 if (!*argv) { 180 mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); 181 if (vfslist != NULL) { 182 maxwidth = 0; 183 for (i = 0; i < mntsize; i++) { 184 width = strlen(mntbuf[i].f_mntfromname); 185 if (width > maxwidth) 186 maxwidth = width; 187 } 188 } 189 for (i = 0; i < mntsize; i++) { 190 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) 191 prtstat(&mntbuf[i], maxwidth); 192 } 193 exit(rv); 194 } 195 196 for (; *argv; argv++) { 197 if (stat(*argv, &stbuf) < 0) { 198 err = errno; 199 if ((mntpt = getmntpt(*argv)) == 0) { 200 warn("%s", *argv); 201 rv = 1; 202 continue; 203 } 204 } else if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { 205 rv = ufs_df(*argv, maxwidth) || rv; 206 continue; 207 } else if ((stbuf.st_mode & S_IFMT) == S_IFBLK) { 208 if ((mntpt = getmntpt(*argv)) == 0) { 209 mdev.fspec = *argv; 210 mntpath = strdup("/tmp/df.XXXXXX"); 211 if (mntpath == NULL) { 212 warn("strdup failed"); 213 rv = 1; 214 continue; 215 } 216 mntpt = mkdtemp(mntpath); 217 if (mntpt == NULL) { 218 warn("mkdtemp(\"%s\") failed", mntpath); 219 rv = 1; 220 free(mntpath); 221 continue; 222 } 223 if (mount("ufs", mntpt, MNT_RDONLY, 224 &mdev) != 0) { 225 rv = ufs_df(*argv, maxwidth) || rv; 226 (void)rmdir(mntpt); 227 free(mntpath); 228 continue; 229 } else if (statfs(mntpt, &statfsbuf) == 0) { 230 statfsbuf.f_mntonname[0] = '\0'; 231 prtstat(&statfsbuf, maxwidth); 232 } else { 233 warn("%s", *argv); 234 rv = 1; 235 } 236 (void)unmount(mntpt, 0); 237 (void)rmdir(mntpt); 238 free(mntpath); 239 continue; 240 } 241 } else 242 mntpt = *argv; 243 /* 244 * Statfs does not take a `wait' flag, so we cannot 245 * implement nflag here. 246 */ 247 if (statfs(mntpt, &statfsbuf) < 0) { 248 warn("%s", mntpt); 249 rv = 1; 250 continue; 251 } 252 if (argc == 1) 253 maxwidth = strlen(statfsbuf.f_mntfromname) + 1; 254 prtstat(&statfsbuf, maxwidth); 255 } 256 return (rv); 257 } 258 259 char * 260 getmntpt(name) 261 char *name; 262 { 263 long mntsize, i; 264 struct statfs *mntbuf; 265 266 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 267 for (i = 0; i < mntsize; i++) { 268 if (!strcmp(mntbuf[i].f_mntfromname, name)) 269 return (mntbuf[i].f_mntonname); 270 } 271 return (0); 272 } 273 274 /* 275 * Make a pass over the filesystem info in ``mntbuf'' filtering out 276 * filesystem types not in vfslist and possibly re-stating to get 277 * current (not cached) info. Returns the new count of valid statfs bufs. 278 */ 279 long 280 regetmntinfo(mntbufp, mntsize, vfslist) 281 struct statfs **mntbufp; 282 long mntsize; 283 char **vfslist; 284 { 285 int i, j; 286 struct statfs *mntbuf; 287 288 if (vfslist == NULL) 289 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 290 291 mntbuf = *mntbufp; 292 for (j = 0, i = 0; i < mntsize; i++) { 293 if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 294 continue; 295 if (!nflag) 296 (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); 297 else if (i != j) 298 mntbuf[j] = mntbuf[i]; 299 j++; 300 } 301 return (j); 302 } 303 304 /* 305 * Output in "human-readable" format. Uses 3 digits max and puts 306 * unit suffixes at the end. Makes output compact and easy to read, 307 * especially on huge disks. 308 * 309 */ 310 unit_t 311 unit_adjust(val) 312 double *val; 313 { 314 double abval; 315 unit_t unit; 316 unsigned int unit_sz; 317 318 abval = fabs(*val); 319 320 unit_sz = abval ? ilogb(abval) / 10 : 0; 321 322 if (unit_sz >= UNIT_MAX) { 323 unit = NONE; 324 } else { 325 unit = unitp[unit_sz]; 326 *val /= (double)valp[unit_sz]; 327 } 328 329 return (unit); 330 } 331 332 void 333 prthuman(sfsp, used) 334 struct statfs *sfsp; 335 long used; 336 { 337 338 prthumanval((double)sfsp->f_blocks * (double)sfsp->f_bsize); 339 prthumanval((double)used * (double)sfsp->f_bsize); 340 prthumanval((double)sfsp->f_bavail * (double)sfsp->f_bsize); 341 } 342 343 void 344 prthumanval(bytes) 345 double bytes; 346 { 347 348 unit_t unit; 349 unit = unit_adjust(&bytes); 350 351 if (bytes == 0) 352 (void)printf(" 0B"); 353 else if (bytes > 10) 354 (void)printf(" %5.0f%c", bytes, "BKMGTPE"[unit]); 355 else 356 (void)printf(" %5.1f%c", bytes, "BKMGTPE"[unit]); 357 } 358 359 /* 360 * Convert statfs returned filesystem size into BLOCKSIZE units. 361 * Attempts to avoid overflow for large filesystems. 362 */ 363 #define fsbtoblk(num, fsbs, bs) \ 364 (((fsbs) != 0 && (fsbs) < (bs)) ? \ 365 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) 366 367 /* 368 * Print out status about a filesystem. 369 */ 370 void 371 prtstat(sfsp, maxwidth) 372 struct statfs *sfsp; 373 int maxwidth; 374 { 375 static long blocksize; 376 static int headerlen, timesthrough; 377 static char *header; 378 long used, availblks, inodes; 379 380 if (maxwidth < 11) 381 maxwidth = 11; 382 if (++timesthrough == 1) { 383 if (hflag) { 384 header = " Size"; 385 headerlen = strlen(header); 386 (void)printf("%-*.*s %-s Used Avail Capacity", 387 maxwidth, maxwidth, "Filesystem", header); 388 } else { 389 header = getbsize(&headerlen, &blocksize); 390 (void)printf("%-*.*s %-s Used Avail Capacity", 391 maxwidth, maxwidth, "Filesystem", header); 392 } 393 if (iflag) 394 (void)printf(" iused ifree %%iused"); 395 (void)printf(" Mounted on\n"); 396 } 397 (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); 398 used = sfsp->f_blocks - sfsp->f_bfree; 399 availblks = sfsp->f_bavail + used; 400 if (hflag) { 401 prthuman(sfsp, used); 402 } else { 403 (void)printf(" %*ld %8ld %8ld", headerlen, 404 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 405 fsbtoblk(used, sfsp->f_bsize, blocksize), 406 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize)); 407 } 408 (void)printf(" %5.0f%%", 409 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); 410 if (iflag) { 411 inodes = sfsp->f_files; 412 used = inodes - sfsp->f_ffree; 413 (void)printf(" %7ld %7ld %5.0f%% ", used, sfsp->f_ffree, 414 inodes == 0 ? 100.0 : (double)used / (double)inodes * 100.0); 415 } else 416 (void)printf(" "); 417 (void)printf(" %s\n", sfsp->f_mntonname); 418 } 419 420 /* 421 * This code constitutes the pre-system call Berkeley df code for extracting 422 * information from filesystem superblocks. 423 */ 424 #include <ufs/ufs/dinode.h> 425 #include <ufs/ffs/fs.h> 426 #include <errno.h> 427 #include <fstab.h> 428 429 union { 430 struct fs iu_fs; 431 char dummy[SBSIZE]; 432 } sb; 433 #define sblock sb.iu_fs 434 435 int rfd; 436 437 int 438 ufs_df(file, maxwidth) 439 char *file; 440 int maxwidth; 441 { 442 struct statfs statfsbuf; 443 struct statfs *sfsp; 444 char *mntpt; 445 static int synced; 446 447 if (synced++ == 0) 448 sync(); 449 450 if ((rfd = open(file, O_RDONLY)) < 0) { 451 warn("%s", file); 452 return (1); 453 } 454 if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) { 455 (void)close(rfd); 456 return (1); 457 } 458 sfsp = &statfsbuf; 459 sfsp->f_type = 1; 460 strcpy(sfsp->f_fstypename, "ufs"); 461 sfsp->f_flags = 0; 462 sfsp->f_bsize = sblock.fs_fsize; 463 sfsp->f_iosize = sblock.fs_bsize; 464 sfsp->f_blocks = sblock.fs_dsize; 465 sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag + 466 sblock.fs_cstotal.cs_nffree; 467 sfsp->f_bavail = freespace(&sblock, sblock.fs_minfree); 468 sfsp->f_files = sblock.fs_ncg * sblock.fs_ipg; 469 sfsp->f_ffree = sblock.fs_cstotal.cs_nifree; 470 sfsp->f_fsid.val[0] = 0; 471 sfsp->f_fsid.val[1] = 0; 472 if ((mntpt = getmntpt(file)) == 0) 473 mntpt = ""; 474 memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN); 475 memmove(&sfsp->f_mntfromname[0], file, MNAMELEN); 476 prtstat(sfsp, maxwidth); 477 (void)close(rfd); 478 return (0); 479 } 480 481 int 482 bread(off, buf, cnt) 483 off_t off; 484 void *buf; 485 int cnt; 486 { 487 int nr; 488 489 (void)lseek(rfd, off, SEEK_SET); 490 if ((nr = read(rfd, buf, cnt)) != cnt) { 491 /* Probably a dismounted disk if errno == EIO. */ 492 if (errno != EIO) 493 (void)fprintf(stderr, "\ndf: %qd: %s\n", 494 off, strerror(nr > 0 ? EIO : errno)); 495 return (0); 496 } 497 return (1); 498 } 499 500 void 501 usage() 502 { 503 504 (void)fprintf(stderr, 505 "usage: df [-b | -H | -h | -k | -m | -P] [-ain] [-t type] [file | filesystem ...]\n"); 506 exit(EX_USAGE); 507 } 508