summaryrefslogtreecommitdiffstats
path: root/sbin/quotacheck
diff options
context:
space:
mode:
authormpp <mpp@FreeBSD.org>2007-01-20 12:28:15 +0000
committermpp <mpp@FreeBSD.org>2007-01-20 12:28:15 +0000
commitd97f830fc18093c0ec4498e4d3e1802069acf788 (patch)
tree723378110f34e255f398f63f46d53922f61bb7db /sbin/quotacheck
parent0f6ed07b8918bfe5659030dbe819eec3e761dcfb (diff)
downloadFreeBSD-src-d97f830fc18093c0ec4498e4d3e1802069acf788.zip
FreeBSD-src-d97f830fc18093c0ec4498e4d3e1802069acf788.tar.gz
Quota system cleanup.
1) Do not account for uids/gids that appear negative to prevent the creation of 131GB+ quota files. This is the same as the kernel now determines which files to provide quota accounting for. Related to PR kern/38156. This should also prevent boots from hanging if a negative uid appears in the file systems. 2) Do not count system files in the usage counts. These currently are file system snapshot and quota data files. This is how the kernel now handles those files. 3) Correctly generate new quota data files if the current files do not exist or are zero length in size. PR kern/30958. It should now be possible to newfs / mount / touch quota.{user,group} and quotaon a file system and have everything work. 4) Change some diagnostics to report the file system and type of id (uid or gid) that is being reported. 5) Truncate the quota data files if possible, instead of letting them grow to a big enough size to hold the largest UID/GID on the system (typically "nobody"). The kernel should now be able to grow the files as needed without deadlocking the system. PR: kern/30958, kern/38156
Diffstat (limited to 'sbin/quotacheck')
-rw-r--r--sbin/quotacheck/quotacheck.c166
1 files changed, 137 insertions, 29 deletions
diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c
index b0a7df9..031ca15 100644
--- a/sbin/quotacheck/quotacheck.c
+++ b/sbin/quotacheck/quotacheck.c
@@ -115,10 +115,9 @@ 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(u_long, int, char *);
+ addid(u_long, int, char *, char *);
char *blockcheck(char *);
void bread(ufs2_daddr_t, char *, long);
extern int checkfstab(int, int, void * (*)(struct fstab *),
@@ -133,6 +132,7 @@ struct fileusage *
lookup(u_long, int);
void *needchk(struct fstab *);
int oneof(char *, char*[], int);
+void printchanges(char *, int, struct dqblk *, struct fileusage *);
void setinodebuf(ino_t);
int update(char *, char *, int);
void usage(void);
@@ -183,13 +183,15 @@ main(argc, argv)
if (gflag) {
setgrent();
while ((gr = getgrent()) != NULL)
- (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+ (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name,
+ NULL);
endgrent();
}
if (uflag) {
setpwent();
while ((pw = getpwent()) != NULL)
- (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
+ (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name,
+ NULL);
endpwent();
}
/*
@@ -271,8 +273,9 @@ chkquota(fsname, mntpt, qnp)
struct fileusage *fup;
union dinode *dp;
int cg, i, mode, errs = 0;
- ino_t ino, inosused;
+ ino_t ino, inosused, userino = 0, groupino = 0;
char *cp;
+ struct stat sb;
if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
warn("%s", fsname);
@@ -287,6 +290,14 @@ chkquota(fsname, mntpt, qnp)
(void)printf("%s", qfextension[GRPQUOTA]);
(void)printf(" quotas for %s (%s)\n", fsname, mntpt);
}
+ if (qnp->flags & HASUSR) {
+ if (stat(qnp->usrqfname, &sb) == 0)
+ userino = sb.st_ino;
+ }
+ if (qnp->flags & HASGRP) {
+ if (stat(qnp->grpqfname, &sb) == 0)
+ groupino = sb.st_ino;
+ }
sync();
dev_bsize = 1;
for (i = 0; sblock_try[i] != -1; i++) {
@@ -341,9 +352,37 @@ chkquota(fsname, mntpt, qnp)
if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
(mode = DIP(dp, di_mode) & IFMT) == 0)
continue;
+ /*
+ * XXX: Do not account for UIDs or GIDs that appear
+ * to be negative to prevent generating 100GB+
+ * quota files.
+ */
+ if ((int)DIP(dp, di_uid) < 0 ||
+ (int)DIP(dp, di_gid) < 0) {
+ if (vflag) {
+ if (aflag)
+ (void)printf("%s: ", mntpt);
+ (void)printf("out of range UID/GID (%u/%u) ino=%u\n",
+ DIP(dp, di_uid), DIP(dp,di_gid),
+ ino);
+ }
+ continue;
+ }
+
+ /*
+ * Do not account for file system snapshot files
+ * or the actual quota data files to be consistent
+ * with how they are handled inside the kernel.
+ */
+#ifdef SF_SNAPSHOT
+ if (DIP(dp, di_flags) & SF_SNAPSHOT)
+ continue;
+#endif
+ if (ino == userino || ino == groupino)
+ continue;
if (qnp->flags & HASGRP) {
fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
- (char *)0);
+ (char *)0, mntpt);
fup->fu_curinodes++;
if (mode == IFREG || mode == IFDIR ||
mode == IFLNK)
@@ -351,7 +390,7 @@ chkquota(fsname, mntpt, qnp)
}
if (qnp->flags & HASUSR) {
fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
- (char *)0);
+ (char *)0, mntpt);
fup->fu_curinodes++;
if (mode == IFREG || mode == IFDIR ||
mode == IFLNK)
@@ -378,9 +417,11 @@ update(fsname, quotafile, type)
{
struct fileusage *fup;
FILE *qfi, *qfo;
- u_long id, lastid;
+ u_long id, lastid, highid = 0;
off_t offset;
+ int i;
struct dqblk dqbuf;
+ struct stat sb;
static int warned = 0;
static struct dqblk zerodqbuf;
static struct fileusage zerofileusage;
@@ -409,32 +450,40 @@ update(fsname, quotafile, type)
(void)printf("*** Warning: %s\n",
"Quotas are not compiled into this kernel");
}
- for (lastid = highid[type], id = 0, offset = 0; id <= lastid;
+ if (fstat(fileno(qfi), &sb) < 0) {
+ warn("Cannot fstat quota file %s\n", quotafile);
+ (void) fclose(qfo);
+ (void) fclose(qfi);
+ return (1);
+ }
+ if ((sb.st_size % sizeof(struct dqblk)) != 0)
+ warn("%s size is not a multiple of dqblk\n", quotafile);
+
+ /*
+ * Scan the on-disk quota file and record any usage changes.
+ */
+
+ if (sb.st_size != 0)
+ lastid = (sb.st_size / sizeof(struct dqblk)) - 1;
+ else
+ lastid = 0;
+ for (id = 0, offset = 0; id <= lastid;
id++, offset += sizeof(struct dqblk)) {
if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
dqbuf = zerodqbuf;
- if ((fup = lookup(id, type)) == 0)
+ if ((fup = lookup(id, type)) == NULL)
fup = &zerofileusage;
+ if (fup->fu_curinodes || fup->fu_curblocks ||
+ dqbuf.dqb_bsoftlimit || dqbuf.dqb_bhardlimit ||
+ dqbuf.dqb_isoftlimit || dqbuf.dqb_ihardlimit)
+ highid = id;
if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
dqbuf.dqb_curblocks == fup->fu_curblocks) {
fup->fu_curinodes = 0;
fup->fu_curblocks = 0;
continue;
}
- if (vflag) {
- if (aflag)
- printf("%s: ", fsname);
- printf("%-8s fixed:", fup->fu_name);
- if (dqbuf.dqb_curinodes != fup->fu_curinodes)
- (void)printf("\tinodes %lu -> %lu",
- (u_long)dqbuf.dqb_curinodes,
- (u_long)fup->fu_curinodes);
- if (dqbuf.dqb_curblocks != fup->fu_curblocks)
- (void)printf("\tblocks %lu -> %lu",
- (u_long)dqbuf.dqb_curblocks,
- (u_long)fup->fu_curblocks);
- (void)printf("\n");
- }
+ printchanges(fsname, type, &dqbuf, fup);
/*
* Reset time limit if have a soft limit and were
* previously under it, but are now over it.
@@ -459,10 +508,41 @@ update(fsname, quotafile, type)
fup->fu_curinodes = 0;
fup->fu_curblocks = 0;
}
+
+ /*
+ * Walk the hash table looking for ids with non-zero usage
+ * that are not currently recorded in the quota file. E.g.
+ * ids that are past the end of the current file.
+ */
+
+ for (i = 0; i < FUHASH; i++) {
+ for (fup = fuhead[type][i]; fup != NULL; fup = fup->fu_next) {
+ if (fup->fu_id <= lastid)
+ continue;
+ if (fup->fu_curinodes == 0 && fup->fu_curblocks == 0)
+ continue;
+ bzero(&dqbuf, sizeof(struct dqblk));
+ if (fup->fu_id > highid)
+ highid = fup->fu_id;
+ printchanges(fsname, type, &dqbuf, fup);
+ dqbuf.dqb_curinodes = fup->fu_curinodes;
+ dqbuf.dqb_curblocks = fup->fu_curblocks;
+ offset = (off_t)fup->fu_id * sizeof(struct dqblk);
+ if (fseek(qfo, offset, SEEK_SET) < 0) {
+ warn("%s: seek failed", quotafile);
+ return(1);
+ }
+ 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)));
+ (((off_t)highid + 1) * sizeof(struct dqblk)));
fclose(qfo);
return (0);
}
@@ -560,10 +640,11 @@ lookup(id, type)
* Add a new file usage id if it does not already exist.
*/
struct fileusage *
-addid(id, type, name)
+addid(id, type, name, fsname)
u_long id;
int type;
char *name;
+ char *fsname;
{
struct fileusage *fup, **fhp;
int len;
@@ -580,15 +661,16 @@ addid(id, type, name)
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, "%lu", id);
- if (vflag)
+ if (vflag) {
+ if (aflag && fsname != NULL)
+ (void)printf("%s: ", fsname);
printf("unknown %cid: %lu\n",
type == USRQUOTA ? 'u' : 'g', id);
+ }
}
return (fup);
}
@@ -695,3 +777,29 @@ bread(bno, buf, cnt)
read(fi, buf, cnt) != cnt)
errx(1, "bread failed on block %ld", (long)bno);
}
+
+/*
+ * Display updated block and i-node counts.
+ */
+void
+printchanges(fsname, type, dp, fup)
+ char *fsname;
+ int type;
+ struct dqblk *dp;
+ struct fileusage *fup;
+{
+ if (!vflag)
+ return;
+ if (aflag)
+ (void)printf("%s: ", fsname);
+ (void)printf("%-8s fixed (%s):", fup->fu_name,
+ type == GRPQUOTA ? "group" : "user");
+ if (dp->dqb_curinodes != fup->fu_curinodes)
+ (void)printf("\tinodes %lu -> %lu", (u_long)dp->dqb_curinodes,
+ (u_long)fup->fu_curinodes);
+ if (dp->dqb_curblocks != fup->fu_curblocks)
+ (void)printf("\tblocks %lu -> %lu",
+ (u_long)dp->dqb_curblocks,
+ (u_long)fup->fu_curblocks);
+ (void)printf("\n");
+}
OpenPOWER on IntegriCloud