1 /* $NetBSD: df.c,v 1.35 2000/10/15 17:50:10 kleink 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. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41 #include <sys/cdefs.h> 42 #ifndef lint 43 __COPYRIGHT( 44 "@(#) Copyright (c) 1980, 1990, 1993, 1994\n\ 45 The Regents of the University of California. All rights reserved.\n"); 46 #endif /* not lint */ 47 48 #ifndef lint 49 #if 0 50 static char sccsid[] = "@(#)df.c 8.7 (Berkeley) 4/2/94"; 51 #else 52 __RCSID("$NetBSD: df.c,v 1.35 2000/10/15 17:50:10 kleink Exp $"); 53 #endif 54 #endif /* not lint */ 55 56 #include <sys/param.h> 57 #include <sys/stat.h> 58 #include <sys/mount.h> 59 60 #include <ufs/ufs/ufsmount.h> 61 62 #include <err.h> 63 #include <errno.h> 64 #include <fcntl.h> 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <unistd.h> 69 70 extern char * strpct __P((u_long num, u_long denom, u_int digits)); 71 72 int main __P((int, char *[])); 73 int bread __P((off_t, void *, int)); 74 char *getmntpt __P((char *)); 75 void prtstat __P((struct statfs *, int)); 76 int ufs_df __P((char *, struct statfs *)); 77 int selected __P((const char *)); 78 void maketypelist __P((char *)); 79 long regetmntinfo __P((struct statfs **, long)); 80 void usage __P((void)); 81 82 int aflag, iflag, kflag, lflag, nflag, Pflag; 83 char **typelist = NULL; 84 struct ufs_args mdev; 85 86 int 87 main(argc, argv) 88 int argc; 89 char *argv[]; 90 { 91 struct stat stbuf; 92 struct statfs *mntbuf; 93 long mntsize; 94 int ch, i, maxwidth, width; 95 char *mntpt; 96 97 while ((ch = getopt(argc, argv, "aiklnPt:")) != -1) 98 switch (ch) { 99 case 'a': 100 aflag = 1; 101 break; 102 case 'i': 103 iflag = 1; 104 break; 105 case 'k': 106 kflag = 1; 107 break; 108 case 'l': 109 lflag = 1; 110 break; 111 case 'n': 112 nflag = 1; 113 break; 114 case 'P': 115 Pflag = 1; 116 break; 117 case 't': 118 if (typelist != NULL) 119 errx(1, "only one -t option may be specified."); 120 maketypelist(optarg); 121 break; 122 case '?': 123 default: 124 usage(); 125 } 126 argc -= optind; 127 argv += optind; 128 129 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 130 if (mntsize == 0) 131 err(1, "retrieving information on mounted file systems"); 132 133 if (!*argv) { 134 mntsize = regetmntinfo(&mntbuf, mntsize); 135 } else { 136 mntbuf = malloc(argc * sizeof(struct statfs)); 137 mntsize = 0; 138 for (; *argv; argv++) { 139 if (stat(*argv, &stbuf) < 0) { 140 if ((mntpt = getmntpt(*argv)) == 0) { 141 warn("%s", *argv); 142 continue; 143 } 144 } else if (S_ISCHR(stbuf.st_mode)) { 145 if (!ufs_df(*argv, &mntbuf[mntsize])) 146 ++mntsize; 147 continue; 148 } else if (S_ISBLK(stbuf.st_mode)) { 149 if ((mntpt = getmntpt(*argv)) == 0) { 150 mntpt = strdup("/tmp/df.XXXXXX"); 151 if (!mkdtemp(mntpt)) { 152 warn("%s", mntpt); 153 continue; 154 } 155 mdev.fspec = *argv; 156 if (mount(MOUNT_FFS, mntpt, MNT_RDONLY, 157 &mdev) != 0) { 158 (void)rmdir(mntpt); 159 if (!ufs_df(*argv, 160 &mntbuf[mntsize])) 161 ++mntsize; 162 continue; 163 } else if (!statfs(mntpt, 164 &mntbuf[mntsize])) { 165 mntbuf[mntsize].f_mntonname[0] = 166 '\0'; 167 ++mntsize; 168 } else 169 warn("%s", *argv); 170 (void)unmount(mntpt, 0); 171 (void)rmdir(mntpt); 172 continue; 173 } 174 } else 175 mntpt = *argv; 176 /* 177 * Statfs does not take a `wait' flag, so we cannot 178 * implement nflag here. 179 */ 180 if (!statfs(mntpt, &mntbuf[mntsize])) 181 if (lflag && 182 (mntbuf[mntsize].f_flags & MNT_LOCAL) == 0) 183 warnx("Warning: %s is not a local %s", 184 *argv, "file system"); 185 else if 186 (!selected(mntbuf[mntsize].f_fstypename)) 187 warnx("Warning: %s mounted as a %s %s", 188 *argv, 189 mntbuf[mntsize].f_fstypename, 190 "file system"); 191 else 192 ++mntsize; 193 else 194 warn("%s", *argv); 195 } 196 } 197 198 maxwidth = 0; 199 for (i = 0; i < mntsize; i++) { 200 width = strlen(mntbuf[i].f_mntfromname); 201 if (width > maxwidth) 202 maxwidth = width; 203 } 204 for (i = 0; i < mntsize; i++) 205 prtstat(&mntbuf[i], maxwidth); 206 exit(0); 207 /* NOTREACHED */ 208 } 209 210 char * 211 getmntpt(name) 212 char *name; 213 { 214 long mntsize, i; 215 struct statfs *mntbuf; 216 217 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 218 for (i = 0; i < mntsize; i++) { 219 if (!strcmp(mntbuf[i].f_mntfromname, name)) 220 return (mntbuf[i].f_mntonname); 221 } 222 return (0); 223 } 224 225 static enum { IN_LIST, NOT_IN_LIST } which; 226 227 int 228 selected(type) 229 const char *type; 230 { 231 char **av; 232 233 /* If no type specified, it's always selected. */ 234 if (typelist == NULL) 235 return (1); 236 for (av = typelist; *av != NULL; ++av) 237 if (!strncmp(type, *av, MFSNAMELEN)) 238 return (which == IN_LIST ? 1 : 0); 239 return (which == IN_LIST ? 0 : 1); 240 } 241 242 void 243 maketypelist(fslist) 244 char *fslist; 245 { 246 int i; 247 char *nextcp, **av; 248 249 if ((fslist == NULL) || (fslist[0] == '\0')) 250 errx(1, "empty type list"); 251 252 /* 253 * XXX 254 * Note: the syntax is "noxxx,yyy" for no xxx's and 255 * no yyy's, not the more intuitive "noyyy,noyyy". 256 */ 257 if (fslist[0] == 'n' && fslist[1] == 'o') { 258 fslist += 2; 259 which = NOT_IN_LIST; 260 } else 261 which = IN_LIST; 262 263 /* Count the number of types. */ 264 for (i = 1, nextcp = fslist; 265 (nextcp = strchr(nextcp, ',')) != NULL; i++) 266 ++nextcp; 267 268 /* Build an array of that many types. */ 269 if ((av = typelist = malloc((i + 1) * sizeof(char *))) == NULL) 270 err(1, "can't allocate type array"); 271 av[0] = fslist; 272 for (i = 1, nextcp = fslist; 273 (nextcp = strchr(nextcp, ',')) != NULL; i++) { 274 *nextcp = '\0'; 275 av[i] = ++nextcp; 276 } 277 /* Terminate the array. */ 278 av[i] = NULL; 279 } 280 281 /* 282 * Make a pass over the filesystem info in ``mntbuf'' filtering out 283 * filesystem types not in ``fsmask'' and possibly re-stating to get 284 * current (not cached) info. Returns the new count of valid statfs bufs. 285 */ 286 long 287 regetmntinfo(mntbufp, mntsize) 288 struct statfs **mntbufp; 289 long mntsize; 290 { 291 int i, j; 292 struct statfs *mntbuf; 293 294 if (!lflag && typelist == NULL) 295 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 296 297 mntbuf = *mntbufp; 298 j = 0; 299 for (i = 0; i < mntsize; i++) { 300 if (!aflag && (mntbuf[i].f_flags & MNT_IGNORE) != 0) 301 continue; 302 if (lflag && (mntbuf[i].f_flags & MNT_LOCAL) == 0) 303 continue; 304 if (!selected(mntbuf[i].f_fstypename)) 305 continue; 306 if (nflag) 307 mntbuf[j] = mntbuf[i]; 308 else { 309 struct statfs layerbuf = mntbuf[i]; 310 (void)statfs(mntbuf[i].f_mntonname, &mntbuf[j]); 311 /* 312 * If the FS name changed, then new data is for 313 * a different layer and we don't want it. 314 */ 315 if(memcmp(layerbuf.f_mntfromname, 316 mntbuf[j].f_mntfromname, MNAMELEN)) 317 mntbuf[j] = layerbuf; 318 } 319 j++; 320 } 321 return (j); 322 } 323 324 /* 325 * Convert statfs returned filesystem size into BLOCKSIZE units. 326 * Attempts to avoid overflow for large filesystems. 327 */ 328 #define fsbtoblk(num, fsbs, bs) \ 329 (((fsbs) != 0 && (fsbs) < (bs)) ? \ 330 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) 331 332 /* 333 * Print out status about a filesystem. 334 */ 335 void 336 prtstat(sfsp, maxwidth) 337 struct statfs *sfsp; 338 int maxwidth; 339 { 340 static long blocksize; 341 static int headerlen, timesthrough; 342 static char *header; 343 long used, availblks, inodes; 344 static char *full = "100%"; 345 346 if (maxwidth < 11) 347 maxwidth = 11; 348 if (++timesthrough == 1) { 349 if (kflag) { 350 blocksize = 1024; 351 header = Pflag ? "1024-blocks" : "1K-blocks"; 352 headerlen = strlen(header); 353 } else 354 header = getbsize(&headerlen, &blocksize); 355 (void)printf("%-*.*s %s Used %9s Capacity", 356 maxwidth, maxwidth, "Filesystem", header, 357 Pflag ? "Available" : "Avail"); 358 if (iflag) 359 (void)printf(" iused ifree %%iused"); 360 (void)printf(" Mounted on\n"); 361 } 362 (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); 363 used = sfsp->f_blocks - sfsp->f_bfree; 364 availblks = sfsp->f_bavail + used; 365 (void)printf(" %*ld %8ld %9ld", headerlen, 366 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 367 fsbtoblk(used, sfsp->f_bsize, blocksize), 368 fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize)); 369 (void)printf("%9s", 370 availblks == 0 ? full : strpct((u_long)used, (u_long)availblks, 0)); 371 if (iflag) { 372 inodes = sfsp->f_files; 373 used = inodes - sfsp->f_ffree; 374 (void)printf(" %7ld %7ld %6s ", used, sfsp->f_ffree, 375 inodes == 0 ? full : strpct((u_long)used, (u_long)inodes, 0)); 376 } else 377 (void)printf(" "); 378 (void)printf(" %s\n", sfsp->f_mntonname); 379 } 380 381 /* 382 * This code constitutes the pre-system call Berkeley df code for extracting 383 * information from filesystem superblocks. 384 */ 385 #include <ufs/ufs/dinode.h> 386 #include <ufs/ffs/fs.h> 387 #include <errno.h> 388 #include <fstab.h> 389 390 union { 391 struct fs iu_fs; 392 char dummy[SBSIZE]; 393 } sb; 394 #define sblock sb.iu_fs 395 396 int rfd; 397 398 int 399 ufs_df(file, sfsp) 400 char *file; 401 struct statfs *sfsp; 402 { 403 char *mntpt; 404 static int synced; 405 406 if (synced++ == 0) 407 sync(); 408 409 if ((rfd = open(file, O_RDONLY)) < 0) { 410 warn("%s", file); 411 return (-1); 412 } 413 if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) { 414 (void)close(rfd); 415 return (-1); 416 } 417 sfsp->f_type = 0; 418 sfsp->f_flags = 0; 419 sfsp->f_bsize = sblock.fs_fsize; 420 sfsp->f_iosize = sblock.fs_bsize; 421 sfsp->f_blocks = sblock.fs_dsize; 422 sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag + 423 sblock.fs_cstotal.cs_nffree; 424 sfsp->f_bavail = ((int64_t)sblock.fs_dsize * (100 - sblock.fs_minfree) / 100) - 425 (sblock.fs_dsize - sfsp->f_bfree); 426 if (sfsp->f_bavail < 0) 427 sfsp->f_bavail = 0; 428 sfsp->f_files = sblock.fs_ncg * sblock.fs_ipg; 429 sfsp->f_ffree = sblock.fs_cstotal.cs_nifree; 430 sfsp->f_fsid.val[0] = 0; 431 sfsp->f_fsid.val[1] = 0; 432 if ((mntpt = getmntpt(file)) == 0) 433 mntpt = ""; 434 (void)memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN); 435 (void)memmove(&sfsp->f_mntfromname[0], file, MNAMELEN); 436 (void)strncpy(sfsp->f_fstypename, MOUNT_FFS, MFSNAMELEN); 437 (void)close(rfd); 438 return (0); 439 } 440 441 int 442 bread(off, buf, cnt) 443 off_t off; 444 void *buf; 445 int cnt; 446 { 447 int nr; 448 449 (void)lseek(rfd, off, SEEK_SET); 450 if ((nr = read(rfd, buf, cnt)) != cnt) { 451 /* Probably a dismounted disk if errno == EIO. */ 452 if (errno != EIO) 453 (void)fprintf(stderr, "\ndf: %qd: %s\n", 454 (long long)off, strerror(nr > 0 ? EIO : errno)); 455 return (0); 456 } 457 return (1); 458 } 459 460 void 461 usage() 462 { 463 extern char *__progname; 464 465 (void)fprintf(stderr, 466 "Usage: %s [-aiklnP] [-t type] [file | file_system ...]\n", 467 __progname); 468 exit(1); 469 /* NOTREACHED */ 470 } 471