Finish conversion of edquota to work with 64-bit quotas.

Add -h option to request "humanized" values such as 1M, 1G, 1T, etc.

Discussed with: Dag-Erling Smorgrav
Sponsored by: Rsync.net
This commit is contained in:
Kirk McKusick 2009-02-04 00:21:36 +00:00
parent 1b3515f39b
commit 08dc3a5e5c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/quota64/; revision=188108
3 changed files with 170 additions and 58 deletions

View File

@ -4,6 +4,7 @@
PROG= edquota
MAN= edquota.8
CSTD= c99
WARNS?= 4
DPADD= ${LIBUTIL}

View File

@ -42,6 +42,7 @@
.Op Fl u
.Op Fl f Ar fspath
.Op Fl p Ar proto-username
.Op Fl h
.Ar username ...
.Nm
.Op Fl u
@ -55,6 +56,7 @@
.Fl g
.Op Fl f Ar fspath
.Op Fl p Ar proto-groupname
.Op Fl h
.Ar groupname ...
.Nm
.Fl g
@ -97,6 +99,14 @@ unless the environment variable
specifies otherwise.
.Pp
The quotas may then be modified, new quotas added, etc.
Block quotas can be specified in bytes (B), kilobytes (K),
megabytes (M), terabytes (T), pedabytes (P), or exabytes (E).
If no units are specified, kilobytes are assumed.
If the
.Fl h
flag is specified, the editor will always display the
block usage and limits in a more human readable format
rather than displaying them in the historic kilobyte format.
Setting a quota to zero indicates that no quota should be imposed.
Setting a hard limit to one indicates that no allocations should
be permitted.
@ -159,6 +169,9 @@ and
.Ar ihlim
values is omitted, it is assumed to be zero, therefore
indicating that no particular quota should be imposed.
Block quotas can be specified in bytes (B), kilobytes (K),
megabytes (M), terabytes (T), pedabytes (P), or exabytes (E).
If no units are specified, kilobytes are assumed.
.Pp
If invoked with the
.Fl f

View File

@ -72,10 +72,22 @@ __FBSDID("$FreeBSD$");
#include "pathnames.h"
/* Let's be paranoid about block size */
#if 10 > DEV_BSHIFT
#define dbtokb(db) \
((off_t)(db) >> (10-DEV_BSHIFT))
#elif 10 < DEV_BSHIFT
#define dbtokb(db) \
((off_t)(db) << (DEV_BSHIFT-10))
#else
#define dbtokb(db) (db)
#endif
const char *qfname = QUOTAFILENAME;
const char *qfextension[] = INITQFNAMES;
const char *quotagroup = QUOTAGROUP;
char tmpfil[] = _PATH_TMP;
int hflag;
struct quotause {
struct quotause *next;
@ -87,9 +99,11 @@ struct quotause {
#define FOUND 0x01
int alldigits(const char *s);
int cvtatos(time_t, char *, time_t *);
char *cvtstoa(time_t);
int cvtatos(u_int64_t, char *, u_int64_t *);
char *cvtstoa(u_int64_t);
u_int64_t cvtval(u_int64_t, char);
int editit(char *);
char *fmthumanval(int64_t);
void freeprivs(struct quotause *);
int getentry(const char *, int);
struct quotause *getprivs(long, int, char *);
@ -106,11 +120,10 @@ main(int argc, char *argv[])
{
struct quotause *qup, *protoprivs, *curprivs;
long id, protoid;
uintmax_t lim;
int i, quotatype, range, tmpfd;
uid_t startuid, enduid;
u_int64_t *limp;
char *protoname, *cp, *oldoptarg, *end;
u_int64_t lim;
char *protoname, *cp, *endpt, *oldoptarg;
int eflag = 0, tflag = 0, pflag = 0, ch;
char *fspath = NULL;
char buf[MAXLOGNAME];
@ -123,7 +136,7 @@ main(int argc, char *argv[])
protoprivs = NULL;
curprivs = NULL;
protoname = NULL;
while ((ch = getopt(argc, argv, "ugtf:p:e:")) != -1) {
while ((ch = getopt(argc, argv, "ughtf:p:e:")) != -1) {
switch(ch) {
case 'f':
fspath = optarg;
@ -135,6 +148,9 @@ main(int argc, char *argv[])
case 'g':
quotatype = GRPQUOTA;
break;
case 'h':
hflag++;
break;
case 'u':
quotatype = USRQUOTA;
break;
@ -142,45 +158,44 @@ main(int argc, char *argv[])
tflag++;
break;
case 'e':
if ((qup = malloc(sizeof(*qup))) == NULL)
if ((qup = malloc(sizeof(*qup) + BUFSIZ)) == NULL)
errx(2, "out of memory");
bzero(qup, sizeof(*qup));
bzero(qup, sizeof(*qup) + BUFSIZ);
i = 0;
oldoptarg = optarg;
for (cp = optarg; (cp = strsep(&optarg, ":")) != NULL;
i++) {
if (cp != oldoptarg)
*(cp - 1) = ':';
limp = NULL;
switch (i) {
case 0:
strlcpy(qup->fsname, cp,
sizeof(qup->fsname));
break;
case 1:
limp = &qup->dqblk.dqb_bsoftlimit;
break;
lim = strtoll(cp, &endpt, 10);
qup->dqblk.dqb_bsoftlimit =
cvtval(lim, *endpt);
continue;
case 2:
limp = &qup->dqblk.dqb_bhardlimit;
break;
lim = strtoll(cp, &endpt, 10);
qup->dqblk.dqb_bhardlimit =
cvtval(lim, *endpt);
continue;
case 3:
limp = &qup->dqblk.dqb_isoftlimit;
break;
qup->dqblk.dqb_isoftlimit =
strtoll(cp, NULL, 10);
continue;
case 4:
limp = &qup->dqblk.dqb_ihardlimit;
break;
qup->dqblk.dqb_ihardlimit =
strtoll(cp, NULL, 10);
continue;
default:
warnx("incorrect quota specification: "
"%s", oldoptarg);
usage();
break; /* XXX: report an error */
}
if (limp != NULL) {
lim = strtoumax(cp, &end, 10);
if (end == cp || *end != '\0' || lim > UINT64_MAX)
errx(1, "invalid limit: %s", cp);
*limp = (u_int64_t)lim;
}
}
qup->dqblk.dqb_bsoftlimit =
btodb((off_t)qup->dqblk.dqb_bsoftlimit * 1024);
@ -285,10 +300,10 @@ static void
usage(void)
{
fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
"usage: edquota [-u] [-f fspath] [-p username] username ...",
"usage: edquota [-u] [-f fspath] [-p username] [-h] username ...",
" edquota [-u] -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]",
" username ...",
" edquota -g [-f fspath] [-p groupname] groupname ...",
" edquota -g [-f fspath] [-p groupname] [-h] groupname ...",
" edquota -g -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]",
" groupname ...",
" edquota [-u] -t [-f fspath]",
@ -314,14 +329,17 @@ getentry(const char *name, int quotatype)
if ((pw = getpwnam(name)))
return (pw->pw_uid);
warnx("%s: no such user", name);
sleep(3);
break;
case GRPQUOTA:
if ((gr = getgrnam(name)))
return (gr->gr_gid);
warnx("%s: no such group", name);
sleep(3);
break;
default:
warnx("%d: unknown quota type", quotatype);
sleep(3);
break;
}
sleep(1);
@ -499,21 +517,36 @@ writeprivs(struct quotause *quplist, int outfd, char *name, int quotatype)
err(1, "%s", tmpfil);
fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
for (qup = quplist; qup; qup = qup->next) {
fprintf(fd, "%s: %s %lu, limits (soft = %lu, hard = %lu)\n",
qup->fsname, "kbytes in use:",
(unsigned long)(dbtob(qup->dqblk.dqb_curblocks) / 1024),
(unsigned long)(dbtob(qup->dqblk.dqb_bsoftlimit) / 1024),
(unsigned long)(dbtob(qup->dqblk.dqb_bhardlimit) / 1024));
fprintf(fd, "%s %lu, limits (soft = %lu, hard = %lu)\n",
fprintf(fd, "%s: in use: %s, ", qup->fsname,
fmthumanval(qup->dqblk.dqb_curblocks));
fprintf(fd, "limits (soft = %s, ",
fmthumanval(qup->dqblk.dqb_bsoftlimit));
fprintf(fd, "hard = %s)\n",
fmthumanval(qup->dqblk.dqb_bhardlimit));
fprintf(fd, "%s %llu, limits (soft = %llu, hard = %llu)\n",
"\tinodes in use:",
(unsigned long)qup->dqblk.dqb_curinodes,
(unsigned long)qup->dqblk.dqb_isoftlimit,
(unsigned long)qup->dqblk.dqb_ihardlimit);
qup->dqblk.dqb_curinodes,
qup->dqblk.dqb_isoftlimit,
qup->dqblk.dqb_ihardlimit);
}
fclose(fd);
return (1);
}
char *
fmthumanval(int64_t blocks)
{
static char buf[7], numbuf[20];
if (hflag) {
humanize_number(buf, sizeof(buf) - (blocks < 0 ? 0 : 1),
dbtob(blocks), "", HN_AUTOSCALE, HN_NOSPACE);
return (buf);
}
snprintf(numbuf, 20, "%lluK", dbtokb(blocks));
return(numbuf);
}
/*
* Merge changes to an ASCII file into a quotause list.
*/
@ -522,8 +555,9 @@ readprivs(struct quotause *quplist, char *inname)
{
struct quotause *qup;
FILE *fd;
unsigned long bhardlimit, bsoftlimit, curblocks;
unsigned long ihardlimit, isoftlimit, curinodes;
u_int64_t ihardlimit, isoftlimit, curinodes;
u_int64_t bhardlimit, bsoftlimit, curblocks;
char bhardunits, bsoftunits, curblockunits;
int cnt;
char *cp;
struct dqblk dqblk;
@ -549,21 +583,40 @@ readprivs(struct quotause *quplist, char *inname)
return (0);
}
cnt = sscanf(cp,
" kbytes in use: %lu, limits (soft = %lu, hard = %lu)",
&curblocks, &bsoftlimit, &bhardlimit);
if (cnt != 3) {
" in use: %llu%c, limits (soft = %llu%c, hard = %llu%c)",
&curblocks, &curblockunits, &bsoftlimit, &bsoftunits,
&bhardlimit, &bhardunits);
/*
* The next three check for old-style input formats.
*/
if (cnt != 6)
cnt = sscanf(cp,
" in use: %llu%c, limits (soft = %llu%c hard = %llu%c",
&curblocks, &curblockunits, &bsoftlimit,
&bsoftunits, &bhardlimit, &bhardunits);
if (cnt != 6)
cnt = sscanf(cp,
" in use: %llu%c, limits (soft = %llu%c hard = %llu%c)",
&curblocks, &curblockunits, &bsoftlimit,
&bsoftunits, &bhardlimit, &bhardunits);
if (cnt != 6)
cnt = sscanf(cp,
" in use: %llu%c, limits (soft = %llu%c, hard = %llu%c",
&curblocks, &curblockunits, &bsoftlimit,
&bsoftunits, &bhardlimit, &bhardunits);
if (cnt != 6) {
warnx("%s:%s: bad format", fsp, cp);
return (0);
}
dqblk.dqb_curblocks = btodb((off_t)curblocks * 1024);
dqblk.dqb_bsoftlimit = btodb((off_t)bsoftlimit * 1024);
dqblk.dqb_bhardlimit = btodb((off_t)bhardlimit * 1024);
dqblk.dqb_curblocks = cvtval(curblocks, curblockunits);
dqblk.dqb_bsoftlimit = cvtval(bsoftlimit, bsoftunits);
dqblk.dqb_bhardlimit = cvtval(bhardlimit, bhardunits);
if ((cp = strtok(line2, "\n")) == NULL) {
warnx("%s: %s: bad format", fsp, line2);
return (0);
}
cnt = sscanf(cp,
"\tinodes in use: %lu, limits (soft = %lu, hard = %lu)",
"\tinodes in use: %llu, limits (soft = %llu, hard = %llu)",
&curinodes, &isoftlimit, &ihardlimit);
if (cnt != 3) {
warnx("%s: %s: bad format", fsp, line2);
@ -598,8 +651,8 @@ readprivs(struct quotause *quplist, char *inname)
qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
qup->flags |= FOUND;
if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
/* No easy way to check change in curblocks */
if (dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
break;
warnx("%s: cannot change current allocation", fsp);
break;
@ -658,8 +711,7 @@ readtimes(struct quotause *quplist, char *inname)
FILE *fd;
int cnt;
char *cp;
time_t itime, btime, iseconds, bseconds;
long l_itime, l_btime;
u_int64_t itime, btime, iseconds, bseconds;
char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
fd = fopen(inname, "r");
@ -682,14 +734,12 @@ readtimes(struct quotause *quplist, char *inname)
return (0);
}
cnt = sscanf(cp,
" block grace period: %ld %s file grace period: %ld %s",
&l_btime, bunits, &l_itime, iunits);
" block grace period: %llu %s file grace period: %llu %s",
&btime, bunits, &itime, iunits);
if (cnt != 4) {
warnx("%s:%s: bad format", fsp, cp);
return (0);
}
btime = l_btime;
itime = l_itime;
if (cvtatos(btime, bunits, &bseconds) == 0)
return (0);
if (cvtatos(itime, iunits, &iseconds) == 0)
@ -723,21 +773,21 @@ readtimes(struct quotause *quplist, char *inname)
* Convert seconds to ASCII times.
*/
char *
cvtstoa(time_t secs)
cvtstoa(u_int64_t secs)
{
static char buf[20];
if (secs % (24 * 60 * 60) == 0) {
secs /= 24 * 60 * 60;
sprintf(buf, "%ld day%s", (long)secs, secs == 1 ? "" : "s");
sprintf(buf, "%llu day%s", secs, secs == 1 ? "" : "s");
} else if (secs % (60 * 60) == 0) {
secs /= 60 * 60;
sprintf(buf, "%ld hour%s", (long)secs, secs == 1 ? "" : "s");
sprintf(buf, "%llu hour%s", secs, secs == 1 ? "" : "s");
} else if (secs % 60 == 0) {
secs /= 60;
sprintf(buf, "%ld minute%s", (long)secs, secs == 1 ? "" : "s");
sprintf(buf, "%llu minute%s", secs, secs == 1 ? "" : "s");
} else
sprintf(buf, "%ld second%s", (long)secs, secs == 1 ? "" : "s");
sprintf(buf, "%llu second%s", secs, secs == 1 ? "" : "s");
return (buf);
}
@ -745,7 +795,7 @@ cvtstoa(time_t secs)
* Convert ASCII input times to seconds.
*/
int
cvtatos(time_t period, char *units, time_t *seconds)
cvtatos(u_int64_t period, char *units, u_int64_t *seconds)
{
if (bcmp(units, "second", 6) == 0)
@ -757,13 +807,60 @@ cvtatos(time_t period, char *units, time_t *seconds)
else if (bcmp(units, "day", 3) == 0)
*seconds = period * 24 * 60 * 60;
else {
printf("%s: bad units, specify %s\n", units,
warnx("%s: bad units, specify %s\n", units,
"days, hours, minutes, or seconds");
return (0);
}
return (1);
}
/*
* Convert a limit to number of disk blocks.
*/
u_int64_t
cvtval(u_int64_t limit, char units)
{
switch(units) {
case 'B':
case 'b':
limit = btodb(limit);
break;
case '\0': /* historic behavior */
case ',': /* historic behavior */
case ')': /* historic behavior */
case 'K':
case 'k':
limit *= btodb(1024);
break;
case 'M':
case 'm':
limit *= btodb(1048576);
break;
case 'G':
case 'g':
limit *= btodb(1073741824);
break;
case 'T':
case 't':
limit *= btodb(1099511627776);
break;
case 'P':
case 'p':
limit *= btodb(1125899906842624);
break;
case 'E':
case 'e':
limit *= btodb(1152921504606846976);
break;
default:
warnx("%llu%c: unknown units, specify K, M, G, T, P, or E\n",
limit, units);
break;
}
return (limit);
}
/*
* Free a list of quotause structures.
*/
@ -833,6 +930,7 @@ hasquota(struct fstab *fs, int type, char **qfnamep)
}
if (statfs(fs->fs_file, &sfb) != 0) {
warn("cannot statfs mount point %s", fs->fs_file);
sleep(3);
return (0);
}
if (strcmp(fs->fs_file, sfb.f_mntonname)) {