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