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