]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sbin/quotacheck/quotacheck.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / sbin / quotacheck / quotacheck.c
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  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #if 0
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1980, 1990, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 static char sccsid[] = "@(#)quotacheck.c        8.3 (Berkeley) 1/29/94";
42 #endif /* not lint */
43 #endif
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46
47 /*
48  * Fix up / report on disk quotas & usage
49  */
50 #include <sys/param.h>
51 #include <sys/disklabel.h>
52 #include <sys/stat.h>
53
54 #include <ufs/ufs/dinode.h>
55 #include <ufs/ufs/quota.h>
56 #include <ufs/ffs/fs.h>
57
58 #include <err.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <fstab.h>
62 #include <grp.h>
63 #include <pwd.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <unistd.h>
68
69 char *qfname = QUOTAFILENAME;
70 char *qfextension[] = INITQFNAMES;
71 char *quotagroup = QUOTAGROUP;
72
73 union {
74         struct  fs      sblk;
75         char    dummy[MAXBSIZE];
76 } sb_un;
77 #define sblock  sb_un.sblk
78 union {
79         struct  cg      cgblk;
80         char    dummy[MAXBSIZE];
81 } cg_un;
82 #define cgblk   cg_un.cgblk
83 long dev_bsize = 1;
84 ino_t maxino;
85
86 union dinode {
87         struct ufs1_dinode dp1;
88         struct ufs2_dinode dp2;
89 };
90 #define DIP(dp, field) \
91         ((sblock.fs_magic == FS_UFS1_MAGIC) ? \
92         (dp)->dp1.field : (dp)->dp2.field)
93
94 struct quotaname {
95         long    flags;
96         char    grpqfname[PATH_MAX];
97         char    usrqfname[PATH_MAX];
98 };
99 #define HASUSR  1
100 #define HASGRP  2
101
102 struct fileusage {
103         struct  fileusage *fu_next;
104         u_long  fu_curinodes;
105         u_long  fu_curblocks;
106         u_long  fu_id;
107         char    fu_name[1];
108         /* actually bigger */
109 };
110 #define FUHASH 1024     /* must be power of two */
111 struct fileusage *fuhead[MAXQUOTAS][FUHASH];
112
113 int     aflag;                  /* all file systems */
114 int     gflag;                  /* check group quotas */
115 int     uflag;                  /* check user quotas */
116 int     vflag;                  /* verbose */
117 int     fi;                     /* open disk file descriptor */
118
119 struct fileusage *
120          addid(u_long, int, char *, char *);
121 char    *blockcheck(char *);
122 void     bread(ufs2_daddr_t, char *, long);
123 extern int checkfstab(int, int, void * (*)(struct fstab *),
124                                 int (*)(char *, char *, struct quotaname *));
125 int      chkquota(char *, char *, struct quotaname *);
126 void     freeinodebuf(void);
127 union dinode *
128          getnextinode(ino_t);
129 int      getquotagid(void);
130 int      hasquota(struct fstab *, int, char **);
131 struct fileusage *
132          lookup(u_long, int);
133 void    *needchk(struct fstab *);
134 int      oneof(char *, char*[], int);
135 void     printchanges(char *, int, struct dqblk *, struct fileusage *, u_long);
136 void     setinodebuf(ino_t);
137 int      update(char *, char *, int);
138 void     usage(void);
139
140 int
141 main(argc, argv)
142         int argc;
143         char *argv[];
144 {
145         struct fstab *fs;
146         struct passwd *pw;
147         struct group *gr;
148         struct quotaname *auxdata;
149         int i, argnum, maxrun, errs, ch;
150         long done = 0;
151         char *name;
152
153         errs = maxrun = 0;
154         while ((ch = getopt(argc, argv, "aguvl:")) != -1) {
155                 switch(ch) {
156                 case 'a':
157                         aflag++;
158                         break;
159                 case 'g':
160                         gflag++;
161                         break;
162                 case 'u':
163                         uflag++;
164                         break;
165                 case 'v':
166                         vflag++;
167                         break;
168                 case 'l':
169                         maxrun = atoi(optarg);
170                         break;
171                 default:
172                         usage();
173                 }
174         }
175         argc -= optind;
176         argv += optind;
177         if ((argc == 0 && !aflag) || (argc > 0 && aflag))
178                 usage();
179         if (!gflag && !uflag) {
180                 gflag++;
181                 uflag++;
182         }
183         if (gflag) {
184                 setgrent();
185                 while ((gr = getgrent()) != NULL)
186                         (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name,
187                             NULL);
188                 endgrent();
189         }
190         if (uflag) {
191                 setpwent();
192                 while ((pw = getpwent()) != NULL)
193                         (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name,
194                             NULL);
195                 endpwent();
196         }
197         /*
198          * Setting maxrun (-l) makes no sense without the -a flag.
199          * Historically this was never an error, so we just warn.
200          */
201         if (maxrun > 0 && !aflag)
202                 warnx("ignoring -l without -a");
203         if (aflag)
204                 exit(checkfstab(1, maxrun, needchk, chkquota));
205         if (setfsent() == 0)
206                 errx(1, "%s: can't open", FSTAB);
207         while ((fs = getfsent()) != NULL) {
208                 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
209                     (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
210                     (auxdata = needchk(fs)) &&
211                     (name = blockcheck(fs->fs_spec))) {
212                         done |= 1 << argnum;
213                         errs += chkquota(name, fs->fs_file, auxdata);
214                 }
215         }
216         endfsent();
217         for (i = 0; i < argc; i++)
218                 if ((done & (1 << i)) == 0)
219                         fprintf(stderr, "%s not found in %s\n",
220                                 argv[i], FSTAB);
221         exit(errs);
222 }
223
224 void
225 usage()
226 {
227         (void)fprintf(stderr, "%s\n%s\n", 
228                 "usage: quotacheck [-guv] -a",
229                 "       quotacheck [-guv] filesystem ...");
230         exit(1);
231 }
232
233 void *
234 needchk(fs)
235         struct fstab *fs;
236 {
237         struct quotaname *qnp;
238         char *qfnp;
239
240         if (strcmp(fs->fs_vfstype, "ufs") ||
241             strcmp(fs->fs_type, FSTAB_RW))
242                 return (NULL);
243         if ((qnp = malloc(sizeof(*qnp))) == NULL)
244                 errx(1, "malloc failed");
245         qnp->flags = 0;
246         if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
247                 strcpy(qnp->grpqfname, qfnp);
248                 qnp->flags |= HASGRP;
249         }
250         if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
251                 strcpy(qnp->usrqfname, qfnp);
252                 qnp->flags |= HASUSR;
253         }
254         if (qnp->flags)
255                 return (qnp);
256         free(qnp);
257         return (NULL);
258 }
259
260 /*
261  * Possible superblock locations ordered from most to least likely.
262  */
263 static int sblock_try[] = SBLOCKSEARCH;
264
265 /*
266  * Scan the specified file system to check quota(s) present on it.
267  */
268 int
269 chkquota(fsname, mntpt, qnp)
270         char *fsname, *mntpt;
271         struct quotaname *qnp;
272 {
273         struct fileusage *fup;
274         union dinode *dp;
275         int cg, i, mode, errs = 0;
276         ino_t ino, inosused, userino = 0, groupino = 0;
277         char *cp;
278         struct stat sb;
279
280         if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
281                 warn("%s", fsname);
282                 return (1);
283         }
284         if (vflag) {
285                 (void)printf("*** Checking ");
286                 if (qnp->flags & HASUSR)
287                         (void)printf("%s%s", qfextension[USRQUOTA],
288                             (qnp->flags & HASGRP) ? " and " : "");
289                 if (qnp->flags & HASGRP)
290                         (void)printf("%s", qfextension[GRPQUOTA]);
291                 (void)printf(" quotas for %s (%s)\n", fsname, mntpt);
292         }
293         if (qnp->flags & HASUSR) {
294                 if (stat(qnp->usrqfname, &sb) == 0)
295                         userino = sb.st_ino;
296         }
297         if (qnp->flags & HASGRP) {
298                 if (stat(qnp->grpqfname, &sb) == 0)
299                         groupino = sb.st_ino;
300         }
301         sync();
302         dev_bsize = 1;
303         for (i = 0; sblock_try[i] != -1; i++) {
304                 bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE);
305                 if ((sblock.fs_magic == FS_UFS1_MAGIC ||
306                      (sblock.fs_magic == FS_UFS2_MAGIC &&
307                       sblock.fs_sblockloc == sblock_try[i])) &&
308                     sblock.fs_bsize <= MAXBSIZE &&
309                     sblock.fs_bsize >= sizeof(struct fs))
310                         break;
311         }
312         if (sblock_try[i] == -1) {
313                 warn("Cannot find file system superblock");
314                 return (1);
315         }
316         dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
317         maxino = sblock.fs_ncg * sblock.fs_ipg;
318         for (cg = 0; cg < sblock.fs_ncg; cg++) {
319                 ino = cg * sblock.fs_ipg;
320                 setinodebuf(ino);
321                 bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk),
322                     sblock.fs_cgsize);
323                 if (sblock.fs_magic == FS_UFS2_MAGIC)
324                         inosused = cgblk.cg_initediblk;
325                 else
326                         inosused = sblock.fs_ipg;
327                 /*
328                  * If we are using soft updates, then we can trust the
329                  * cylinder group inode allocation maps to tell us which
330                  * inodes are allocated. We will scan the used inode map
331                  * to find the inodes that are really in use, and then
332                  * read only those inodes in from disk.
333                  */
334                 if (sblock.fs_flags & FS_DOSOFTDEP) {
335                         if (!cg_chkmagic(&cgblk))
336                                 errx(1, "CG %d: BAD MAGIC NUMBER\n", cg);
337                         cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT];
338                         for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
339                                 if (*cp == 0)
340                                         continue;
341                                 for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
342                                         if (*cp & i)
343                                                 break;
344                                         inosused--;
345                                 }
346                                 break;
347                         }
348                         if (inosused <= 0)
349                                 continue;
350                 }
351                 for (i = 0; i < inosused; i++, ino++) {
352                         if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
353                             (mode = DIP(dp, di_mode) & IFMT) == 0)
354                                 continue;
355                         /*
356                          * XXX: Do not account for UIDs or GIDs that appear
357                          * to be negative to prevent generating 100GB+
358                          * quota files.
359                          */
360                         if ((int)DIP(dp, di_uid) < 0 || 
361                             (int)DIP(dp, di_gid) < 0) {
362                                 if (vflag) {
363                                         if (aflag)
364                                                 (void)printf("%s: ", mntpt);
365                         (void)printf("out of range UID/GID (%u/%u) ino=%u\n",
366                                             DIP(dp, di_uid), DIP(dp,di_gid),
367                                             ino);
368                                 }
369                                 continue;
370                         }
371
372                         /* 
373                          * Do not account for file system snapshot files
374                          * or the actual quota data files to be consistent
375                          * with how they are handled inside the kernel.
376                          */
377 #ifdef  SF_SNAPSHOT
378                         if (DIP(dp, di_flags) & SF_SNAPSHOT)
379                                 continue;
380 #endif
381                         if (ino == userino || ino == groupino)
382                                 continue;
383                         if (qnp->flags & HASGRP) {
384                                 fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
385                                     (char *)0, mntpt);
386                                 fup->fu_curinodes++;
387                                 if (mode == IFREG || mode == IFDIR ||
388                                     mode == IFLNK)
389                                         fup->fu_curblocks += DIP(dp, di_blocks);
390                         }
391                         if (qnp->flags & HASUSR) {
392                                 fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
393                                     (char *)0, mntpt);
394                                 fup->fu_curinodes++;
395                                 if (mode == IFREG || mode == IFDIR ||
396                                     mode == IFLNK)
397                                         fup->fu_curblocks += DIP(dp, di_blocks);
398                         }
399                 }
400         }
401         freeinodebuf();
402         if (qnp->flags & HASUSR)
403                 errs += update(mntpt, qnp->usrqfname, USRQUOTA);
404         if (qnp->flags & HASGRP)
405                 errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
406         close(fi);
407         return (errs);
408 }
409
410 /*
411  * Update a specified quota file.
412  */
413 int
414 update(fsname, quotafile, type)
415         char *fsname, *quotafile;
416         int type;
417 {
418         struct fileusage *fup;
419         FILE *qfi, *qfo;
420         u_long id, lastid, highid = 0;
421         off_t offset;
422         int i;
423         struct dqblk dqbuf;
424         struct stat sb;
425         static int warned = 0;
426         static struct dqblk zerodqbuf;
427         static struct fileusage zerofileusage;
428
429         if ((qfo = fopen(quotafile, "r+")) == NULL) {
430                 if (errno == ENOENT)
431                         qfo = fopen(quotafile, "w+");
432                 if (qfo) {
433                         warnx("creating quota file %s", quotafile);
434 #define MODE    (S_IRUSR|S_IWUSR|S_IRGRP)
435                         (void) fchown(fileno(qfo), getuid(), getquotagid());
436                         (void) fchmod(fileno(qfo), MODE);
437                 } else {
438                         warn("%s", quotafile);
439                         return (1);
440                 }
441         }
442         if ((qfi = fopen(quotafile, "r")) == NULL) {
443                 warn("%s", quotafile);
444                 (void) fclose(qfo);
445                 return (1);
446         }
447         if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
448             errno == EOPNOTSUPP && !warned && vflag) {
449                 warned++;
450                 (void)printf("*** Warning: %s\n",
451                     "Quotas are not compiled into this kernel");
452         }
453         if (fstat(fileno(qfi), &sb) < 0) {
454                 warn("Cannot fstat quota file %s\n", quotafile);
455                 (void) fclose(qfo);
456                 (void) fclose(qfi);
457                 return (1);
458         }
459         if ((sb.st_size % sizeof(struct dqblk)) != 0)
460                 warn("%s size is not a multiple of dqblk\n", quotafile);
461
462         /*
463          * Scan the on-disk quota file and record any usage changes.
464          */
465
466         if (sb.st_size != 0)
467                 lastid = (sb.st_size / sizeof(struct dqblk)) - 1;
468         else
469                 lastid = 0;
470         for (id = 0, offset = 0; id <= lastid; 
471             id++, offset += sizeof(struct dqblk)) {
472                 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
473                         dqbuf = zerodqbuf;
474                 if ((fup = lookup(id, type)) == NULL)
475                         fup = &zerofileusage;
476                 if (fup->fu_curinodes || fup->fu_curblocks ||
477                     dqbuf.dqb_bsoftlimit || dqbuf.dqb_bhardlimit ||
478                     dqbuf.dqb_isoftlimit || dqbuf.dqb_ihardlimit)
479                         highid = id;
480                 if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
481                     dqbuf.dqb_curblocks == fup->fu_curblocks) {
482                         fup->fu_curinodes = 0;
483                         fup->fu_curblocks = 0;
484                         continue;
485                 }
486                 printchanges(fsname, type, &dqbuf, fup, id);
487                 /*
488                  * Reset time limit if have a soft limit and were
489                  * previously under it, but are now over it.
490                  */
491                 if (dqbuf.dqb_bsoftlimit &&
492                     dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
493                     fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
494                         dqbuf.dqb_btime = 0;
495                 if (dqbuf.dqb_isoftlimit &&
496                     dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
497                     fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
498                         dqbuf.dqb_itime = 0;
499                 dqbuf.dqb_curinodes = fup->fu_curinodes;
500                 dqbuf.dqb_curblocks = fup->fu_curblocks;
501                 if (fseeko(qfo, offset, SEEK_SET) < 0) {
502                         warn("%s: seek failed", quotafile);
503                         return(1);
504                 }
505                 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
506                 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
507                     (caddr_t)&dqbuf);
508                 fup->fu_curinodes = 0;
509                 fup->fu_curblocks = 0;
510         }
511
512         /*
513          * Walk the hash table looking for ids with non-zero usage
514          * that are not currently recorded in the quota file. E.g.
515          * ids that are past the end of the current file.
516          */
517
518         for (i = 0; i < FUHASH; i++) {
519                 for (fup = fuhead[type][i]; fup != NULL; fup = fup->fu_next) {
520                         if (fup->fu_id <= lastid)
521                                 continue;
522                         if (fup->fu_curinodes == 0 && fup->fu_curblocks == 0)
523                                 continue;
524                         bzero(&dqbuf, sizeof(struct dqblk));
525                         if (fup->fu_id > highid)
526                                 highid = fup->fu_id;
527                         printchanges(fsname, type, &dqbuf, fup, id);
528                         dqbuf.dqb_curinodes = fup->fu_curinodes;
529                         dqbuf.dqb_curblocks = fup->fu_curblocks;
530                         offset = (off_t)fup->fu_id * sizeof(struct dqblk);
531                         if (fseeko(qfo, offset, SEEK_SET) < 0) {
532                                 warn("%s: seek failed", quotafile);
533                                 return(1);
534                         }
535                         fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
536                         (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
537                             (caddr_t)&dqbuf);
538                         fup->fu_curinodes = 0;
539                         fup->fu_curblocks = 0;
540                 }
541         }
542         fclose(qfi);
543         fflush(qfo);
544         ftruncate(fileno(qfo),
545             (((off_t)highid + 1) * sizeof(struct dqblk)));
546         fclose(qfo);
547         return (0);
548 }
549
550 /*
551  * Check to see if target appears in list of size cnt.
552  */
553 int
554 oneof(target, list, cnt)
555         char *target, *list[];
556         int cnt;
557 {
558         int i;
559
560         for (i = 0; i < cnt; i++)
561                 if (strcmp(target, list[i]) == 0)
562                         return (i);
563         return (-1);
564 }
565
566 /*
567  * Determine the group identifier for quota files.
568  */
569 int
570 getquotagid()
571 {
572         struct group *gr;
573
574         if ((gr = getgrnam(quotagroup)) != NULL)
575                 return (gr->gr_gid);
576         return (-1);
577 }
578
579 /*
580  * Check to see if a particular quota is to be enabled.
581  */
582 int
583 hasquota(fs, type, qfnamep)
584         struct fstab *fs;
585         int type;
586         char **qfnamep;
587 {
588         char *opt;
589         char *cp;
590         static char initname, usrname[100], grpname[100];
591         static char buf[BUFSIZ];
592
593         if (!initname) {
594                 (void)snprintf(usrname, sizeof(usrname),
595                     "%s%s", qfextension[USRQUOTA], qfname);
596                 (void)snprintf(grpname, sizeof(grpname),
597                     "%s%s", qfextension[GRPQUOTA], qfname);
598                 initname = 1;
599         }
600         strcpy(buf, fs->fs_mntops);
601         for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
602                 if ((cp = index(opt, '=')) != NULL)
603                         *cp++ = '\0';
604                 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
605                         break;
606                 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
607                         break;
608         }
609         if (!opt)
610                 return (0);
611         if (cp)
612                 *qfnamep = cp;
613         else {
614                 (void)snprintf(buf, sizeof(buf),
615                     "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
616                 *qfnamep = buf;
617         }
618         return (1);
619 }
620
621 /*
622  * Routines to manage the file usage table.
623  *
624  * Lookup an id of a specific type.
625  */
626 struct fileusage *
627 lookup(id, type)
628         u_long id;
629         int type;
630 {
631         struct fileusage *fup;
632
633         for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
634                 if (fup->fu_id == id)
635                         return (fup);
636         return (NULL);
637 }
638
639 /*
640  * Add a new file usage id if it does not already exist.
641  */
642 struct fileusage *
643 addid(id, type, name, fsname)
644         u_long id;
645         int type;
646         char *name;
647         char *fsname;
648 {
649         struct fileusage *fup, **fhp;
650         int len;
651
652         if ((fup = lookup(id, type)) != NULL)
653                 return (fup);
654         if (name)
655                 len = strlen(name);
656         else
657                 len = 0;
658         if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
659                 errx(1, "calloc failed");
660         fhp = &fuhead[type][id & (FUHASH - 1)];
661         fup->fu_next = *fhp;
662         *fhp = fup;
663         fup->fu_id = id;
664         if (name)
665                 bcopy(name, fup->fu_name, len + 1);
666         else {
667                 (void)sprintf(fup->fu_name, "%lu", id);
668                 if (vflag) {
669                         if (aflag && fsname != NULL)
670                                 (void)printf("%s: ", fsname);
671                         printf("unknown %cid: %lu\n", 
672                             type == USRQUOTA ? 'u' : 'g', id);
673                 }
674         }
675         return (fup);
676 }
677
678 /*
679  * Special purpose version of ginode used to optimize pass
680  * over all the inodes in numerical order.
681  */
682 static ino_t nextino, lastinum, lastvalidinum;
683 static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
684 static caddr_t inodebuf;
685 #define INOBUFSIZE      56*1024         /* size of buffer to read inodes */
686
687 union dinode *
688 getnextinode(ino_t inumber)
689 {
690         long size;
691         ufs2_daddr_t dblk;
692         union dinode *dp;
693         static caddr_t nextinop;
694
695         if (inumber != nextino++ || inumber > lastvalidinum)
696                 errx(1, "bad inode number %d to nextinode", inumber);
697         if (inumber >= lastinum) {
698                 readcnt++;
699                 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
700                 if (readcnt % readpercg == 0) {
701                         size = partialsize;
702                         lastinum += partialcnt;
703                 } else {
704                         size = inobufsize;
705                         lastinum += fullcnt;
706                 }
707                 /*
708                  * If bread returns an error, it will already have zeroed
709                  * out the buffer, so we do not need to do so here.
710                  */
711                 bread(dblk, inodebuf, size);
712                 nextinop = inodebuf;
713         }
714         dp = (union dinode *)nextinop;
715         if (sblock.fs_magic == FS_UFS1_MAGIC)
716                 nextinop += sizeof(struct ufs1_dinode);
717         else
718                 nextinop += sizeof(struct ufs2_dinode);
719         return (dp);
720 }
721
722 /*
723  * Prepare to scan a set of inodes.
724  */
725 void
726 setinodebuf(ino_t inum)
727 {
728
729         if (inum % sblock.fs_ipg != 0)
730                 errx(1, "bad inode number %d to setinodebuf", inum);
731         lastvalidinum = inum + sblock.fs_ipg - 1;
732         nextino = inum;
733         lastinum = inum;
734         readcnt = 0;
735         if (inodebuf != NULL)
736                 return;
737         inobufsize = blkroundup(&sblock, INOBUFSIZE);
738         fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
739             sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
740         readpercg = sblock.fs_ipg / fullcnt;
741         partialcnt = sblock.fs_ipg % fullcnt;
742         partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
743             sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
744         if (partialcnt != 0) {
745                 readpercg++;
746         } else {
747                 partialcnt = fullcnt;
748                 partialsize = inobufsize;
749         }
750         if ((inodebuf = malloc((unsigned)inobufsize)) == NULL)
751                 errx(1, "cannot allocate space for inode buffer");
752 }
753
754 /*
755  * Free up data structures used to scan inodes.
756  */
757 void
758 freeinodebuf()
759 {
760
761         if (inodebuf != NULL)
762                 free(inodebuf);
763         inodebuf = NULL;
764 }
765
766 /*
767  * Read specified disk blocks.
768  */
769 void
770 bread(bno, buf, cnt)
771         ufs2_daddr_t bno;
772         char *buf;
773         long cnt;
774 {
775
776         if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
777             read(fi, buf, cnt) != cnt)
778                 errx(1, "bread failed on block %ld", (long)bno);
779 }
780
781 /*
782  * Display updated block and i-node counts.
783  */
784 void
785 printchanges(fsname, type, dp, fup, id)
786         char *fsname;
787         int type;
788         struct dqblk *dp;
789         struct fileusage *fup;
790         u_long id;
791 {
792         if (!vflag)
793                 return;
794         if (aflag)
795                 (void)printf("%s: ", fsname);
796         if (fup->fu_name[0] == '\0')
797                 (void)printf("%-8lu fixed ", id);
798         else
799                 (void)printf("%-8s fixed ", fup->fu_name);
800         switch (type) {
801
802         case GRPQUOTA:
803                 (void)printf("(group):");
804                 break;
805
806         case USRQUOTA:
807                 (void)printf("(user): ");
808                 break;
809
810         default:
811                 (void)printf("(unknown quota type %d)", type);
812                 break;
813         }
814         if (dp->dqb_curinodes != fup->fu_curinodes)
815                 (void)printf("\tinodes %lu -> %lu", (u_long)dp->dqb_curinodes,
816                     (u_long)fup->fu_curinodes);
817         if (dp->dqb_curblocks != fup->fu_curblocks)
818                 (void)printf("\tblocks %lu -> %lu", 
819                     (u_long)dp->dqb_curblocks,
820                     (u_long)fup->fu_curblocks);
821         (void)printf("\n");
822 }