Subject: bin/23971: quotacheck fixes
To: None <gnats-bugs@gnats.netbsd.org>
From: None <kre@munnari.OZ.AU>
List: netbsd-bugs
Date: 01/04/2004 12:39:27
>Number: 23971
>Category: bin
>Synopsis: quotacheck fixes
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: bin-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Jan 04 05:42:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator: Robert Elz
>Release: NetBSD 1.6T (--current as of 2004-01-03)
>Organization:
Prince of Songkla University
>Environment:
System: NetBSD delta.cs.mu.OZ.AU 1.6T NetBSD 1.6T (DELTA) #42: Thu May 29 12:23:16 ICT 2003 kre@fuchsia.cs.mu.OZ.AU:/usr/obj/sys/DELTA i386
Architecture: i386
Machine: i386
>Description:
sbin/quotacheck fails when the max uid is present, and is
generally inefficient any time there are large gaps in the
uid space. It also doesn't co-operate properly with the
fsck "preen" code that it uses to implement quotacheck -a.
>How-To-Repeat:
Try it and see...
>Fix:
Treat this as an author's update if you like.
Apply the following patch to quotacheck.c and quotacheck.8
Ignore the patch in PR bin/23725 - that one isn't correct,
isn't complete, and would cause problems (the PR should be
closed). This one should be applied.
--- quotacheck.c Thu Aug 7 23:24:53 2003
+++ quotacheck-new.c Sun Dec 28 05:30:21 2003
@@ -115,8 +115,9 @@
static int aflag; /* all file systems */
static int gflag; /* check group quotas */
static int uflag; /* check user quotas */
static int vflag; /* verbose */
+static int qflag; /* quick but untidy mode */
static int fi; /* open disk file descriptor */
static u_long highid[MAXQUOTAS];/* highest addid()'ed identifier per type */
static int needswap; /* FS is in swapped order */
static int got_siginfo = 0; /* got a siginfo signal */
@@ -128,13 +129,15 @@
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 u_long skipforward __P((u_long, u_long, FILE *));
static int oneof __P((const 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, const char *));
+static u_long subsequent __P((u_long, int));
static union dinode *getnextinode __P((ino_t));
static void setinodebuf __P((ino_t));
static void freeinodebuf __P((void));
static void bread __P((daddr_t, char *, long));
@@ -157,9 +160,9 @@
const char *name;
int ch;
errs = maxrun = 0;
- while ((ch = getopt(argc, argv, "aguvdl:")) != -1) {
+ while ((ch = getopt(argc, argv, "aguvqdl:")) != -1) {
switch(ch) {
case 'a':
aflag++;
break;
@@ -171,8 +174,11 @@
break;
case 'u':
uflag++;
break;
+ case 'q':
+ qflag++;
+ break;
case 'v':
vflag++;
break;
case 'l':
@@ -292,22 +298,27 @@
union dinode *dp;
int cg, i, mode, errs = 0, inosused;
ino_t ino;
struct cg *cgp;
+ char msgbuf[4096];
if (pid != NULL) {
+ fflush(stdout);
switch ((*pid = fork())) {
default:
- break;
- case 0:
return 0;
+ case 0:
+ break;
case -1:
err(1, "Cannot fork");
}
+ setvbuf(stdout, msgbuf, _IOFBF, sizeof msgbuf);
}
if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
warn("Cannot open %s", fsname);
+ if (pid != NULL)
+ exit(1);
return 1;
}
if (vflag) {
(void)printf("*** Checking ");
@@ -316,8 +327,9 @@
(qnp->flags & HASGRP) ? " and " : "");
if (qnp->flags & HASGRP)
(void)printf("%s", qfextension[GRPQUOTA]);
(void)printf(" quotas for %s (%s)\n", fsname, mntpt);
+ fflush(stdout);
}
signal(SIGINFO, infohandler);
sync();
dev_bsize = 1;
@@ -325,8 +337,10 @@
cgp = malloc(sblock.fs_cgsize);
if (cgp == NULL) {
warn("%s: can't allocate %d bytes of cg space", fsname,
sblock.fs_cgsize);
+ if (pid != NULL)
+ exit(1);
return 1;
}
for (i = 0; sblock_try[i] != -1; i++) {
@@ -348,10 +362,12 @@
}
}
warnx("%s: superblock not found", fsname);
free(cgp);
+ if (pid != NULL)
+ exit(1);
return 1;
-found:
+ found:;
if (needswap)
ffs_sb_swap(&sblock, &sblock);
dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
maxino = sblock.fs_ncg * sblock.fs_ipg;
@@ -403,8 +419,10 @@
errs += update(mntpt, qnp->usrqfname, USRQUOTA);
if (qnp->flags & HASGRP)
errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
close(fi);
+ if (pid != NULL)
+ exit(errs);
return errs;
}
/*
@@ -416,9 +434,10 @@
int type;
{
struct fileusage *fup;
FILE *qfi, *qfo;
- u_long id, lastid;
+ u_long id, lastid, nextid;
+ int need_seek;
struct dqblk dqbuf;
static int warned = 0;
static struct dqblk zerodqbuf;
static struct fileusage zerofileusage;
@@ -449,18 +468,35 @@
warned++;
(void)printf("*** Warning: %s\n",
"Quotas are not compiled into this kernel");
}
- for (lastid = highid[type], id = 0; id <= lastid; id++) {
+ need_seek = 1;
+ for (lastid = highid[type], id = 0; id <= lastid; id = nextid) {
if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
dqbuf = zerodqbuf;
if ((fup = lookup(id, type)) == 0)
fup = &zerofileusage;
+
+ nextid = subsequent(id, type);
+ if (nextid != id + 1)
+ nextid = skipforward(id, nextid, qfi);
+
+ if (got_siginfo) {
+ fprintf(stderr,
+ "%s: updating %s quotas for id=%ld (%s)\n", fsname,
+ qfextension[type < MAXQUOTAS ? type : MAXQUOTAS],
+ id, fup->fu_name);
+ got_siginfo = 0;
+ }
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);
+ fup->fu_curinodes = 0; /* reset usage */
+ fup->fu_curblocks = 0; /* for next filesystem */
+
+ need_seek = 1;
+
+ if (id == ULONG_MAX) /* ++id == 0, infinite loop */
+ break;
continue;
}
if (vflag) {
if (aflag)
@@ -487,22 +523,66 @@
fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
dqbuf.dqb_itime = 0;
dqbuf.dqb_curinodes = fup->fu_curinodes;
dqbuf.dqb_curblocks = fup->fu_curblocks;
+
+ if (need_seek) {
+ (void) fseeko(qfo, (off_t)id * sizeof(struct dqblk),
+ SEEK_SET);
+ need_seek = nextid != id + 1;
+ }
(void) fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
- (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
- (caddr_t)&dqbuf);
+
+ if (!warned)
+ (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
+ (caddr_t)&dqbuf);
+
fup->fu_curinodes = 0;
fup->fu_curblocks = 0;
+ if (id == ULONG_MAX)
+ break;
}
(void) fclose(qfi);
(void) fflush(qfo);
- (void) ftruncate(fileno(qfo),
- (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
+ if (highid[type] != ULONG_MAX)
+ (void) ftruncate(fileno(qfo),
+ (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
(void) fclose(qfo);
return (0);
}
+u_long
+skipforward(cur, to, qfi)
+ u_long cur, to;
+ FILE *qfi;
+{
+ struct dqblk dqbuf;
+
+ if (qflag) {
+ (void) fseeko(qfi, (off_t)to * sizeof(struct dqblk), SEEK_SET);
+ return (to);
+ }
+
+ while (++cur < to) {
+ /*
+ * if EOF occurs, nothing left to read, we're done
+ */
+ if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
+ return (to);
+
+ /*
+ * If we find an entry that shows usage, before the next
+ * id that has actual usage, we have to stop here, so the
+ * incorrect entry can be corrected in the file
+ */
+ if (dqbuf.dqb_curinodes != 0 || dqbuf.dqb_curblocks != 0) {
+ (void)fseek(qfi, -(long)sizeof(struct dqblk), SEEK_CUR);
+ return (cur);
+ }
+ }
+ return (to);
+}
+
/*
* Check to see if target appears in list of size cnt.
*/
static int
@@ -624,8 +704,34 @@
(void)sprintf(fup->fu_name, "%lu", id);
return (fup);
}
+static u_long
+subsequent(id, type)
+ u_long id;
+ int type;
+{
+ struct fileusage *fup, **iup, **cup;
+ u_long next, offset;
+
+ next = highid[type] + 1;
+ offset = 0;
+ cup = iup = &fuhead[type][id & (FUHASH-1)];
+ do {
+ ++offset;
+ if (++cup >= &fuhead[type][FUHASH])
+ cup = &fuhead[type][0];
+ for (fup = *cup; fup != 0; fup = fup->fu_next) {
+ if (fup->fu_id > id && fup->fu_id <= id + offset)
+ return (fup->fu_id);
+ if (fup->fu_id > id && fup->fu_id < next)
+ next = fup->fu_id;
+ }
+ } while (cup != iup);
+
+ return next;
+}
+
/*
* Special purpose version of ginode used to optimize first pass
* over all the inodes in numerical order.
*/
@@ -755,5 +861,4 @@
infohandler(int sig)
{
got_siginfo = 1;
}
-
--- quotacheck.8 Thu Aug 7 23:24:52 2003
+++ quotacheck-new.8 Fri Dec 12 18:38:24 2003
@@ -41,13 +41,15 @@
.Sh SYNOPSIS
.Nm
.Op Fl g
.Op Fl u
+.Op Fl q
.Op Fl v
.Ar filesystem Ar ...
.Nm
.Op Fl g
.Op Fl u
+.Op Fl q
.Op Fl v
.Fl a
.Sh DESCRIPTION
.Nm
@@ -81,11 +83,20 @@
.It Fl u
Only user quotas listed in
.Pa /etc/fstab
are to be checked.
+.It Fl q
+.Nm
+runs more quickly,
+particularly on systems with sparse user id usage,
+but fails to correct quotas for users [groups]
+not in the system user [group] database,
+and owning no files on the filesystem,
+if the quota file incorrectly believes that they do.
.It Fl v
.Nm
-reports discrepancies between the
+is more verbose,
+and reports corrected discrepancies between the
calculated and recorded disk quotas.
.El
.Pp
Specifying both
>Release-Note:
>Audit-Trail:
>Unformatted: