BSD 4_4_Lite2 release
[unix-history] / usr / src / sbin / quotacheck / quotacheck.c
index 4e48531..31c94d1 100644 (file)
+/*
+ * Copyright (c) 1980, 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
 #ifndef lint
 #ifndef lint
-static char sccsid[] = "@(#)quotacheck.c       4.4 (Berkeley, Melbourne) %G%";
-#endif
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1990, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)quotacheck.c       8.6 (Berkeley) 4/28/95";
+#endif /* not lint */
 
 /*
 
 /*
- * Fix up / report on disc quotas & usage
+ * Fix up / report on disk quotas & usage
  */
  */
-#include <stdio.h>
-#include <ctype.h>
-#include <signal.h>
 #include <sys/param.h>
 #include <sys/param.h>
-#include <sys/inode.h>
-#include <sys/fs.h>
-#include <sys/quota.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
+#include <sys/queue.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ffs/fs.h>
+
+#include <fcntl.h>
 #include <fstab.h>
 #include <pwd.h>
 #include <fstab.h>
 #include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+char *quotagroup = QUOTAGROUP;
 
 union {
        struct  fs      sblk;
        char    dummy[MAXBSIZE];
 } un;
 #define        sblock  un.sblk
 
 union {
        struct  fs      sblk;
        char    dummy[MAXBSIZE];
 } un;
 #define        sblock  un.sblk
+long dev_bsize;
+long maxino;
 
 
-#define        ITABSZ  256
-struct dinode  itab[ITABSZ];
-struct dinode  *dp;
-long   blocks;
-dev_t  dev;
+struct quotaname {
+       long    flags;
+       char    grpqfname[MAXPATHLEN + 1];
+       char    usrqfname[MAXPATHLEN + 1];
+};
+#define        HASUSR  1
+#define        HASGRP  2
 
 
-#define LOGINNAMESIZE 8
 struct fileusage {
 struct fileusage {
-       struct fileusage *fu_next;
-       struct dqusage fu_usage;
-       u_short fu_uid;
-       char fu_name[LOGINNAMESIZE + 1];
+       struct  fileusage *fu_next;
+       u_long  fu_curinodes;
+       u_long  fu_curblocks;
+       u_long  fu_id;
+       char    fu_name[1];
+       /* actually bigger */
 };
 };
-#define FUHASH 997
-struct fileusage *fuhead[FUHASH];
-struct fileusage *lookup();
-struct fileusage *adduid();
-int highuid;
-
-int fi;
-ino_t ino;
-long done;
-struct passwd  *getpwent();
-struct dinode  *ginode();
-char *malloc(), *makerawname();
-
-int    vflag;          /* verbose */
-int    aflag;          /* all file systems */
-
-char *qfname = "quotas";
-char quotafile[MAXPATHLEN + 1];
-struct dqblk zerodqbuf;
+#define FUHASH 1024    /* must be power of two */
+struct fileusage *fuhead[MAXQUOTAS][FUHASH];
+
+int    aflag;                  /* all file systems */
+int    gflag;                  /* check group quotas */
+int    uflag;                  /* check user quotas */
+int    vflag;                  /* verbose */
+int    fi;                     /* open disk file descriptor */
+u_long highid[MAXQUOTAS];      /* highest addid()'ed identifier per type */
+
+struct fileusage *
+        addid __P((u_long, int, char *));
+char   *blockcheck __P((char *));
+void    bread __P((daddr_t, char *, long));
+int     chkquota __P((char *, char *, struct quotaname *));
+void    freeinodebuf __P((void));
+struct dinode *
+        getnextinode __P((ino_t));
+int     getquotagid __P((void));
+int     hasquota __P((struct fstab *, int, char **));
+struct fileusage *
+        lookup __P((u_long, int));
+void   *needchk __P((struct fstab *));
+int     oneof __P((char *, char*[], int));
+void    resetinodebuf __P((void));
+int     update __P((char *, char *, int));
+void    usage __P((void));
 
 
+int
 main(argc, argv)
        int argc;
 main(argc, argv)
        int argc;
-       char **argv;
+       char *argv[];
 {
        register struct fstab *fs;
 {
        register struct fstab *fs;
-       register struct fileusage *fup;
        register struct passwd *pw;
        register struct passwd *pw;
-       int i, errs = 0;
+       register struct group *gr;
+       struct quotaname *auxdata;
+       int i, argnum, maxrun, errs;
+       long done = 0;
+       char ch, *name;
 
 
-again:
-       argc--, argv++;
-       if (argc > 0 && strcmp(*argv, "-v") == 0) {
-               vflag++;
-               goto again;
+       errs = maxrun = 0;
+       while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
+               switch(ch) {
+               case 'a':
+                       aflag++;
+                       break;
+               case 'g':
+                       gflag++;
+                       break;
+               case 'u':
+                       uflag++;
+                       break;
+               case 'v':
+                       vflag++;
+                       break;
+               case 'l':
+                       maxrun = atoi(optarg);
+                       break;
+               default:
+                       usage();
+               }
        }
        }
-       if (argc > 0 && strcmp(*argv, "-a") == 0) {
-               aflag++;
-               goto again;
+       argc -= optind;
+       argv += optind;
+       if ((argc == 0 && !aflag) || (argc > 0 && aflag))
+               usage();
+       if (!gflag && !uflag) {
+               gflag++;
+               uflag++;
        }
        }
-       if (argc <= 0 && !aflag) {
-               fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
-                       "quotacheck [-v] -a",
-                       "quotacheck [-v] filesys ...");
-               exit(1);
+       if (gflag) {
+               setgrent();
+               while ((gr = getgrent()) != 0)
+                       (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+               endgrent();
        }
        }
-       if (vflag) {
+       if (uflag) {
                setpwent();
                setpwent();
-               while ((pw = getpwent()) != 0) {
-                       fup = lookup(pw->pw_uid);
-                       if (fup == 0)
-                               fup = adduid(pw->pw_uid);
-                       strncpy(fup->fu_name, pw->pw_name,
-                               sizeof(fup->fu_name));
-               }
+               while ((pw = getpwent()) != 0)
+                       (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
                endpwent();
        }
                endpwent();
        }
-       setfsent();
+       if (aflag)
+               exit(checkfstab(1, maxrun, needchk, chkquota));
+       if (setfsent() == 0)
+               err(1, "%s: can't open", FSTAB);
        while ((fs = getfsent()) != NULL) {
        while ((fs = getfsent()) != NULL) {
-               if (aflag &&
-                   (fs->fs_type == 0 || strcmp(fs->fs_type, "rq") != 0))
-                       continue;
-               if (!aflag &&
-                   !(oneof(fs->fs_file, argv, argc) ||
-                     oneof(fs->fs_spec, argv, argc)))
-                       continue;
-               (void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname);
-               errs += chkquota(fs->fs_spec, quotafile);
+               if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+                   (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
+                   (auxdata = needchk(fs)) &&
+                   (name = blockcheck(fs->fs_spec))) {
+                       done |= 1 << argnum;
+                       errs += chkquota(name, fs->fs_file, auxdata);
+               }
        }
        endfsent();
        for (i = 0; i < argc; i++)
                if ((done & (1 << i)) == 0)
        }
        endfsent();
        for (i = 0; i < argc; i++)
                if ((done & (1 << i)) == 0)
-                       fprintf(stderr, "%s not found in /etc/fstab\n",
-                               argv[i]);
+                       fprintf(stderr, "%s not found in %s\n",
+                               argv[i], FSTAB);
        exit(errs);
 }
 
        exit(errs);
 }
 
-chkquota(fsdev, qffile)
-       char *fsdev;
-       char *qffile;
+void
+usage()
+{
+       (void)fprintf(stderr, "usage:\t%s\n\t%s\n",
+               "quotacheck -a [-guv]",
+               "quotacheck [-guv] filesys ...");
+       exit(1);
+}
+
+void *
+needchk(fs)
+       register struct fstab *fs;
+{
+       register struct quotaname *qnp;
+       char *qfnp;
+
+       if (strcmp(fs->fs_vfstype, "ufs") ||
+           strcmp(fs->fs_type, FSTAB_RW))
+               return (NULL);
+       if ((qnp = malloc(sizeof(*qnp))) == NULL)
+               err(1, "%s", strerror(errno));
+       qnp->flags = 0;
+       if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
+               strcpy(qnp->grpqfname, qfnp);
+               qnp->flags |= HASGRP;
+       }
+       if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
+               strcpy(qnp->usrqfname, qfnp);
+               qnp->flags |= HASUSR;
+       }
+       if (qnp->flags)
+               return (qnp);
+       free(qnp);
+       return (NULL);
+}
+
+/*
+ * Scan the specified filesystem to check quota(s) present on it.
+ */
+int
+chkquota(fsname, mntpt, qnp)
+       char *fsname, *mntpt;
+       register struct quotaname *qnp;
 {
        register struct fileusage *fup;
 {
        register struct fileusage *fup;
-       dev_t quotadev;
-       FILE *qf;
-       u_short uid;
-       int cg, i;
-       char *rawdisk;
-       struct stat statb;
-       struct dqblk dqbuf;
+       register struct dinode *dp;
+       int cg, i, mode, errs = 0;
+       ino_t ino;
 
 
-       rawdisk = makerawname(fsdev);
-       if (vflag)
-               fprintf(stdout, "*** Check quotas for %s\n", rawdisk);
-       fi = open(rawdisk, 0);
-       if (fi < 0) {
-               perror(rawdisk);
+       if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
+               perror(fsname);
                return (1);
        }
                return (1);
        }
-       qf = fopen(qffile, "r+");
-       if (qf == NULL) {
-               perror(qffile);
-               return (1);
+       if (vflag) {
+               (void)printf("*** Checking ");
+               if (qnp->flags & HASUSR)
+                       (void)printf("%s%s", qfextension[USRQUOTA],
+                           (qnp->flags & HASGRP) ? " and " : "");
+               if (qnp->flags & HASGRP)
+                       (void)printf("%s", qfextension[GRPQUOTA]);
+               (void)printf(" quotas for %s (%s)\n", fsname, mntpt);
        }
        }
-       if (fstat(fileno(qf), &statb) < 0) {
-               perror(qffile);
-               return (1);
+       sync();
+       dev_bsize = 1;
+       bread(SBOFF, (char *)&sblock, (long)SBSIZE);
+       dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+       maxino = sblock.fs_ncg * sblock.fs_ipg;
+       resetinodebuf();
+       for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) {
+               for (i = 0; i < sblock.fs_ipg; i++, ino++) {
+                       if (ino < ROOTINO)
+                               continue;
+                       if ((dp = getnextinode(ino)) == NULL)
+                               continue;
+                       if ((mode = dp->di_mode & IFMT) == 0)
+                               continue;
+                       if (qnp->flags & HASGRP) {
+                               fup = addid((u_long)dp->di_gid, GRPQUOTA,
+                                   (char *)0);
+                               fup->fu_curinodes++;
+                               if (mode == IFREG || mode == IFDIR ||
+                                   mode == IFLNK)
+                                       fup->fu_curblocks += dp->di_blocks;
+                       }
+                       if (qnp->flags & HASUSR) {
+                               fup = addid((u_long)dp->di_uid, USRQUOTA,
+                                   (char *)0);
+                               fup->fu_curinodes++;
+                               if (mode == IFREG || mode == IFDIR ||
+                                   mode == IFLNK)
+                                       fup->fu_curblocks += dp->di_blocks;
+                       }
+               }
        }
        }
-       quotadev = statb.st_dev;
-       if (stat(fsdev, &statb) < 0) {
-               perror(fsdev);
-               return (1);
+       freeinodebuf();
+       if (qnp->flags & HASUSR)
+               errs += update(mntpt, qnp->usrqfname, USRQUOTA);
+       if (qnp->flags & HASGRP)
+               errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
+       close(fi);
+       return (errs);
+}
+
+/*
+ * Update a specified quota file.
+ */
+int
+update(fsname, quotafile, type)
+       char *fsname, *quotafile;
+       register int type;
+{
+       register struct fileusage *fup;
+       register FILE *qfi, *qfo;
+       register u_long id, lastid;
+       struct dqblk dqbuf;
+       static int warned = 0;
+       static struct dqblk zerodqbuf;
+       static struct fileusage zerofileusage;
+
+       if ((qfo = fopen(quotafile, "r+")) == NULL) {
+               if (errno == ENOENT)
+                       qfo = fopen(quotafile, "w+");
+               if (qfo) {
+                       (void) fprintf(stderr,
+                           "quotacheck: creating quota file %s\n", quotafile);
+#define        MODE    (S_IRUSR|S_IWUSR|S_IRGRP)
+                       (void) fchown(fileno(qfo), getuid(), getquotagid());
+                       (void) fchmod(fileno(qfo), MODE);
+               } else {
+                       (void) fprintf(stderr,
+                           "quotacheck: %s: %s\n", quotafile, strerror(errno));
+                       return (1);
+               }
        }
        }
-       if (quotadev != statb.st_rdev) {
-               fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
-                       qffile, quotadev, fsdev, statb.st_rdev);
+       if ((qfi = fopen(quotafile, "r")) == NULL) {
+               (void) fprintf(stderr,
+                   "quotacheck: %s: %s\n", quotafile, strerror(errno));
+               (void) fclose(qfo);
                return (1);
        }
                return (1);
        }
-       quota(Q_SYNC, 0, quotadev, 0);
-       sync();
-       bread(SBLOCK, (char *)&sblock, SBSIZE);
-       ino = 0;
-       for (cg = 0; cg < sblock.fs_ncg; cg++) {
-               dp = NULL;
-               for (i = 0; i < sblock.fs_ipg; i++)
-                       acct(ginode());
+       if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
+           errno == EOPNOTSUPP && !warned && vflag) {
+               warned++;
+               (void)printf("*** Warning: %s\n",
+                   "Quotas are not compiled into this kernel");
        }
        }
-       for (uid = 0; uid <= highuid; uid++) {
-               fup = lookup(uid);
-               if (fup == 0)
-                       continue;
-               fseek(qf, uid * sizeof(struct dqblk), 0);
-               i = fread(&dqbuf, sizeof(struct dqblk), 1, qf);
-               if (i == 0)
+       for (lastid = highid[type], id = 0; id <= lastid; id++) {
+               if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
                        dqbuf = zerodqbuf;
                        dqbuf = zerodqbuf;
-               if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
-                   dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
-                       fup->fu_usage.du_curinodes = 0;
-                       fup->fu_usage.du_curblocks = 0;
+               if ((fup = lookup(id, type)) == 0)
+                       fup = &zerofileusage;
+               if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
+                   dqbuf.dqb_curblocks == fup->fu_curblocks) {
+                       fup->fu_curinodes = 0;
+                       fup->fu_curblocks = 0;
+                       fseek(qfo, (long)sizeof(struct dqblk), 1);
                        continue;
                }
                if (vflag) {
                        continue;
                }
                if (vflag) {
-                       if (fup->fu_name[0] != '\0')
-                               printf("%-10s fixed:", fup->fu_name);
-                       else
-                               printf("#%-9d fixed:", uid);
-                       fprintf(stdout, " inodes (old %d, new %d)",
-                           dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
-                       fprintf(stdout, " blocks (old %d, new %d)\n",
-                           dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
+                       if (aflag)
+                               printf("%s: ", fsname);
+                       printf("%-8s fixed:", fup->fu_name);
+                       if (dqbuf.dqb_curinodes != fup->fu_curinodes)
+                               (void)printf("\tinodes %d -> %d",
+                                       dqbuf.dqb_curinodes, fup->fu_curinodes);
+                       if (dqbuf.dqb_curblocks != fup->fu_curblocks)
+                               (void)printf("\tblocks %d -> %d",
+                                       dqbuf.dqb_curblocks, fup->fu_curblocks);
+                       (void)printf("\n");
                }
                }
-               dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
-               dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
-               fseek(qf, uid * sizeof(struct dqblk), 0);
-               fwrite(&dqbuf, sizeof(struct dqblk), 1, qf);
-               quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
-               fup->fu_usage.du_curinodes = 0;
-               fup->fu_usage.du_curblocks = 0;
+               /*
+                * Reset time limit if have a soft limit and were
+                * previously under it, but are now over it.
+                */
+               if (dqbuf.dqb_bsoftlimit &&
+                   dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+                   fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
+                       dqbuf.dqb_btime = 0;
+               if (dqbuf.dqb_isoftlimit &&
+                   dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
+                   fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
+                       dqbuf.dqb_itime = 0;
+               dqbuf.dqb_curinodes = fup->fu_curinodes;
+               dqbuf.dqb_curblocks = fup->fu_curblocks;
+               fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
+               (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
+                   (caddr_t)&dqbuf);
+               fup->fu_curinodes = 0;
+               fup->fu_curblocks = 0;
        }
        }
+       fclose(qfi);
+       fflush(qfo);
+       ftruncate(fileno(qfo),
+           (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
+       fclose(qfo);
        return (0);
 }
 
        return (0);
 }
 
-acct(ip)
-       register struct dinode *ip;
-{
-       register n;
-       register struct fileusage *fup;
-
-       if (ip == NULL)
-               return;
-       if (ip->di_mode == 0)
-               return;
-       fup = lookup(ip->di_uid);
-       if (fup == 0)
-               fup = adduid(ip->di_uid);
-       fup->fu_usage.du_curinodes++;
-       if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
-               return;
-       fup->fu_usage.du_curblocks += ip->di_blocks;
-}
-
-oneof(target, list, n)
-       char *target, *list[];
-       register int n;
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+       register char *target, *list[];
+       int cnt;
 {
        register int i;
 
 {
        register int i;
 
-       for (i = 0; i < n; i++)
-               if (strcmp(target, list[i]) == 0) {
-                       done |= 1 << i;
-                       return (1);
-               }
-       return (0);
+       for (i = 0; i < cnt; i++)
+               if (strcmp(target, list[i]) == 0)
+                       return (i);
+       return (-1);
 }
 
 }
 
-struct dinode *
-ginode()
+/*
+ * Determine the group identifier for quota files.
+ */
+int
+getquotagid()
 {
 {
-       register unsigned long iblk;
+       struct group *gr;
 
 
-       if (dp == NULL || ++dp >= &itab[ITABSZ]) {
-               iblk = itod(&sblock, ino);
-               bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
-               dp = &itab[ino % INOPB(&sblock)];
-       }
-       if (ino++ < ROOTINO)
-               return(NULL);
-       return(dp);
+       if (gr = getgrnam(quotagroup))
+               return (gr->gr_gid);
+       return (-1);
 }
 
 }
 
-bread(bno, buf, cnt)
-       long unsigned bno;
-       char *buf;
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+       register struct fstab *fs;
+       int type;
+       char **qfnamep;
 {
 {
+       register char *opt;
+       char *cp;
+       static char initname, usrname[100], grpname[100];
+       static char buf[BUFSIZ];
 
 
-       lseek(fi, (long)dbtob(bno), 0);
-       if (read(fi, buf, cnt) != cnt) {
-               printf("read error %u\n", bno);
-               exit(1);
+       if (!initname) {
+               (void)snprintf(usrname, sizeof(usrname),
+                   "%s%s", qfextension[USRQUOTA], qfname);
+               (void)snprintf(grpname, sizeof(grpname),
+                   "%s%s", qfextension[GRPQUOTA], qfname);
+               initname = 1;
        }
        }
+       strcpy(buf, fs->fs_mntops);
+       for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+               if (cp = strchr(opt, '='))
+                       *cp++ = '\0';
+               if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+                       break;
+               if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+                       break;
+       }
+       if (!opt)
+               return (0);
+       if (cp)
+               *qfnamep = cp;
+       else {
+               (void)snprintf(buf, sizeof(buf),
+                   "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+               *qfnamep = buf;
+       }
+       return (1);
 }
 
 }
 
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
 struct fileusage *
 struct fileusage *
-lookup(uid)
-       u_short uid;
+lookup(id, type)
+       u_long id;
+       int type;
 {
        register struct fileusage *fup;
 
 {
        register struct fileusage *fup;
 
-       for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
-               if (fup->fu_uid == uid)
+       for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
+               if (fup->fu_id == id)
                        return (fup);
                        return (fup);
-       return ((struct fileusage *)0);
+       return (NULL);
 }
 
 }
 
+/*
+ * Add a new file usage id if it does not already exist.
+ */
 struct fileusage *
 struct fileusage *
-adduid(uid)
-       u_short uid;
+addid(id, type, name)
+       u_long id;
+       int type;
+       char *name;
 {
        struct fileusage *fup, **fhp;
 {
        struct fileusage *fup, **fhp;
+       int len;
 
 
-       fup = lookup(uid);
-       if (fup != 0)
+       if (fup = lookup(id, type))
                return (fup);
                return (fup);
-       fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
-       if (fup == 0) {
-               fprintf(stderr, "out of memory for fileusage structures\n");
-               exit(1);
-       }
-       fhp = &fuhead[uid % FUHASH];
+       if (name)
+               len = strlen(name);
+       else
+               len = 10;
+       if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
+               err(1, "%s", strerror(errno));
+       fhp = &fuhead[type][id & (FUHASH - 1)];
        fup->fu_next = *fhp;
        *fhp = fup;
        fup->fu_next = *fhp;
        *fhp = fup;
-       fup->fu_uid = uid;
-       if (uid > highuid)
-               highuid = uid;
+       fup->fu_id = id;
+       if (id > highid[type])
+               highid[type] = id;
+       if (name)
+               memmove(fup->fu_name, name, len + 1);
+       else
+               (void)sprintf(fup->fu_name, "%u", id);
        return (fup);
 }
 
        return (fup);
 }
 
-char *
-makerawname(name)
-       char *name;
+/*
+ * Special purpose version of ginode used to optimize pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct dinode *inodebuf;
+#define        INOBUFSIZE      56*1024 /* size of buffer to read inodes */
+
+struct dinode *
+getnextinode(inumber)
+       ino_t inumber;
+{
+       long size;
+       daddr_t dblk;
+       static struct dinode *dp;
+
+       if (inumber != nextino++ || inumber > maxino)
+               err(1, "bad inode number %d to nextinode", inumber);
+       if (inumber >= lastinum) {
+               readcnt++;
+               dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
+               if (readcnt % readpercg == 0) {
+                       size = partialsize;
+                       lastinum += partialcnt;
+               } else {
+                       size = inobufsize;
+                       lastinum += fullcnt;
+               }
+               bread(dblk, (char *)inodebuf, size);
+               dp = inodebuf;
+       }
+       return (dp++);
+}
+
+/*
+ * Prepare to scan a set of inodes.
+ */
+void
+resetinodebuf()
 {
 {
-       register char *cp;
-       char tmp, ch, *rindex();
-       static char rawname[MAXPATHLEN];
-
-       strcpy(rawname, name);
-       cp = rindex(rawname, '/') + 1;
-       if (cp == (char *)1 || *cp == 'r')
-               return (name);
-       for (ch = 'r'; *cp != '\0'; ) {
-               tmp = *cp;
-               *cp++ = ch;
-               ch = tmp;
+
+       nextino = 0;
+       lastinum = 0;
+       readcnt = 0;
+       inobufsize = blkroundup(&sblock, INOBUFSIZE);
+       fullcnt = inobufsize / sizeof(struct dinode);
+       readpercg = sblock.fs_ipg / fullcnt;
+       partialcnt = sblock.fs_ipg % fullcnt;
+       partialsize = partialcnt * sizeof(struct dinode);
+       if (partialcnt != 0) {
+               readpercg++;
+       } else {
+               partialcnt = fullcnt;
+               partialsize = inobufsize;
        }
        }
-       *cp++ = ch;
-       *cp = '\0';
-       return (rawname);
+       if (inodebuf == NULL &&
+          (inodebuf = malloc((u_int)inobufsize)) == NULL)
+               err(1, "%s", strerror(errno));
+       while (nextino < ROOTINO)
+               getnextinode(nextino);
+}
+
+/*
+ * Free up data structures used to scan inodes.
+ */
+void
+freeinodebuf()
+{
+
+       if (inodebuf != NULL)
+               free(inodebuf);
+       inodebuf = NULL;
+}
+
+/*
+ * Read specified disk blocks.
+ */
+void
+bread(bno, buf, cnt)
+       daddr_t bno;
+       char *buf;
+       long cnt;
+{
+
+       if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
+           read(fi, buf, cnt) != cnt)
+               err(1, "block %ld", bno);
 }
 }