1 /* 2 * Copyright (c) 1980, 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Robert Elz at The University of Melbourne. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. 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 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1980, 1990, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94"; 45 #endif /* not lint */ 46 47 /* 48 * Fix up / report on disk quotas & usage 49 */ 50 #include <sys/param.h> 51 #include <sys/stat.h> 52 53 #include <ufs/ufs/dinode.h> 54 #include <ufs/ufs/quota.h> 55 #include <ufs/ffs/fs.h> 56 57 #include <fcntl.h> 58 #include <fstab.h> 59 #include <pwd.h> 60 #include <grp.h> 61 #include <errno.h> 62 #include <unistd.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 67 char *qfname = QUOTAFILENAME; 68 char *qfextension[] = INITQFNAMES; 69 char *quotagroup = QUOTAGROUP; 70 71 union { 72 struct fs sblk; 73 char dummy[MAXBSIZE]; 74 } un; 75 #define sblock un.sblk 76 long dev_bsize = 1; 77 long maxino; 78 79 struct quotaname { 80 long flags; 81 char grpqfname[MAXPATHLEN + 1]; 82 char usrqfname[MAXPATHLEN + 1]; 83 }; 84 #define HASUSR 1 85 #define HASGRP 2 86 87 struct fileusage { 88 struct fileusage *fu_next; 89 u_long fu_curinodes; 90 u_long fu_curblocks; 91 u_long fu_id; 92 char fu_name[1]; 93 /* actually bigger */ 94 }; 95 #define FUHASH 1024 /* must be power of two */ 96 struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 97 98 int aflag; /* all file systems */ 99 int gflag; /* check group quotas */ 100 int uflag; /* check user quotas */ 101 int vflag; /* verbose */ 102 int fi; /* open disk file descriptor */ 103 u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 104 105 struct fileusage * 106 addid __P((u_long, int, char *)); 107 char *blockcheck __P((char *)); 108 void bread __P((daddr_t, char *, long)); 109 int chkquota __P((char *, char *, struct quotaname *)); 110 void err __P((const char *, ...)); 111 void freeinodebuf __P((void)); 112 struct dinode * 113 getnextinode __P((ino_t)); 114 int getquotagid __P((void)); 115 int hasquota __P((struct fstab *, int, char **)); 116 struct fileusage * 117 lookup __P((u_long, int)); 118 void *needchk __P((struct fstab *)); 119 int oneof __P((char *, char*[], int)); 120 void resetinodebuf __P((void)); 121 int update __P((char *, char *, int)); 122 void usage __P((void)); 123 124 int 125 main(argc, argv) 126 int argc; 127 char *argv[]; 128 { 129 register struct fstab *fs; 130 register struct passwd *pw; 131 register struct group *gr; 132 struct quotaname *auxdata; 133 int i, argnum, maxrun, errs; 134 long done = 0; 135 char ch, *name; 136 137 errs = maxrun = 0; 138 while ((ch = getopt(argc, argv, "aguvl:")) != EOF) { 139 switch(ch) { 140 case 'a': 141 aflag++; 142 break; 143 case 'g': 144 gflag++; 145 break; 146 case 'u': 147 uflag++; 148 break; 149 case 'v': 150 vflag++; 151 break; 152 case 'l': 153 maxrun = atoi(optarg); 154 break; 155 default: 156 usage(); 157 } 158 } 159 argc -= optind; 160 argv += optind; 161 if ((argc == 0 && !aflag) || (argc > 0 && aflag)) 162 usage(); 163 if (!gflag && !uflag) { 164 gflag++; 165 uflag++; 166 } 167 if (gflag) { 168 setgrent(); 169 while ((gr = getgrent()) != 0) 170 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 171 endgrent(); 172 } 173 if (uflag) { 174 setpwent(); 175 while ((pw = getpwent()) != 0) 176 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); 177 endpwent(); 178 } 179 if (aflag) 180 exit(checkfstab(1, maxrun, needchk, chkquota)); 181 if (setfsent() == 0) 182 err("%s: can't open", FSTAB); 183 while ((fs = getfsent()) != NULL) { 184 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 185 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && 186 (auxdata = needchk(fs)) && 187 (name = blockcheck(fs->fs_spec))) { 188 done |= 1 << argnum; 189 errs += chkquota(name, fs->fs_file, auxdata); 190 } 191 } 192 endfsent(); 193 for (i = 0; i < argc; i++) 194 if ((done & (1 << i)) == 0) 195 fprintf(stderr, "%s not found in %s\n", 196 argv[i], FSTAB); 197 exit(errs); 198 } 199 200 void 201 usage() 202 { 203 (void)fprintf(stderr, "usage:\t%s\n\t%s\n", 204 "quotacheck -a [-guv]", 205 "quotacheck [-guv] filesys ..."); 206 exit(1); 207 } 208 209 void * 210 needchk(fs) 211 register struct fstab *fs; 212 { 213 register struct quotaname *qnp; 214 char *qfnp; 215 216 if (strcmp(fs->fs_vfstype, "ufs") || 217 strcmp(fs->fs_type, FSTAB_RW)) 218 return (NULL); 219 if ((qnp = malloc(sizeof(*qnp))) == NULL) 220 err("%s", strerror(errno)); 221 qnp->flags = 0; 222 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 223 strcpy(qnp->grpqfname, qfnp); 224 qnp->flags |= HASGRP; 225 } 226 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 227 strcpy(qnp->usrqfname, qfnp); 228 qnp->flags |= HASUSR; 229 } 230 if (qnp->flags) 231 return (qnp); 232 free(qnp); 233 return (NULL); 234 } 235 236 /* 237 * Scan the specified filesystem to check quota(s) present on it. 238 */ 239 int 240 chkquota(fsname, mntpt, qnp) 241 char *fsname, *mntpt; 242 register struct quotaname *qnp; 243 { 244 register struct fileusage *fup; 245 register struct dinode *dp; 246 int cg, i, mode, errs = 0; 247 ino_t ino; 248 249 if ((fi = open(fsname, O_RDONLY, 0)) < 0) { 250 perror(fsname); 251 return (1); 252 } 253 if (vflag) { 254 (void)printf("*** Checking "); 255 if (qnp->flags & HASUSR) 256 (void)printf("%s%s", qfextension[USRQUOTA], 257 (qnp->flags & HASGRP) ? " and " : ""); 258 if (qnp->flags & HASGRP) 259 (void)printf("%s", qfextension[GRPQUOTA]); 260 (void)printf(" quotas for %s (%s)\n", fsname, mntpt); 261 } 262 sync(); 263 bread(SBOFF, (char *)&sblock, (long)SBSIZE); 264 dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 265 maxino = sblock.fs_ncg * sblock.fs_ipg; 266 resetinodebuf(); 267 for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) { 268 for (i = 0; i < sblock.fs_ipg; i++, ino++) { 269 if (ino < ROOTINO) 270 continue; 271 if ((dp = getnextinode(ino)) == NULL) 272 continue; 273 if ((mode = dp->di_mode & IFMT) == 0) 274 continue; 275 if (qnp->flags & HASGRP) { 276 fup = addid((u_long)dp->di_gid, GRPQUOTA, 277 (char *)0); 278 fup->fu_curinodes++; 279 if (mode == IFREG || mode == IFDIR || 280 mode == IFLNK) 281 fup->fu_curblocks += dp->di_blocks; 282 } 283 if (qnp->flags & HASUSR) { 284 fup = addid((u_long)dp->di_uid, USRQUOTA, 285 (char *)0); 286 fup->fu_curinodes++; 287 if (mode == IFREG || mode == IFDIR || 288 mode == IFLNK) 289 fup->fu_curblocks += dp->di_blocks; 290 } 291 } 292 } 293 freeinodebuf(); 294 if (qnp->flags & HASUSR) 295 errs += update(mntpt, qnp->usrqfname, USRQUOTA); 296 if (qnp->flags & HASGRP) 297 errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 298 close(fi); 299 return (errs); 300 } 301 302 /* 303 * Update a specified quota file. 304 */ 305 int 306 update(fsname, quotafile, type) 307 char *fsname, *quotafile; 308 register int type; 309 { 310 register struct fileusage *fup; 311 register FILE *qfi, *qfo; 312 register u_long id, lastid; 313 struct dqblk dqbuf; 314 static int warned = 0; 315 static struct dqblk zerodqbuf; 316 static struct fileusage zerofileusage; 317 318 if ((qfo = fopen(quotafile, "r+")) == NULL) { 319 if (errno == ENOENT) 320 qfo = fopen(quotafile, "w+"); 321 if (qfo) { 322 (void) fprintf(stderr, 323 "quotacheck: creating quota file %s\n", quotafile); 324 #define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 325 (void) fchown(fileno(qfo), getuid(), getquotagid()); 326 (void) fchmod(fileno(qfo), MODE); 327 } else { 328 (void) fprintf(stderr, 329 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 330 return (1); 331 } 332 } 333 if ((qfi = fopen(quotafile, "r")) == NULL) { 334 (void) fprintf(stderr, 335 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 336 (void) fclose(qfo); 337 return (1); 338 } 339 if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 && 340 errno == EOPNOTSUPP && !warned && vflag) { 341 warned++; 342 (void)printf("*** Warning: %s\n", 343 "Quotas are not compiled into this kernel"); 344 } 345 for (lastid = highid[type], id = 0; id <= lastid; id++) { 346 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 347 dqbuf = zerodqbuf; 348 if ((fup = lookup(id, type)) == 0) 349 fup = &zerofileusage; 350 if (dqbuf.dqb_curinodes == fup->fu_curinodes && 351 dqbuf.dqb_curblocks == fup->fu_curblocks) { 352 fup->fu_curinodes = 0; 353 fup->fu_curblocks = 0; 354 fseek(qfo, (long)sizeof(struct dqblk), 1); 355 continue; 356 } 357 if (vflag) { 358 if (aflag) 359 printf("%s: ", fsname); 360 printf("%-8s fixed:", fup->fu_name); 361 if (dqbuf.dqb_curinodes != fup->fu_curinodes) 362 (void)printf("\tinodes %d -> %d", 363 dqbuf.dqb_curinodes, fup->fu_curinodes); 364 if (dqbuf.dqb_curblocks != fup->fu_curblocks) 365 (void)printf("\tblocks %d -> %d", 366 dqbuf.dqb_curblocks, fup->fu_curblocks); 367 (void)printf("\n"); 368 } 369 /* 370 * Reset time limit if have a soft limit and were 371 * previously under it, but are now over it. 372 */ 373 if (dqbuf.dqb_bsoftlimit && 374 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 375 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 376 dqbuf.dqb_btime = 0; 377 if (dqbuf.dqb_isoftlimit && 378 dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 379 fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 380 dqbuf.dqb_itime = 0; 381 dqbuf.dqb_curinodes = fup->fu_curinodes; 382 dqbuf.dqb_curblocks = fup->fu_curblocks; 383 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 384 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 385 (caddr_t)&dqbuf); 386 fup->fu_curinodes = 0; 387 fup->fu_curblocks = 0; 388 } 389 fclose(qfi); 390 fflush(qfo); 391 ftruncate(fileno(qfo), 392 (off_t)((highid[type] + 1) * sizeof(struct dqblk))); 393 fclose(qfo); 394 return (0); 395 } 396 397 /* 398 * Check to see if target appears in list of size cnt. 399 */ 400 int 401 oneof(target, list, cnt) 402 register char *target, *list[]; 403 int cnt; 404 { 405 register int i; 406 407 for (i = 0; i < cnt; i++) 408 if (strcmp(target, list[i]) == 0) 409 return (i); 410 return (-1); 411 } 412 413 /* 414 * Determine the group identifier for quota files. 415 */ 416 int 417 getquotagid() 418 { 419 struct group *gr; 420 421 if (gr = getgrnam(quotagroup)) 422 return (gr->gr_gid); 423 return (-1); 424 } 425 426 /* 427 * Check to see if a particular quota is to be enabled. 428 */ 429 int 430 hasquota(fs, type, qfnamep) 431 register struct fstab *fs; 432 int type; 433 char **qfnamep; 434 { 435 register char *opt; 436 char *cp; 437 static char initname, usrname[100], grpname[100]; 438 static char buf[BUFSIZ]; 439 440 if (!initname) { 441 (void)snprintf(usrname, sizeof(usrname), 442 "%s%s", qfextension[USRQUOTA], qfname); 443 (void)snprintf(grpname, sizeof(grpname), 444 "%s%s", qfextension[GRPQUOTA], qfname); 445 initname = 1; 446 } 447 strcpy(buf, fs->fs_mntops); 448 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 449 if (cp = index(opt, '=')) 450 *cp++ = '\0'; 451 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 452 break; 453 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 454 break; 455 } 456 if (!opt) 457 return (0); 458 if (cp) 459 *qfnamep = cp; 460 else { 461 (void)snprintf(buf, sizeof(buf), 462 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 463 *qfnamep = buf; 464 } 465 return (1); 466 } 467 468 /* 469 * Routines to manage the file usage table. 470 * 471 * Lookup an id of a specific type. 472 */ 473 struct fileusage * 474 lookup(id, type) 475 u_long id; 476 int type; 477 { 478 register struct fileusage *fup; 479 480 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 481 if (fup->fu_id == id) 482 return (fup); 483 return (NULL); 484 } 485 486 /* 487 * Add a new file usage id if it does not already exist. 488 */ 489 struct fileusage * 490 addid(id, type, name) 491 u_long id; 492 int type; 493 char *name; 494 { 495 struct fileusage *fup, **fhp; 496 int len; 497 498 if (fup = lookup(id, type)) 499 return (fup); 500 if (name) 501 len = strlen(name); 502 else 503 len = 10; 504 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 505 err("%s", strerror(errno)); 506 fhp = &fuhead[type][id & (FUHASH - 1)]; 507 fup->fu_next = *fhp; 508 *fhp = fup; 509 fup->fu_id = id; 510 if (id > highid[type]) 511 highid[type] = id; 512 if (name) 513 bcopy(name, fup->fu_name, len + 1); 514 else 515 (void)sprintf(fup->fu_name, "%u", id); 516 return (fup); 517 } 518 519 /* 520 * Special purpose version of ginode used to optimize pass 521 * over all the inodes in numerical order. 522 */ 523 ino_t nextino, lastinum; 524 long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 525 struct dinode *inodebuf; 526 #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 527 528 struct dinode * 529 getnextinode(inumber) 530 ino_t inumber; 531 { 532 long size; 533 daddr_t dblk; 534 static struct dinode *dp; 535 536 if (inumber != nextino++ || inumber > maxino) 537 err("bad inode number %d to nextinode", inumber); 538 if (inumber >= lastinum) { 539 readcnt++; 540 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 541 if (readcnt % readpercg == 0) { 542 size = partialsize; 543 lastinum += partialcnt; 544 } else { 545 size = inobufsize; 546 lastinum += fullcnt; 547 } 548 bread(dblk, (char *)inodebuf, size); 549 dp = inodebuf; 550 } 551 return (dp++); 552 } 553 554 /* 555 * Prepare to scan a set of inodes. 556 */ 557 void 558 resetinodebuf() 559 { 560 561 nextino = 0; 562 lastinum = 0; 563 readcnt = 0; 564 inobufsize = blkroundup(&sblock, INOBUFSIZE); 565 fullcnt = inobufsize / sizeof(struct dinode); 566 readpercg = sblock.fs_ipg / fullcnt; 567 partialcnt = sblock.fs_ipg % fullcnt; 568 partialsize = partialcnt * sizeof(struct dinode); 569 if (partialcnt != 0) { 570 readpercg++; 571 } else { 572 partialcnt = fullcnt; 573 partialsize = inobufsize; 574 } 575 if (inodebuf == NULL && 576 (inodebuf = malloc((u_int)inobufsize)) == NULL) 577 err("%s", strerror(errno)); 578 while (nextino < ROOTINO) 579 getnextinode(nextino); 580 } 581 582 /* 583 * Free up data structures used to scan inodes. 584 */ 585 void 586 freeinodebuf() 587 { 588 589 if (inodebuf != NULL) 590 free(inodebuf); 591 inodebuf = NULL; 592 } 593 594 /* 595 * Read specified disk blocks. 596 */ 597 void 598 bread(bno, buf, cnt) 599 daddr_t bno; 600 char *buf; 601 long cnt; 602 { 603 604 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || 605 read(fi, buf, cnt) != cnt) 606 err("block %ld", bno); 607 } 608 609 #if __STDC__ 610 #include <stdarg.h> 611 #else 612 #include <varargs.h> 613 #endif 614 615 void 616 #if __STDC__ 617 err(const char *fmt, ...) 618 #else 619 err(fmt, va_alist) 620 char *fmt; 621 va_dcl 622 #endif 623 { 624 va_list ap; 625 #if __STDC__ 626 va_start(ap, fmt); 627 #else 628 va_start(ap); 629 #endif 630 (void)fprintf(stderr, "quotacheck: "); 631 (void)vfprintf(stderr, fmt, ap); 632 va_end(ap); 633 (void)fprintf(stderr, "\n"); 634 exit(1); 635 /* NOTREACHED */ 636 } 637