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