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