From d038e02fd667ab6c02875840105798aaa7029504 Mon Sep 17 00:00:00 2001 From: rgrimes Date: Thu, 26 May 1994 06:35:07 +0000 Subject: BSD 4.4 Lite sbin Sources Note: XNSrouted and routed NOT imported here, they shall be imported with usr.sbin. --- sbin/quotacheck/Makefile | 8 + sbin/quotacheck/quotacheck.8 | 157 +++++++++++ sbin/quotacheck/quotacheck.c | 636 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 801 insertions(+) create mode 100644 sbin/quotacheck/Makefile create mode 100644 sbin/quotacheck/quotacheck.8 create mode 100644 sbin/quotacheck/quotacheck.c (limited to 'sbin/quotacheck') diff --git a/sbin/quotacheck/Makefile b/sbin/quotacheck/Makefile new file mode 100644 index 0000000..c6b8e3e --- /dev/null +++ b/sbin/quotacheck/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= quotacheck +SRCS= quotacheck.c preen.c +MAN8= quotacheck.0 +.PATH: ${.CURDIR}/../fsck + +.include diff --git a/sbin/quotacheck/quotacheck.8 b/sbin/quotacheck/quotacheck.8 new file mode 100644 index 0000000..41d9fc4 --- /dev/null +++ b/sbin/quotacheck/quotacheck.8 @@ -0,0 +1,157 @@ +.\" Copyright (c) 1983, 1990, 1991, 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. +.\" +.\" @(#)quotacheck.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt QUOTACHECK 8 +.Os BSD 4.2 +.Sh NAME +.Nm quotacheck +.Nd filesystem quota consistency checker +.Sh SYNOPSIS +.Nm quotacheck +.Op Fl g +.Op Fl u +.Op Fl v +.Ar filesystem Ar ... +.Nm quotacheck +.Op Fl g +.Op Fl u +.Op Fl v +.Fl a +.Sh DESCRIPTION +.Nm Quotacheck +examines each filesystem, +builds a table of current disk usage, +and compares this table against that recorded +in the disk quota file for the filesystem. +If any inconsistencies are detected, both the +quota file and the current system copy of the +incorrect quotas are updated (the latter only +occurs if an active filesystem is checked). +By default both user and group quotas are checked. +.Pp +Available options: +.Bl -tag -width Ds +.It Fl a +If the +.Fl a +flag is supplied in place of any filesystem names, +.Nm quotacheck +will check all the filesystems indicated in +.Pa /etc/fstab +to be read-write with disk quotas. +By default only the types of quotas listed in +.Pa /etc/fstab +are checked. +.It Fl g +Only group quotas listed in +.Pa /etc/fstab +are to be checked. +.It Fl u +Only user quotas listed in +.Pa /etc/fstab +are to be checked. +.It Fl v +.Nm quotacheck +reports discrepancies between the +calculated and recorded disk quotas. +.El +.Pp +Specifying both +.Fl g +and +.Fl u +is equivalent to the default. +Parallel passes are run on the filesystems required, +using the pass numbers in +.Pa /etc/fstab +in an identical fashion to +.Xr fsck 8 . +.Pp +Normally +.Nm quotacheck +operates silently. +.Pp +.Nm Quotacheck +expects each filesystem to be checked to have a +quota files named +.Pa quota.user +and +.Pa quota.group +which are located at the root of the associated file system. +These defaults may be overridden in +.Pa /etc/fstab . +If a file is not present, +.Nm quotacheck +will create it. +.Pp +.Nm Quotacheck +is normally run at boot time from the +.Pa /etc/rc.local +file, see +.Xr rc 8 , +before enabling disk quotas with +.Xr quotaon 8 . +.Pp +.Nm Quotacheck +accesses the raw device in calculating the actual +disk usage for each user. +Thus, the filesystems +checked should be quiescent while +.Nm quotacheck +is running. +.Sh FILES +.Bl -tag -width quota.group -compact +.It Pa quota.user +at the filesystem root with user quotas +.It Pa quota.group +at the filesystem root with group quotas +.It Pa /etc/fstab +default filesystems +.El +.Sh SEE ALSO +.Xr quota 1 , +.Xr quotactl 2 , +.Xr fstab 5 , +.Xr edquota 8 , +.Xr fsck 8 , +.Xr quotaon 8 , +.Xr repquota 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c new file mode 100644 index 0000000..1ee9e87 --- /dev/null +++ b/sbin/quotacheck/quotacheck.c @@ -0,0 +1,636 @@ +/* + * 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.3 (Berkeley) 1/29/94"; +#endif /* not lint */ + +/* + * Fix up / report on disk quotas & usage + */ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *qfname = QUOTAFILENAME; +char *qfextension[] = INITQFNAMES; +char *quotagroup = QUOTAGROUP; + +union { + struct fs sblk; + char dummy[MAXBSIZE]; +} un; +#define sblock un.sblk +long dev_bsize = 1; +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 err __P((const char *, ...)); +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; + char *argv[]; +{ + 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; + case 'g': + gflag++; + break; + case 'u': + uflag++; + break; + case 'v': + vflag++; + break; + case 'l': + maxrun = atoi(optarg); + break; + default: + usage(); + } + } + 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("%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); +} + +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("%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 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(); + 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; + } + } + } + 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 ((qfi = fopen(quotafile, "r")) == NULL) { + (void) fprintf(stderr, + "quotacheck: %s: %s\n", quotafile, strerror(errno)); + (void) fclose(qfo); + return (1); + } + 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 (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; + } + 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; + } + fclose(qfi); + fflush(qfo); + ftruncate(fileno(qfo), + (off_t)((highid[type] + 1) * sizeof(struct dqblk))); + fclose(qfo); + return (0); +} + +/* + * 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; + + for (i = 0; i < cnt; i++) + if (strcmp(target, list[i]) == 0) + return (i); + return (-1); +} + +/* + * Determine the group identifier for quota files. + */ +int +getquotagid() +{ + struct group *gr; + + if (gr = getgrnam(quotagroup)) + return (gr->gr_gid); + return (-1); +} + +/* + * 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]; + + 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 = index(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 * +lookup(id, type) + u_long id; + int type; +{ + register struct fileusage *fup; + + for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) + if (fup->fu_id == id) + return (fup); + return (NULL); +} + +/* + * 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 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("%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) + bcopy(name, fup->fu_name, len + 1); + else + (void)sprintf(fup->fu_name, "%u", id); + return (fup); +} + +/* + * 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("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() +{ + + 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("%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("block %ld", bno); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "quotacheck: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} -- cgit v1.1