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:  * Redistribution and use in source and binary forms, with or without
   9:  * modification, are permitted provided that the following conditions
  10:  * are met:
  11:  * 1. Redistributions of source code must retain the above copyright
  12:  *    notice, this list of conditions and the following disclaimer.
  13:  * 2. Redistributions in binary form must reproduce the above copyright
  14:  *    notice, this list of conditions and the following disclaimer in the
  15:  *    documentation and/or other materials provided with the distribution.
  16:  * 3. All advertising materials mentioning features or use of this software
  17:  *    must display the following acknowledgement:
  18:  *	This product includes software developed by the University of
  19:  *	California, Berkeley and its contributors.
  20:  * 4. Neither the name of the University nor the names of its contributors
  21:  *    may be used to endorse or promote products derived from this software
  22:  *    without specific prior written permission.
  23:  *
  24:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34:  * SUCH DAMAGE.
  35:  */
  36: 
  37: #if !defined(lint) && defined(DOSCCS)
  38: static char copyright[] =
  39: "@(#) Copyright (c) 1980, 1990, 1993\n\
  40: 	The Regents of the University of California.  All rights reserved.\n";
  41: 
  42: static char sccsid[] = "@(#)quotacheck.c	8.3.1 (2.11BSD) 1996/1/23";
  43: #endif /* not lint */
  44: 
  45: /*
  46:  * Fix up / report on disk quotas & usage
  47:  */
  48: #include <sys/param.h>
  49: #include <sys/stat.h>
  50: 
  51: #include <sys/inode.h>
  52: #include <sys/quota.h>
  53: #include <sys/fs.h>
  54: 
  55: #include <fcntl.h>
  56: #include <fstab.h>
  57: #include <pwd.h>
  58: #include <grp.h>
  59: #include <errno.h>
  60: #include <unistd.h>
  61: #include <stdio.h>
  62: #include <stdlib.h>
  63: #include <string.h>
  64: 
  65: char *qfname = QUOTAFILENAME;
  66: char *quotagroup = QUOTAGROUP;
  67: 
  68: union {
  69:     struct  fs  sblk;
  70:     char    dummy[MAXBSIZE];
  71: } un;
  72: #define sblock  un.sblk
  73: ino_t maxino;
  74: 
  75: struct quotaname {
  76:     char    flags;
  77:     char    usrqfname[MAXPATHLEN + 1];
  78: };
  79: #define HASUSR  1
  80: 
  81: struct fileusage {
  82:     struct  fileusage *fu_next;
  83:     ino_t   fu_curinodes;
  84:     u_long  fu_curblocks;
  85:     uid_t   fu_id;
  86:     char    fu_name[1];
  87:     /* actually bigger */
  88: };
  89: #define FUHASH 256  /* must be power of two */
  90: struct fileusage *fuhead[FUHASH];
  91: 
  92: int aflag;          /* all file systems */
  93: int vflag;          /* verbose */
  94: int fi;         /* open disk file descriptor */
  95: uid_t   highid;         /* highest addid()'ed identifier */
  96: 
  97: struct fileusage *addid();
  98: char    *blockcheck();
  99: void    bread();
 100: int chkquota();
 101: void    freeinodebuf();
 102: struct  dinode *getnextinode();
 103: gid_t    getquotagid();
 104: int  hasquota();
 105: struct fileusage *lookup();
 106: void    *needchk();
 107: int  oneof();
 108: void     resetinodebuf();
 109: int  update();
 110: void     usage();
 111: 
 112: int
 113: main(argc, argv)
 114:     int argc;
 115:     char *argv[];
 116: {
 117:     register struct fstab *fs;
 118:     register struct passwd *pw;
 119:     struct quotaname *auxdata;
 120:     int i, argnum, maxrun, errs, ch;
 121:     long done = 0;
 122:     char *name;
 123: 
 124:     errs = maxrun = 0;
 125:     while ((ch = getopt(argc, argv, "avl:")) != EOF) {
 126:         switch(ch) {
 127:         case 'a':
 128:             aflag++;
 129:             break;
 130:         case 'v':
 131:             vflag++;
 132:             break;
 133:         case 'l':
 134:             maxrun = atoi(optarg);
 135:             break;
 136:         default:
 137:             usage();
 138:         }
 139:     }
 140:     argc -= optind;
 141:     argv += optind;
 142:     if ((argc == 0 && !aflag) || (argc > 0 && aflag))
 143:         usage();
 144: 
 145:     setpwent();
 146:     while ((pw = getpwent()) != 0)
 147:         (void) addid(pw->pw_uid, pw->pw_name);
 148:     endpwent();
 149: 
 150:     if (aflag)
 151:         exit(checkfstab(1, maxrun, needchk, chkquota));
 152:     if (setfsent() == 0)
 153:         err(1, "can't open %s", FSTAB);
 154:     while ((fs = getfsent()) != NULL) {
 155:         if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
 156:             (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
 157:             (auxdata = (struct quotaname *)needchk(fs)) &&
 158:             (name = blockcheck(fs->fs_spec))) {
 159:             done |= 1 << argnum;
 160:             errs += chkquota(name, fs->fs_file, auxdata);
 161:         }
 162:     }
 163:     endfsent();
 164:     for (i = 0; i < argc; i++)
 165:         if ((done & (1 << i)) == 0)
 166:             warn("%s not found in %s", argv[i], FSTAB);
 167:     exit(errs);
 168: }
 169: 
 170: void
 171: usage()
 172: {
 173:     (void)err(1, "usage:\t%s\n\t%s\n",
 174:         "quotacheck -a [-v] [-l num]",
 175:         "quotacheck [-v] filesys ...");
 176: /* NOTREACHED */
 177: }
 178: 
 179: void *
 180: needchk(fs)
 181:     register struct fstab *fs;
 182: {
 183:     register struct quotaname *qnp;
 184:     char *qfnp;
 185: 
 186:     if (strcmp(fs->fs_vfstype, "ufs") ||
 187:         (strcmp(fs->fs_type, FSTAB_RW) && strcmp(fs->fs_type, FSTAB_RQ)))
 188:         return ((void *)NULL);
 189:     if ((qnp = (struct quotaname *)malloc(sizeof(*qnp))) == NULL)
 190:         nomem();
 191:     qnp->flags = 0;
 192:     if (hasquota(fs, &qfnp)) {
 193:         strcpy(qnp->usrqfname, qfnp);
 194:         qnp->flags |= HASUSR;
 195:     }
 196:     if (qnp->flags)
 197:         return ((void *)qnp);
 198:     free(qnp);
 199:     return ((void *)NULL);
 200: }
 201: 
 202: /*
 203:  * Scan the specified filesystem to check quota(s) present on it.
 204:  */
 205: int
 206: chkquota(fsname, mntpt, qnp)
 207:     char *fsname, *mntpt;
 208:     register struct quotaname *qnp;
 209: {
 210:     register struct fileusage *fup;
 211:     register struct dinode *dp;
 212:     int mode, errs = 0;
 213:     ino_t ino;
 214: 
 215:     if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
 216:         perror(fsname);
 217:         return (1);
 218:     }
 219:     if (vflag)
 220:         (void)printf("*** Checking quotas for %s (%s)\n",
 221:             fsname, mntpt);
 222:     sync();
 223:     bread(SBLOCK, (char *)&sblock, SBSIZE);
 224:     maxino = (sblock.fs_isize - 2) * INOPB;
 225:     resetinodebuf();
 226:     for (ino = ROOTINO; ino < maxino; ino++) {
 227:         if ((dp = getnextinode(ino)) == NULL)
 228:             continue;
 229:         if ((mode = dp->di_mode & IFMT) == 0)
 230:             continue;
 231:         if (qnp->flags & HASUSR) {
 232:             fup = addid(dp->di_uid, (char *)0);
 233:             fup->fu_curinodes++;
 234:             if (mode == IFREG || mode == IFDIR || mode == IFLNK)
 235:                 fup->fu_curblocks += dp->di_size;
 236:         }
 237:     }
 238:     freeinodebuf();
 239:     if (qnp->flags & HASUSR)
 240:         errs += update(fsname, mntpt, qnp->usrqfname);
 241:     close(fi);
 242:     return (errs);
 243: }
 244: 
 245: /*
 246:  * Update a specified quota file.
 247:  */
 248: int
 249: update(fsdev, fsname, quotafile)
 250:     char *fsdev, *fsname, *quotafile;
 251: {
 252:     register struct fileusage *fup;
 253:     register FILE *qfi, *qfo;
 254:     uid_t id, lastid;
 255:     struct dqblk dqbuf;
 256:     struct stat  statb;
 257:     struct dqusage dqu;
 258:     dev_t  quotadev;
 259:     static int warned = 0;
 260:     static struct dqblk zerodqbuf;
 261:     static struct fileusage zerofileusage;
 262: 
 263:     if ((qfo = fopen(quotafile, "r+")) == NULL) {
 264:         if (errno == ENOENT)
 265:             qfo = fopen(quotafile, "w+");
 266:         if (qfo) {
 267:             (void) warn("creating quota file %s", quotafile);
 268:             (void) fchown(fileno(qfo), getuid(), getquotagid());
 269:             (void) fchmod(fileno(qfo), 0640);
 270:         } else {
 271:             (void) warn("%s: %s", quotafile, strerror(errno));
 272:             return (1);
 273:         }
 274:     }
 275:     if ((qfi = fopen(quotafile, "r")) == NULL) {
 276:         (void) warn("%s: %s", quotafile, strerror(errno));
 277:         (void) fclose(qfo);
 278:         return (1);
 279:     }
 280: /*
 281:  * We check that the quota file resides in the filesystem being checked.  This
 282:  * restriction is imposed by the kernel for now.
 283: */
 284:     fstat(fileno(qfi), &statb);
 285:     quotadev = statb.st_dev;
 286:     if (stat(unrawname(fsdev), &statb) < 0) {
 287:         warn("Can't stat %s", fsname);
 288:         fclose(qfi);
 289:         fclose(qfo);
 290:         return(1);
 291:     }
 292:     if (quotadev != statb.st_rdev)  {
 293:         warn("%s dev (0x%x) mismatch %s dev (0x%x)", quotafile,
 294:             quotadev, fsname, statb.st_rdev);
 295:         fclose(qfi);
 296:         fclose(qfo);
 297:         return(1);
 298:     }
 299:     if (quota(Q_SYNC, 0, quotadev, 0) < 0 &&
 300:         errno == EOPNOTSUPP && !warned && vflag) {
 301:         warned++;
 302:         (void)printf("*** Warning: %s\n",
 303:             "Quotas are not compiled into this kernel");
 304:     }
 305:     for (lastid = highid, id = 0; id <= lastid; id++) {
 306:         if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
 307:             dqbuf = zerodqbuf;
 308:         if ((fup = lookup(id)) == 0)
 309:             fup = &zerofileusage;
 310:         if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
 311:             dqbuf.dqb_curblocks == fup->fu_curblocks) {
 312:             fup->fu_curinodes = 0;
 313:             fup->fu_curblocks = 0;
 314:             fseek(qfo, (long)sizeof(struct dqblk), 1);
 315:             continue;
 316:         }
 317:         if (vflag) {
 318:             if (aflag)
 319:                 printf("%s: ", fsname);
 320:             printf("%-8s fixed:", fup->fu_name);
 321:             if (dqbuf.dqb_curinodes != fup->fu_curinodes)
 322:                 (void)printf("\tinodes %u -> %u",
 323:                     dqbuf.dqb_curinodes, fup->fu_curinodes);
 324:             if (dqbuf.dqb_curblocks != fup->fu_curblocks)
 325:                 (void)printf("\tbytes %ld -> %ld",
 326:                     dqbuf.dqb_curblocks, fup->fu_curblocks);
 327:             (void)printf("\n");
 328:         }
 329:         dqbuf.dqb_curinodes = fup->fu_curinodes;
 330:         dqbuf.dqb_curblocks = fup->fu_curblocks;
 331:         fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
 332: /*
 333:  * Need to convert from bytes to blocks for system call interface.
 334:  */
 335:         dqu.du_curblocks = btodb(fup->fu_curblocks);
 336:         dqu.du_curinodes = fup->fu_curinodes;
 337:         (void) quota(Q_SETDUSE, id, quotadev, &dqu);
 338:         fup->fu_curinodes = 0;
 339:         fup->fu_curblocks = 0;
 340:     }
 341:     fclose(qfi);
 342:     fflush(qfo);
 343:     ftruncate(fileno(qfo), (off_t)(highid + 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: int
 352: oneof(target, list, cnt)
 353:     register char *target, *list[];
 354:     int cnt;
 355: {
 356:     register int i;
 357: 
 358:     for (i = 0; i < cnt; i++)
 359:         if (strcmp(target, list[i]) == 0)
 360:             return (i);
 361:     return (-1);
 362: }
 363: 
 364: /*
 365:  * Determine the group identifier for quota files.
 366:  */
 367: gid_t
 368: getquotagid()
 369: {
 370:     struct group *gr;
 371: 
 372:     if (gr = getgrnam(quotagroup))
 373:         return (gr->gr_gid);
 374:     return (-1);
 375: }
 376: 
 377: /*
 378:  * Check to see if a particular quota is to be enabled.
 379:  */
 380: hasquota(fs, qfnamep)
 381:     register struct fstab *fs;
 382:     char **qfnamep;
 383: {
 384:     register char *opt;
 385:     char *cp;
 386:     static char initname, usrname[100];
 387:     static char buf[BUFSIZ];
 388: 
 389:     if (!initname) {
 390:         strcpy(usrname, 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 (strcmp(opt, usrname) == 0)
 398:             break;
 399:         if (strcmp(opt, FSTAB_RQ) == 0) /* XXX compatibility */
 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", fs->fs_file, qfname);
 409:     *qfnamep = buf;
 410:     return (1);
 411: }
 412: 
 413: /*
 414:  * Routines to manage the file usage table.
 415:  *
 416:  * Lookup an id.
 417:  */
 418: struct fileusage *
 419: lookup(id)
 420:     uid_t id;
 421: {
 422:     register struct fileusage *fup;
 423: 
 424:     for (fup = fuhead[id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
 425:         if (fup->fu_id == id)
 426:             return (fup);
 427:     return (NULL);
 428: }
 429: 
 430: /*
 431:  * Add a new file usage id if it does not already exist.
 432:  */
 433: struct fileusage *
 434: addid(id, name)
 435:     uid_t id;
 436:     char *name;
 437: {
 438:     register struct fileusage *fup, **fhp;
 439:     int len;
 440: 
 441:     if (fup = lookup(id))
 442:         return (fup);
 443:     if (name)
 444:         len = strlen(name);
 445:     else
 446:         len = 10;
 447:     if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL)
 448:         err("%s", strerror(errno));
 449:     fhp = &fuhead[id & (FUHASH - 1)];
 450:     fup->fu_next = *fhp;
 451:     *fhp = fup;
 452:     fup->fu_id = id;
 453:     if (id > highid)
 454:         highid = id;
 455:     if (name)
 456:         bcopy(name, fup->fu_name, len + 1);
 457:     else
 458:         (void)sprintf(fup->fu_name, "%u", id);
 459:     return (fup);
 460: }
 461: 
 462: /*
 463:  * Special purpose version of ginode used to optimize pass
 464:  * over all the inodes in numerical order.
 465:  */
 466: ino_t nextino;
 467: long lastinum;
 468: int inobufsize;
 469: struct dinode *inodebuf;
 470: #define INOBUFSIZE  128 /* number of inodes to read at a time */
 471: 
 472: struct dinode *
 473: getnextinode(inumber)
 474:     ino_t inumber;
 475: {
 476:     daddr_t dblk;
 477:     static struct dinode *dp;
 478: 
 479:     if (inumber != nextino++ || inumber >= maxino)
 480:         err(1, "bad inode number %d to nextinode", inumber);
 481:     if (inumber > lastinum) {
 482:         dblk = itod(inumber);
 483:         lastinum += INOBUFSIZE;
 484:         bread(dblk, (char *)inodebuf, inobufsize);
 485:         dp = inodebuf;
 486:     }
 487:     return (dp++);
 488: }
 489: 
 490: /*
 491:  * Prepare to scan a set of inodes.
 492:  */
 493: void
 494: resetinodebuf()
 495: {
 496: 
 497:     nextino = 1;
 498:     lastinum = 0;
 499:     inobufsize = INOBUFSIZE * sizeof (struct dinode);
 500:     if (inodebuf == NULL &&
 501:         (inodebuf = (struct dinode *)malloc(inobufsize)) == NULL)
 502:         nomem();
 503:     while (nextino < ROOTINO)
 504:         getnextinode(nextino);
 505: }
 506: 
 507: /*
 508:  * Free up data structures used to scan inodes.
 509:  */
 510: void
 511: freeinodebuf()
 512: {
 513: 
 514:     if (inodebuf != NULL)
 515:         free(inodebuf);
 516:     inodebuf = NULL;
 517: }
 518: 
 519: /*
 520:  * Read specified disk blocks.
 521:  */
 522: void
 523: bread(bno, buf, cnt)
 524:     daddr_t bno;
 525:     char *buf;
 526:     int cnt;
 527: {
 528: 
 529:     if (lseek(fi, (off_t)bno * DEV_BSIZE, 0) == -1 ||
 530:         read(fi, buf, cnt) != cnt)
 531:         err(1, "block %ld", bno);
 532: }

Defined functions

addid defined in line 433; used 3 times
bread defined in line 522; used 3 times
chkquota defined in line 205; used 3 times
freeinodebuf defined in line 510; used 2 times
getnextinode defined in line 472; used 3 times
getquotagid defined in line 367; used 2 times
hasquota defined in line 380; used 2 times
lookup defined in line 418; used 3 times
main defined in line 112; never used
needchk defined in line 179; used 3 times
oneof defined in line 351; used 3 times
resetinodebuf defined in line 493; used 2 times
update defined in line 248; used 2 times
usage defined in line 170; used 3 times

Defined variables

aflag defined in line 92; used 5 times
copyright defined in line 38; never used
fi defined in line 94; used 4 times
fuhead defined in line 90; used 2 times
highid defined in line 95; used 4 times
inobufsize defined in line 468; used 3 times
inodebuf defined in line 469; used 7 times
lastinum defined in line 467; used 3 times
maxino defined in line 73; used 3 times
nextino defined in line 466; used 4 times
qfname defined in line 65; used 2 times
quotagroup defined in line 66; used 1 times
sccsid defined in line 42; never used
vflag defined in line 93; used 4 times

Defined struct's

fileusage defined in line 81; used 24 times
quotaname defined in line 75; used 10 times

Defined macros

FUHASH defined in line 89; used 3 times
HASUSR defined in line 79; used 3 times
INOBUFSIZE defined in line 470; used 2 times
sblock defined in line 72; used 2 times
Last modified: 1996-01-25
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 5743
Valid CSS Valid XHTML 1.0 Strict