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