BSD 4_4_Lite2 release
[unix-history] / usr / src / sbin / quotacheck / quotacheck.c
index 10193ad..31c94d1 100644 (file)
-#ifndef lit
-static char sccsid[] = "@(#)quotacheck.c       4.1 (Melbourne) %G%";
-#endif
+/*
+ * 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
+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 <pwd.h>
 #include <sys/param.h>
 #include <sys/param.h>
-#include <sys/inode.h>
-#include <sys/fs.h>
-#define        QUOTA
-#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 <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;
 
 
-#define        ITABSZ  256
-#define        NUID    3500
 union {
        struct  fs      sblk;
 union {
        struct  fs      sblk;
-       char    ___[MAXBSIZE];
+       char    dummy[MAXBSIZE];
 } un;
 #define        sblock  un.sblk
 } un;
 #define        sblock  un.sblk
-struct dinode  itab[ITABSZ];
-struct dinode  *dp;
-struct dqblk   du[NUID];
-char   *dn[NUID];
-struct dqblk   zeroes;
-u_short        iuse[NUID];
-u_long buse[NUID];
-long   blocks;
-dev_t  dev;
-
-int    bflg;
-int    iflg;
-int    rflg;
-int    sflg;
-
-int    fi;
-unsigned       ino;
-unsigned       nfiles;
-int    highuid;
-
-struct passwd  *getpwent();
-struct dinode  *ginode();
-char   *malloc();
-char   *copy();
-
+long dev_bsize;
+long maxino;
+
+struct quotaname {
+       long    flags;
+       char    grpqfname[MAXPATHLEN + 1];
+       char    usrqfname[MAXPATHLEN + 1];
+};
+#define        HASUSR  1
+#define        HASGRP  2
+
+struct fileusage {
+       struct  fileusage *fu_next;
+       u_long  fu_curinodes;
+       u_long  fu_curblocks;
+       u_long  fu_id;
+       char    fu_name[1];
+       /* actually bigger */
+};
+#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)
 main(argc, argv)
-       char **argv;
+       int argc;
+       char *argv[];
 {
 {
-       register int n;
-       register struct passwd *lp;
-       register char *p;
-       register long unsigned i;
-       register c;
-       register cg;
-       FILE *qf;
-       struct stat statb;
-
-       while (--argc > 0 && *(p = *++argv) == '-')
-               while (c = *++p) switch (c) {
-
-               case 's':
-                       sflg++;
+       register struct fstab *fs;
+       register struct passwd *pw;
+       register struct group *gr;
+       struct quotaname *auxdata;
+       int i, argnum, maxrun, errs;
+       long done = 0;
+       char ch, *name;
+
+       errs = maxrun = 0;
+       while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
+               switch(ch) {
+               case 'a':
+                       aflag++;
                        break;
                        break;
-
-               case 'b':
-                       bflg++;
+               case 'g':
+                       gflag++;
                        break;
                        break;
-
-               case 'i':
-                       iflg++;
+               case 'u':
+                       uflag++;
                        break;
                        break;
-
-               case 'r':
-                       rflg++;
+               case 'v':
+                       vflag++;
                        break;
                        break;
+               case 'l':
+                       maxrun = atoi(optarg);
+                       break;
+               default:
+                       usage();
                }
                }
-
-       if (argc != 2) {
-               fprintf(stderr, "Usage: fixquota filesys qfile\n");
-               exit(1);
        }
        }
+       argc -= optind;
+       argv += optind;
+       if ((argc == 0 && !aflag) || (argc > 0 && aflag))
+               usage();
+       if (!gflag && !uflag) {
+               gflag++;
+               uflag++;
+       }
+       if (gflag) {
+               setgrent();
+               while ((gr = getgrent()) != 0)
+                       (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+               endgrent();
+       }
+       if (uflag) {
+               setpwent();
+               while ((pw = getpwent()) != 0)
+                       (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
+               endpwent();
+       }
+       if (aflag)
+               exit(checkfstab(1, maxrun, needchk, chkquota));
+       if (setfsent() == 0)
+               err(1, "%s: can't open", FSTAB);
+       while ((fs = getfsent()) != NULL) {
+               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)
+                       fprintf(stderr, "%s not found in %s\n",
+                               argv[i], FSTAB);
+       exit(errs);
+}
 
 
-       fi = open(p, 0);
-       if (fi < 0) {
-               fprintf(stderr, "Can't open %s\n", p);
-               exit(1);
+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);
+}
 
 
-       if (iflg || bflg || rflg) {
-               while((lp=getpwent()) != 0) {
-                       n = lp->pw_uid;
-                       if (n>=NUID)
+/*
+ * 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 dinode *dp;
+       int cg, i, mode, errs = 0;
+       ino_t ino;
+
+       if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
+               perror(fsname);
+               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);
+       }
+       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;
                                continue;
-                       if(dn[n])
+                       if ((mode = dp->di_mode & IFMT) == 0)
                                continue;
                                continue;
-                       dn[n] = copy(lp->pw_name);
+                       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;
+                       }
                }
        }
                }
        }
+       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);
+}
 
 
-       if (!(iflg || bflg))
-               sflg++;
-
-       qf = fopen(*++argv, "r");
-       if (qf != NULL) {
-               fstat(fileno(qf), &statb);
-               dev = statb.st_dev;
-               quota(Q_SYNC, 0, dev, 0);
-               n = fread(du, sizeof(struct dqblk), NUID, qf);
-               if (n == EOF)
-                       n = 0;
-               highuid = n-1;
-               fclose(qf);
-       } else {
-               highuid = -1;
-               dev = NODEV;
+/*
+ * 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);
+               }
        }
        }
-/*ZZprintf("highuid = %d\n", highuid);*/
-
-       for (n = 0; n <= highuid; n++) {
-               iuse[n] = du[n].dqb_curinodes;
-               buse[n] = du[n].dqb_curblocks;
-               du[n].dqb_curinodes = du[n].dqb_curblocks = 0;
+       if ((qfi = fopen(quotafile, "r")) == NULL) {
+               (void) fprintf(stderr,
+                   "quotacheck: %s: %s\n", quotafile, strerror(errno));
+               (void) fclose(qfo);
+               return (1);
        }
        }
-
-       sync();
-       bread(SBLOCK, (char *)&sblock, SBSIZE);
-       ino = 0;
-       for (cg = 0; cg < sblock.fs_ncg; cg++) {
-/*ZZprintf("cg %d <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 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");
        }
        }
-       if (sflg && highuid >= 0) {
-               int sig;
-               int ssig;
-
-               sig = (int) signal(SIGINT, SIG_IGN);
-               ssig = (int) signal(SIGTSTP, SIG_IGN);
-               if ((qf = fopen(*argv, "a")) == NULL) {
-                       fprintf(stderr, "Can't create %s\n", *argv);
-                       exit(1);
+       for (lastid = highid[type], id = 0; id <= lastid; id++) {
+               if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
+                       dqbuf = zerodqbuf;
+               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;
                }
                }
-
-               rewind(qf);
-               fwrite(du, sizeof(struct dqblk), highuid+1, qf);
-               fclose(qf);
-               signal(SIGTSTP, ssig);
-               sysset();
-               signal(SIGINT, sig);
+               if (vflag) {
+                       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");
+               }
+               /*
+                * 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;
        }
        }
-       report();
+       fclose(qfi);
+       fflush(qfo);
+       ftruncate(fileno(qfo),
+           (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
+       fclose(qfo);
+       return (0);
 }
 
 }
 
-acct(ip)
-       register struct dinode *ip;
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+       register char *target, *list[];
+       int cnt;
 {
 {
-       register n;
-
-       if (ip == NULL)
-               return;
-       if (ip->di_mode == 0)
-/*ZZ{printf(" unallocated\n");*/
-               return;
-/*ZZ}*/
-       if (ip->di_uid >= NUID)
-/*ZZ{printf(" uid oor\n");*/
-               return;
-/*ZZ}*/
-       if (ip->di_uid > highuid) {
-               for (n = highuid+1; n <= ip->di_uid; n++)
-                       du[n] = zeroes;
-               highuid = ip->di_uid;
-       }
-       du[ip->di_uid].dqb_curinodes++;
-       if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
-/*ZZ{printf(" special\n");*/
-               return;
-/*ZZ}*/
-       blocks = 0;
-       for (n = 0; n < NDADDR; n++)
-               if (ip->di_db[n])
-                       blocks += dblksize(&sblock, ip, n) / DEV_BSIZE;
-       for (n = 0; n < NIADDR; n++)
-               tloop(ip->di_ib[n], ip, n);
-       du[ip->di_uid].dqb_curblocks += blocks;
-       if (blocks != ip->di_blocks)
-               printf("Ino %d: <calc %d, recorded %d>\n", ino, blocks, ip->di_blocks);
-/*ZZprintf(" %d blks\n", blocks);*/
+       register int i;
+
+       for (i = 0; i < cnt; i++)
+               if (strcmp(target, list[i]) == 0)
+                       return (i);
+       return (-1);
 }
 
 }
 
-tloop(bn, ip, f)
-       long bn;
+/*
+ * Determine the group identifier for quota files.
+ */
+int
+getquotagid()
 {
 {
-       register i;
-       long    iblk[MAXBSIZE/sizeof(long)];
-
-       if (!bn)
-               return;
-       blocks += sblock.fs_bsize / DEV_BSIZE;
-       bread(fsbtodb(&sblock, bn), iblk, sblock.fs_bsize);
-       if (f) {
-               for (i = 0; i < NINDIR(&sblock); i++)
-                       tloop(iblk[i], ip, f-1);
-       } else {
-               for (i = 0; i < NINDIR(&sblock); i++)
-                       if (iblk[i])
-                               blocks += sblock.fs_bsize / DEV_BSIZE;
-       }
+       struct group *gr;
+
+       if (gr = getgrnam(quotagroup))
+               return (gr->gr_gid);
+       return (-1);
 }
 
 }
 
-struct dinode *
-ginode()
+/*
+ * 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 unsigned long iblk;
-
-       if (dp == NULL || ++dp >= &itab[ITABSZ]) {
-               iblk = itod(&sblock, ino);
-/*ZZprintf("dp = %x, itab=%x", dp, itab);*/
-/*ZZprintf(" Reading inodes from fs blk %d ", iblk);*/
-               bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
-               dp = &itab[ino % INOPB(&sblock)];
-/*ZZprintf("dp = %x\n", dp, itab);*/
+       register char *opt;
+       char *cp;
+       static char initname, usrname[100], grpname[100];
+       static char buf[BUFSIZ];
+
+       if (!initname) {
+               (void)snprintf(usrname, sizeof(usrname),
+                   "%s%s", qfextension[USRQUOTA], qfname);
+               (void)snprintf(grpname, sizeof(grpname),
+                   "%s%s", qfextension[GRPQUOTA], qfname);
+               initname = 1;
        }
        }
-/*ZZprintf("ino %d ", ino);*/
-       if (ino++ < ROOTINO)
-               return(NULL);
-       return(dp);
+       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);
 }
 
 }
 
-bread(bno, buf, cnt)
-       long unsigned bno;
-       char *buf;
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(id, type)
+       u_long id;
+       int type;
 {
 {
+       register struct fileusage *fup;
 
 
-       lseek(fi, (long)bno*DEV_BSIZE, 0);
-       if (read(fi, buf, cnt) != cnt) {
-               printf("read error %u\n", bno);
-               exit(1);
-       }
+       for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
+               if (fup->fu_id == id)
+                       return (fup);
+       return (NULL);
 }
 
 }
 
-sysset()
+/*
+ * Add a new file usage id if it does not already exist.
+ */
+struct fileusage *
+addid(id, type, name)
+       u_long id;
+       int type;
+       char *name;
 {
 {
-       struct dqusage usage;
-       register i;
+       struct fileusage *fup, **fhp;
+       int len;
+
+       if (fup = lookup(id, type))
+               return (fup);
+       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_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);
+}
 
 
-       for (i = 0; i <= highuid; i++) {
-               if (du[i].dqb_curinodes != iuse[i] || du[i].dqb_curblocks != buse[i]) {
-                       if (du[i].dqb_isoftlimit == 0 && du[i].dqb_bsoftlimit == 0)
-                               continue;
-                       if (rflg) {
-                               if (dn[i])
-                                       printf("%s", dn[i]);
-                               else
-                                       printf("#%d", i);
-                               printf(": i %d->%d, b %d->%d\n"
-                                       , iuse[i]
-                                       , du[i].dqb_curinodes
-                                       , buse[i]
-                                       , du[i].dqb_curblocks
-                               );
-                       }
-                       usage.du_curinodes = du[i].dqb_curinodes;
-                       usage.du_curblocks = du[i].dqb_curblocks;
-                       quota(Q_SETDUSE, i, dev, &usage);
+/*
+ * 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++);
 }
 
 }
 
-report()
+/*
+ * Prepare to scan a set of inodes.
+ */
+void
+resetinodebuf()
 {
 {
-       register i;
-
-       if (iflg)
-               for (i = 0; i <= highuid; i++)
-                       if (du[i].dqb_isoftlimit && du[i].dqb_curinodes >= du[i].dqb_isoftlimit) {
-                               if (dn[i])
-                                       printf("%-10s", dn[i]);
-                               else
-                                       printf("#%-9d", i);
-                               printf("%5d (iq = %d)\n", du[i].dqb_curinodes, du[i].dqb_isoftlimit);
-                       }
 
 
-       if (bflg)
-               for (i = 0; i <= highuid; i++)
-                       if (du[i].dqb_bsoftlimit && du[i].dqb_curblocks >= du[i].dqb_bsoftlimit) {
-                               if (dn[i])
-                                       printf("%-10s", dn[i]);
-                               else
-                                       printf("#%-9s", i);
-                               printf("%5d (quot = %d)\n", du[i].dqb_curblocks, du[i].dqb_bsoftlimit);
-                       }
+       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;
+       }
+       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;
 }
 
 }
 
-char *
-copy(s)
-       char *s;
+/*
+ * Read specified disk blocks.
+ */
+void
+bread(bno, buf, cnt)
+       daddr_t bno;
+       char *buf;
+       long cnt;
 {
 {
-       register char *p;
-       register n;
-
-       for(n=0; s[n]; n++)
-               ;
-       p = malloc((unsigned)n+1);
-       for(n=0; p[n] = s[n]; n++)
-               ;
-       return(p);
+
+       if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
+           read(fi, buf, cnt) != cnt)
+               err(1, "block %ld", bno);
 }
 }