`quotacheck' has moved to /usr/sbin

This commit is contained in:
pk 1997-06-20 20:15:24 +00:00
parent 0c0c4a7ccc
commit 69e5f9827e
3 changed files with 0 additions and 804 deletions

View File

@ -1,10 +0,0 @@
# $NetBSD: Makefile,v 1.9 1997/05/08 21:11:31 gwr Exp $
# @(#)Makefile 8.1 (Berkeley) 6/5/93
PROG= quotacheck
SRCS= quotacheck.c fsutil.c preen.c
MAN= quotacheck.8
CFLAGS+=-I${.CURDIR}/../fsck
.PATH: ${.CURDIR}/../fsck
.include <bsd.prog.mk>

View File

@ -1,159 +0,0 @@
.\" $NetBSD: quotacheck.8,v 1.4 1995/03/18 14:59:20 cgd Exp $
.\"
.\" 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 .

View File

@ -1,635 +0,0 @@
/* $NetBSD: quotacheck.c,v 1.14 1996/09/28 19:20:44 christos Exp $ */
/*
* 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
#if 0
static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94";
#else
static char rcsid[] = "$NetBSD: quotacheck.c,v 1.14 1996/09/28 19:20:44 christos Exp $";
#endif
#endif /* not lint */
/*
* Fix up / report on disk quotas & usage
*/
#include <sys/param.h>
#include <sys/stat.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>
#include "fsutil.h"
static char *qfname = QUOTAFILENAME;
static char *qfextension[] = INITQFNAMES;
static char *quotagroup = QUOTAGROUP;
static union {
struct fs sblk;
char dummy[MAXBSIZE];
} un;
#define sblock un.sblk
static long dev_bsize;
static 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 */
static struct fileusage *fuhead[MAXQUOTAS][FUHASH];
static int aflag; /* all file systems */
static int gflag; /* check group quotas */
static int uflag; /* check user quotas */
static int vflag; /* verbose */
static int fi; /* open disk file descriptor */
static u_long highid[MAXQUOTAS];/* highest addid()'ed identifier per type */
int main __P((int, char *[]));
static void usage __P((void));
static void *needchk __P((struct fstab *));
static int chkquota __P((const char *, const char *, const char *, void *,
pid_t *));
static int update __P((const char *, const char *, int));
static int oneof __P((char *, char *[], int));
static int getquotagid __P((void));
static int hasquota __P((struct fstab *, int, char **));
static struct fileusage *lookup __P((u_long, int));
static struct fileusage *addid __P((u_long, int, char *));
static struct dinode *getnextinode __P((ino_t));
static void resetinodebuf __P((void));
static void freeinodebuf __P((void));
static void bread __P((daddr_t, char *, long));
int
main(argc, argv)
int argc;
char *argv[];
{
struct fstab *fs;
struct passwd *pw;
struct group *gr;
struct quotaname *auxdata;
int i, argnum, maxrun, errs;
long done = 0;
int flags = CHECK_PREEN;
char *name;
int ch;
errs = maxrun = 0;
while ((ch = getopt(argc, argv, "aguvdl:")) != -1) {
switch(ch) {
case 'a':
aflag++;
break;
case 'd':
flags |= CHECK_DEBUG;
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(flags, 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(fs->fs_type, name, fs->fs_file,
auxdata, NULL);
}
}
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);
}
static void
usage()
{
extern char *__progname;
(void)fprintf(stderr,
"Usage:\t%s -a [-guv]\n\t%s [-guv] filesys ...\n", __progname,
__progname);
exit(1);
}
static void *
needchk(fs)
struct fstab *fs;
{
struct quotaname *qnp;
char *qfnp;
if (strcmp(fs->fs_vfstype, "ffs") ||
strcmp(fs->fs_type, FSTAB_RW))
return (NULL);
if ((qnp = malloc(sizeof(*qnp))) == NULL)
err(1, "%s", strerror(errno));
qnp->flags = 0;
if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
strcpy(qnp->grpqfname, qfnp);
qnp->flags |= HASGRP;
}
if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
strcpy(qnp->usrqfname, qfnp);
qnp->flags |= HASUSR;
}
if (qnp->flags)
return (qnp);
free(qnp);
return (NULL);
}
/*
* Scan the specified filesystem to check quota(s) present on it.
*/
static int
chkquota(type, fsname, mntpt, v, pid)
const char *type, *fsname, *mntpt;
void *v;
pid_t *pid;
{
struct quotaname *qnp = v;
struct fileusage *fup;
struct dinode *dp;
int cg, i, mode, errs = 0;
ino_t ino;
if (pid != NULL) {
switch ((*pid = fork())) {
default:
break;
case 0:
return 0;
case -1:
err(1, "Cannot fork");
}
}
if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
warn("Cannot open %s", 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;
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.
*/
static int
update(fsname, quotafile, type)
const char *fsname, *quotafile;
int type;
{
struct fileusage *fup;
FILE *qfi, *qfo;
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;
(void) 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 -> %ld",
dqbuf.dqb_curinodes, fup->fu_curinodes);
if (dqbuf.dqb_curblocks != fup->fu_curblocks)
(void)printf("\tblocks %d -> %ld",
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;
(void) 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;
}
(void) fclose(qfi);
(void) fflush(qfo);
(void) ftruncate(fileno(qfo),
(off_t)((highid[type] + 1) * sizeof(struct dqblk)));
(void) fclose(qfo);
return (0);
}
/*
* Check to see if target appears in list of size cnt.
*/
static int
oneof(target, list, cnt)
char *target, *list[];
int cnt;
{
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.
*/
static int
getquotagid()
{
struct group *gr;
if ((gr = getgrnam(quotagroup)) != NULL)
return (gr->gr_gid);
return (-1);
}
/*
* Check to see if a particular quota is to be enabled.
*/
static int
hasquota(fs, type, qfnamep)
struct fstab *fs;
int type;
char **qfnamep;
{
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;
}
(void) strcpy(buf, fs->fs_mntops);
for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
if ((cp = strchr(opt, '=')) != NULL)
*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.
*/
static struct fileusage *
lookup(id, type)
u_long id;
int type;
{
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.
*/
static struct fileusage *
addid(id, type, name)
u_long id;
int type;
char *name;
{
struct fileusage *fup, **fhp;
int len;
if ((fup = lookup(id, type)) != NULL)
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)
memcpy(fup->fu_name, name, len + 1);
else
(void)sprintf(fup->fu_name, "%lu", id);
return (fup);
}
/*
* Special purpose version of ginode used to optimize pass
* over all the inodes in numerical order.
*/
static ino_t nextino, lastinum;
static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
static struct dinode *inodebuf;
#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */
static struct dinode *
getnextinode(inumber)
ino_t inumber;
{
long size;
daddr_t dblk;
static struct dinode *dp;
if (inumber != nextino++ || inumber > maxino)
err(1, "bad inode number %d to nextinode", inumber);
if (inumber >= lastinum) {
readcnt++;
dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
if (readcnt % readpercg == 0) {
size = partialsize;
lastinum += partialcnt;
} else {
size = inobufsize;
lastinum += fullcnt;
}
bread(dblk, (char *)inodebuf, size);
dp = inodebuf;
}
return (dp++);
}
/*
* Prepare to scan a set of inodes.
*/
static 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(1, "%s", strerror(errno));
while (nextino < ROOTINO)
getnextinode(nextino);
}
/*
* Free up data structures used to scan inodes.
*/
static void
freeinodebuf()
{
if (inodebuf != NULL)
free(inodebuf);
inodebuf = NULL;
}
/*
* Read specified disk blocks.
*/
static void
bread(bno, buf, cnt)
daddr_t bno;
char *buf;
long cnt;
{
if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
read(fi, buf, cnt) != cnt)
err(1, "block %d", bno);
}