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