/*
* 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. 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.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
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 uint32_t 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 */
static int is_ufs2;
int
main(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;
const char *name;
int ch;
errs = maxrun = 0;
while ((ch = getopt(argc, argv, "aguvqdl:")) != -1) {
switch(ch) {
case 'a':
aflag++;
break;
case 'd':
flags |= CHECK_DEBUG;
break;
case 'g':
gflag++;
break;
case 'u':
uflag++;
break;
case 'q':
qflag++;
break;
case 'v':
vflag++;
break;
case 'l':
maxrun = atoi(optarg);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if ((argc == 0 && !aflag) || (argc > 0 && aflag) || (!aflag && maxrun))
usage();
if (!gflag && !uflag) {
gflag++;
uflag++;
}
/* If -a, we do not want to pay the cost of processing every
* group and password entry if there are no filesystems with quotas
*/
if (aflag) {
i = 0;
while ((fs = getfsent()) != NULL) {
if (needchk(fs))
i = 1;
}
endfsent();
if (!i) /* No filesystems with quotas */
return 0;
}
if (gflag) {
setgrent();
while ((gr = getgrent()) != 0)
(void) addid((uint32_t)gr->gr_gid, GRPQUOTA, gr->gr_name);
endgrent();
}
if (uflag) {
setpwent();
while ((pw = getpwent()) != 0)
(void) addid((uint32_t)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) {
const char *fsspec;
char buf[MAXPATHLEN];
fsspec = getfsspecname(buf, sizeof(buf), fs->fs_spec);
if (fsspec == NULL) {
warn("%s", buf);
continue;
}
if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
(argnum = oneof(fsspec, argv, argc)) >= 0) &&
(auxdata = needchk(fs)) &&
(name = blockcheck(fsspec))) {
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)
warnx("%s not found in %s", argv[i], FSTAB);
return errs;
}
nextid = subsequent(id, type);
/* watch out for id == UINT32_MAX */
if (nextid > 0 && nextid != id + 1)
nextid = skipforward(id, nextid, qfi);
if (got_siginfo) {
/*
* XXX this could try to show percentage through
* the ID list
*/
fprintf(stderr, "%s: updating %s quotas for id=%"
PRIu32 " (%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; /* reset usage */
fup->fu_curblocks = 0; /* for next filesystem */
need_seek = 1;
/* infinite loop avoidance (OR do as "nextid < id"?) */
if (id == UINT32_MAX || nextid == 0) {
break;
}
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_curinodes < dqbuf.dqb_isoftlimit &&
fup->fu_curinodes >= dqbuf.dqb_isoftlimit)
dqbuf.dqb_itime = 0;
dqbuf.dqb_curinodes = fup->fu_curinodes;
dqbuf.dqb_curblocks = fup->fu_curblocks;
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;
}
/*
* Determine the group identifier for quota files.
*/
static int
getquotagid(void)
{
struct group *gr;
/*
* Routines to manage the file usage table.
*
* Lookup an id of a specific type.
*/
static struct fileusage *
lookup(uint32_t id, int type)
{
struct fileusage *fup;
/*
* Add a new file usage id if it does not already exist.
*/
static struct fileusage *
addid(uint32_t id, int type, const char *name)
{
struct fileusage *fup, **fhp;
size_t 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)
memmove(fup->fu_name, name, len + 1);
else
(void)snprintf(fup->fu_name, len + 1, "%" PRIu32, id);
return fup;
}
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.
*/
static ino_t nextino, lastinum, lastvalidinum;
static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
static union comb_dinode *inodebuf;
#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */
static union comb_dinode *
getnextinode(ino_t inumber)
{
long size;
daddr_t dblk;
static union comb_dinode *dp;
union comb_dinode *ret;
if (inumber != nextino++ || inumber > lastvalidinum) {
errx(1, "bad inode number %llu to nextinode",
(unsigned long long)inumber);
}