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
This commit is contained in:
parent
db9b81eabc
commit
f01beaa692
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=166143
@ -115,10 +115,9 @@ int gflag; /* check group quotas */
|
|||||||
int uflag; /* check user quotas */
|
int uflag; /* check user quotas */
|
||||||
int vflag; /* verbose */
|
int vflag; /* verbose */
|
||||||
int fi; /* open disk file descriptor */
|
int fi; /* open disk file descriptor */
|
||||||
u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
|
|
||||||
|
|
||||||
struct fileusage *
|
struct fileusage *
|
||||||
addid(u_long, int, char *);
|
addid(u_long, int, char *, char *);
|
||||||
char *blockcheck(char *);
|
char *blockcheck(char *);
|
||||||
void bread(ufs2_daddr_t, char *, long);
|
void bread(ufs2_daddr_t, char *, long);
|
||||||
extern int checkfstab(int, int, void * (*)(struct fstab *),
|
extern int checkfstab(int, int, void * (*)(struct fstab *),
|
||||||
@ -133,6 +132,7 @@ struct fileusage *
|
|||||||
lookup(u_long, int);
|
lookup(u_long, int);
|
||||||
void *needchk(struct fstab *);
|
void *needchk(struct fstab *);
|
||||||
int oneof(char *, char*[], int);
|
int oneof(char *, char*[], int);
|
||||||
|
void printchanges(char *, int, struct dqblk *, struct fileusage *);
|
||||||
void setinodebuf(ino_t);
|
void setinodebuf(ino_t);
|
||||||
int update(char *, char *, int);
|
int update(char *, char *, int);
|
||||||
void usage(void);
|
void usage(void);
|
||||||
@ -183,13 +183,15 @@ main(argc, argv)
|
|||||||
if (gflag) {
|
if (gflag) {
|
||||||
setgrent();
|
setgrent();
|
||||||
while ((gr = getgrent()) != NULL)
|
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();
|
endgrent();
|
||||||
}
|
}
|
||||||
if (uflag) {
|
if (uflag) {
|
||||||
setpwent();
|
setpwent();
|
||||||
while ((pw = getpwent()) != NULL)
|
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();
|
endpwent();
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@ -271,8 +273,9 @@ chkquota(fsname, mntpt, qnp)
|
|||||||
struct fileusage *fup;
|
struct fileusage *fup;
|
||||||
union dinode *dp;
|
union dinode *dp;
|
||||||
int cg, i, mode, errs = 0;
|
int cg, i, mode, errs = 0;
|
||||||
ino_t ino, inosused;
|
ino_t ino, inosused, userino = 0, groupino = 0;
|
||||||
char *cp;
|
char *cp;
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
|
if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
|
||||||
warn("%s", fsname);
|
warn("%s", fsname);
|
||||||
@ -287,6 +290,14 @@ chkquota(fsname, mntpt, qnp)
|
|||||||
(void)printf("%s", qfextension[GRPQUOTA]);
|
(void)printf("%s", qfextension[GRPQUOTA]);
|
||||||
(void)printf(" quotas for %s (%s)\n", fsname, mntpt);
|
(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();
|
sync();
|
||||||
dev_bsize = 1;
|
dev_bsize = 1;
|
||||||
for (i = 0; sblock_try[i] != -1; i++) {
|
for (i = 0; sblock_try[i] != -1; i++) {
|
||||||
@ -341,9 +352,37 @@ chkquota(fsname, mntpt, qnp)
|
|||||||
if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
|
if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
|
||||||
(mode = DIP(dp, di_mode) & IFMT) == 0)
|
(mode = DIP(dp, di_mode) & IFMT) == 0)
|
||||||
continue;
|
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) {
|
if (qnp->flags & HASGRP) {
|
||||||
fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
|
fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
|
||||||
(char *)0);
|
(char *)0, mntpt);
|
||||||
fup->fu_curinodes++;
|
fup->fu_curinodes++;
|
||||||
if (mode == IFREG || mode == IFDIR ||
|
if (mode == IFREG || mode == IFDIR ||
|
||||||
mode == IFLNK)
|
mode == IFLNK)
|
||||||
@ -351,7 +390,7 @@ chkquota(fsname, mntpt, qnp)
|
|||||||
}
|
}
|
||||||
if (qnp->flags & HASUSR) {
|
if (qnp->flags & HASUSR) {
|
||||||
fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
|
fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
|
||||||
(char *)0);
|
(char *)0, mntpt);
|
||||||
fup->fu_curinodes++;
|
fup->fu_curinodes++;
|
||||||
if (mode == IFREG || mode == IFDIR ||
|
if (mode == IFREG || mode == IFDIR ||
|
||||||
mode == IFLNK)
|
mode == IFLNK)
|
||||||
@ -378,9 +417,11 @@ update(fsname, quotafile, type)
|
|||||||
{
|
{
|
||||||
struct fileusage *fup;
|
struct fileusage *fup;
|
||||||
FILE *qfi, *qfo;
|
FILE *qfi, *qfo;
|
||||||
u_long id, lastid;
|
u_long id, lastid, highid = 0;
|
||||||
off_t offset;
|
off_t offset;
|
||||||
|
int i;
|
||||||
struct dqblk dqbuf;
|
struct dqblk dqbuf;
|
||||||
|
struct stat sb;
|
||||||
static int warned = 0;
|
static int warned = 0;
|
||||||
static struct dqblk zerodqbuf;
|
static struct dqblk zerodqbuf;
|
||||||
static struct fileusage zerofileusage;
|
static struct fileusage zerofileusage;
|
||||||
@ -409,32 +450,40 @@ update(fsname, quotafile, type)
|
|||||||
(void)printf("*** Warning: %s\n",
|
(void)printf("*** Warning: %s\n",
|
||||||
"Quotas are not compiled into this kernel");
|
"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)) {
|
id++, offset += sizeof(struct dqblk)) {
|
||||||
if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
|
if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
|
||||||
dqbuf = zerodqbuf;
|
dqbuf = zerodqbuf;
|
||||||
if ((fup = lookup(id, type)) == 0)
|
if ((fup = lookup(id, type)) == NULL)
|
||||||
fup = &zerofileusage;
|
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 &&
|
if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
|
||||||
dqbuf.dqb_curblocks == fup->fu_curblocks) {
|
dqbuf.dqb_curblocks == fup->fu_curblocks) {
|
||||||
fup->fu_curinodes = 0;
|
fup->fu_curinodes = 0;
|
||||||
fup->fu_curblocks = 0;
|
fup->fu_curblocks = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (vflag) {
|
printchanges(fsname, type, &dqbuf, fup);
|
||||||
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");
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Reset time limit if have a soft limit and were
|
* Reset time limit if have a soft limit and were
|
||||||
* previously under it, but are now over it.
|
* previously under it, but are now over it.
|
||||||
@ -459,10 +508,41 @@ update(fsname, quotafile, type)
|
|||||||
fup->fu_curinodes = 0;
|
fup->fu_curinodes = 0;
|
||||||
fup->fu_curblocks = 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);
|
fclose(qfi);
|
||||||
fflush(qfo);
|
fflush(qfo);
|
||||||
ftruncate(fileno(qfo),
|
ftruncate(fileno(qfo),
|
||||||
(((off_t)highid[type] + 1) * sizeof(struct dqblk)));
|
(((off_t)highid + 1) * sizeof(struct dqblk)));
|
||||||
fclose(qfo);
|
fclose(qfo);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
@ -560,10 +640,11 @@ lookup(id, type)
|
|||||||
* Add a new file usage id if it does not already exist.
|
* Add a new file usage id if it does not already exist.
|
||||||
*/
|
*/
|
||||||
struct fileusage *
|
struct fileusage *
|
||||||
addid(id, type, name)
|
addid(id, type, name, fsname)
|
||||||
u_long id;
|
u_long id;
|
||||||
int type;
|
int type;
|
||||||
char *name;
|
char *name;
|
||||||
|
char *fsname;
|
||||||
{
|
{
|
||||||
struct fileusage *fup, **fhp;
|
struct fileusage *fup, **fhp;
|
||||||
int len;
|
int len;
|
||||||
@ -580,16 +661,17 @@ addid(id, type, name)
|
|||||||
fup->fu_next = *fhp;
|
fup->fu_next = *fhp;
|
||||||
*fhp = fup;
|
*fhp = fup;
|
||||||
fup->fu_id = id;
|
fup->fu_id = id;
|
||||||
if (id > highid[type])
|
|
||||||
highid[type] = id;
|
|
||||||
if (name)
|
if (name)
|
||||||
bcopy(name, fup->fu_name, len + 1);
|
bcopy(name, fup->fu_name, len + 1);
|
||||||
else {
|
else {
|
||||||
(void)sprintf(fup->fu_name, "%lu", id);
|
(void)sprintf(fup->fu_name, "%lu", id);
|
||||||
if (vflag)
|
if (vflag) {
|
||||||
|
if (aflag && fsname != NULL)
|
||||||
|
(void)printf("%s: ", fsname);
|
||||||
printf("unknown %cid: %lu\n",
|
printf("unknown %cid: %lu\n",
|
||||||
type == USRQUOTA ? 'u' : 'g', id);
|
type == USRQUOTA ? 'u' : 'g', id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return (fup);
|
return (fup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,3 +777,29 @@ bread(bno, buf, cnt)
|
|||||||
read(fi, buf, cnt) != cnt)
|
read(fi, buf, cnt) != cnt)
|
||||||
errx(1, "bread failed on block %ld", (long)bno);
|
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");
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user