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