11553Srgrimes/* 21553Srgrimes * Copyright (c) 1980, 1990, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * This code is derived from software contributed to Berkeley by 61553Srgrimes * Robert Elz at The University of Melbourne. 71553Srgrimes * 81553Srgrimes * Redistribution and use in source and binary forms, with or without 91553Srgrimes * modification, are permitted provided that the following conditions 101553Srgrimes * are met: 111553Srgrimes * 1. Redistributions of source code must retain the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer. 131553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141553Srgrimes * notice, this list of conditions and the following disclaimer in the 151553Srgrimes * documentation and/or other materials provided with the distribution. 161553Srgrimes * 4. Neither the name of the University nor the names of its contributors 171553Srgrimes * may be used to endorse or promote products derived from this software 181553Srgrimes * without specific prior written permission. 191553Srgrimes * 201553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301553Srgrimes * SUCH DAMAGE. 311553Srgrimes */ 321553Srgrimes 33114601Sobrien#if 0 341553Srgrimes#ifndef lint 3529529Scharnierstatic const char copyright[] = 361553Srgrimes"@(#) Copyright (c) 1980, 1990, 1993\n\ 371553Srgrimes The Regents of the University of California. All rights reserved.\n"; 381553Srgrimes#endif /* not lint */ 391553Srgrimes 401553Srgrimes#ifndef lint 411553Srgrimesstatic char sccsid[] = "@(#)edquota.c 8.1 (Berkeley) 6/6/93"; 42114601Sobrien#endif /* not lint */ 4329529Scharnier#endif 44162295Scharnier 45114601Sobrien#include <sys/cdefs.h> 46114601Sobrien__FBSDID("$FreeBSD$"); 471553Srgrimes 481553Srgrimes/* 491553Srgrimes * Disk quota editor. 501553Srgrimes */ 51162295Scharnier 521553Srgrimes#include <sys/param.h> 531553Srgrimes#include <sys/stat.h> 541553Srgrimes#include <sys/file.h> 55166485Smpp#include <sys/mount.h> 561553Srgrimes#include <sys/wait.h> 571553Srgrimes#include <ufs/ufs/quota.h> 58207736Smckusick 5929529Scharnier#include <ctype.h> 6029529Scharnier#include <err.h> 611553Srgrimes#include <errno.h> 621553Srgrimes#include <fstab.h> 6329529Scharnier#include <grp.h> 64207736Smckusick#include <inttypes.h> 65207736Smckusick#include <libutil.h> 661553Srgrimes#include <pwd.h> 6729529Scharnier#include <signal.h> 681553Srgrimes#include <stdio.h> 6929529Scharnier#include <stdlib.h> 701553Srgrimes#include <string.h> 711553Srgrimes#include <unistd.h> 72207736Smckusick 731553Srgrimes#include "pathnames.h" 741553Srgrimes 75207736Smckusick/* Let's be paranoid about block size */ 76207736Smckusick#if 10 > DEV_BSHIFT 77207736Smckusick#define dbtokb(db) \ 78207736Smckusick ((off_t)(db) >> (10-DEV_BSHIFT)) 79207736Smckusick#elif 10 < DEV_BSHIFT 80207736Smckusick#define dbtokb(db) \ 81207736Smckusick ((off_t)(db) << (DEV_BSHIFT-10)) 82207736Smckusick#else 83207736Smckusick#define dbtokb(db) (db) 84207736Smckusick#endif 85207736Smckusick 8687596Smikehconst char *qfextension[] = INITQFNAMES; 871553Srgrimeschar tmpfil[] = _PATH_TMP; 88207736Smckusickint hflag; 891553Srgrimes 901553Srgrimesstruct quotause { 911553Srgrimes struct quotause *next; 92207736Smckusick struct quotafile *qf; 931553Srgrimes struct dqblk dqblk; 94207736Smckusick int flags; 951553Srgrimes char fsname[MAXPATHLEN + 1]; 9629529Scharnier}; 971553Srgrimes#define FOUND 0x01 981553Srgrimes 9999800Salfredint alldigits(const char *s); 100207736Smckusickint cvtatos(uint64_t, char *, uint64_t *); 101207736Smckusickchar *cvtstoa(uint64_t); 102207736Smckusickuint64_t cvtblkval(uint64_t, char, const char *); 103207736Smckusickuint64_t cvtinoval(uint64_t, char, const char *); 10499800Salfredint editit(char *); 105207736Smckusickchar *fmthumanvalblks(int64_t); 106207736Smckusickchar *fmthumanvalinos(int64_t); 10799800Salfredvoid freeprivs(struct quotause *); 10899800Salfredint getentry(const char *, int); 10999800Salfredstruct quotause *getprivs(long, int, char *); 110207736Smckusickvoid putprivs(long, struct quotause *); 11199800Salfredint readprivs(struct quotause *, char *); 11299800Salfredint readtimes(struct quotause *, char *); 11399800Salfredstatic void usage(void); 11499800Salfredint writetimes(struct quotause *, int, int); 11599800Salfredint writeprivs(struct quotause *, int, char *, int); 11629529Scharnier 11729529Scharnierint 118180187Sdesmain(int argc, char *argv[]) 1191553Srgrimes{ 120103071Ssobomax struct quotause *qup, *protoprivs, *curprivs; 121103071Ssobomax long id, protoid; 122103071Ssobomax int i, quotatype, range, tmpfd; 123103071Ssobomax uid_t startuid, enduid; 124207736Smckusick uint64_t lim; 125207736Smckusick char *protoname, *cp, *endpt, *oldoptarg; 126124830Sgrehan int eflag = 0, tflag = 0, pflag = 0, ch; 12784081Syar char *fspath = NULL; 128126201Sceri char buf[MAXLOGNAME]; 1291553Srgrimes 1301553Srgrimes if (argc < 2) 1311553Srgrimes usage(); 13229529Scharnier if (getuid()) 13329529Scharnier errx(1, "permission denied"); 1341553Srgrimes quotatype = USRQUOTA; 135103071Ssobomax protoprivs = NULL; 136162295Scharnier curprivs = NULL; 137162295Scharnier protoname = NULL; 138207736Smckusick while ((ch = getopt(argc, argv, "ughtf:p:e:")) != -1) { 1391553Srgrimes switch(ch) { 14084081Syar case 'f': 14184081Syar fspath = optarg; 14284081Syar break; 1431553Srgrimes case 'p': 144207736Smckusick if (eflag) { 145207736Smckusick warnx("cannot specify both -e and -p"); 146207736Smckusick usage(); 147207736Smckusick /* not reached */ 148207736Smckusick } 1491553Srgrimes protoname = optarg; 1501553Srgrimes pflag++; 1511553Srgrimes break; 1521553Srgrimes case 'g': 1531553Srgrimes quotatype = GRPQUOTA; 1541553Srgrimes break; 155207736Smckusick case 'h': 156207736Smckusick hflag++; 157207736Smckusick break; 1581553Srgrimes case 'u': 1591553Srgrimes quotatype = USRQUOTA; 1601553Srgrimes break; 1611553Srgrimes case 't': 1621553Srgrimes tflag++; 1631553Srgrimes break; 164103071Ssobomax case 'e': 165207736Smckusick if (pflag) { 166207736Smckusick warnx("cannot specify both -e and -p"); 167207736Smckusick usage(); 168207736Smckusick /* not reached */ 169207736Smckusick } 170207736Smckusick if ((qup = calloc(1, sizeof(*qup))) == NULL) 171103071Ssobomax errx(2, "out of memory"); 172103071Ssobomax oldoptarg = optarg; 173207736Smckusick for (i = 0, cp = optarg; 174207736Smckusick (cp = strsep(&optarg, ":")) != NULL; i++) { 175103071Ssobomax if (cp != oldoptarg) 176103071Ssobomax *(cp - 1) = ':'; 177207736Smckusick if (i > 0 && !isdigit(*cp)) { 178207736Smckusick warnx("incorrect quota specification: " 179207736Smckusick "%s", oldoptarg); 180207736Smckusick usage(); 181207736Smckusick /* Not Reached */ 182207736Smckusick } 183103071Ssobomax switch (i) { 184103071Ssobomax case 0: 185103071Ssobomax strlcpy(qup->fsname, cp, 186103071Ssobomax sizeof(qup->fsname)); 187103071Ssobomax break; 188103071Ssobomax case 1: 189207736Smckusick lim = strtoll(cp, &endpt, 10); 190207736Smckusick qup->dqblk.dqb_bsoftlimit = 191207736Smckusick cvtblkval(lim, *endpt, 192207736Smckusick "block soft limit"); 193207736Smckusick continue; 194103071Ssobomax case 2: 195207736Smckusick lim = strtoll(cp, &endpt, 10); 196207736Smckusick qup->dqblk.dqb_bhardlimit = 197207736Smckusick cvtblkval(lim, *endpt, 198207736Smckusick "block hard limit"); 199207736Smckusick continue; 200103071Ssobomax case 3: 201207736Smckusick lim = strtoll(cp, &endpt, 10); 202207736Smckusick qup->dqblk.dqb_isoftlimit = 203207736Smckusick cvtinoval(lim, *endpt, 204207736Smckusick "inode soft limit"); 205207736Smckusick continue; 206103071Ssobomax case 4: 207207736Smckusick lim = strtoll(cp, &endpt, 10); 208207736Smckusick qup->dqblk.dqb_ihardlimit = 209207736Smckusick cvtinoval(lim, *endpt, 210207736Smckusick "inode hard limit"); 211207736Smckusick continue; 212103071Ssobomax default: 213103071Ssobomax warnx("incorrect quota specification: " 214103071Ssobomax "%s", oldoptarg); 215103071Ssobomax usage(); 216207736Smckusick /* Not Reached */ 217103071Ssobomax } 218103071Ssobomax } 219103071Ssobomax if (protoprivs == NULL) { 220103071Ssobomax protoprivs = curprivs = qup; 221103071Ssobomax } else { 222103071Ssobomax curprivs->next = qup; 223103071Ssobomax curprivs = qup; 224103071Ssobomax } 225103071Ssobomax eflag++; 226103071Ssobomax break; 2271553Srgrimes default: 2281553Srgrimes usage(); 229207736Smckusick /* Not Reached */ 2301553Srgrimes } 2311553Srgrimes } 2321553Srgrimes argc -= optind; 2331553Srgrimes argv += optind; 234207736Smckusick if (pflag || eflag) { 235207736Smckusick if (pflag) { 236103071Ssobomax if ((protoid = getentry(protoname, quotatype)) == -1) 237103071Ssobomax exit(1); 238103071Ssobomax protoprivs = getprivs(protoid, quotatype, fspath); 239207736Smckusick if (protoprivs == NULL) 240207736Smckusick exit(0); 241103071Ssobomax for (qup = protoprivs; qup; qup = qup->next) { 242103071Ssobomax qup->dqblk.dqb_btime = 0; 243103071Ssobomax qup->dqblk.dqb_itime = 0; 244103071Ssobomax } 2451553Srgrimes } 246101546Siedowse for (; argc-- > 0; argv++) { 247101546Siedowse if (strspn(*argv, "0123456789-") == strlen(*argv) && 24814961Smpp (cp = strchr(*argv, '-')) != NULL) { 24914961Smpp *cp++ = '\0'; 25014961Smpp startuid = atoi(*argv); 25114961Smpp enduid = atoi(cp); 25229529Scharnier if (enduid < startuid) 25329529Scharnier errx(1, 25429529Scharnier "ending uid (%d) must be >= starting uid (%d) when using uid ranges", 25514961Smpp enduid, startuid); 256103071Ssobomax range = 1; 257103071Ssobomax } else { 258103071Ssobomax startuid = enduid = 0; 259103071Ssobomax range = 0; 260103071Ssobomax } 261103071Ssobomax for ( ; startuid <= enduid; startuid++) { 262103071Ssobomax if (range) 263103071Ssobomax snprintf(buf, sizeof(buf), "%d", 26414961Smpp startuid); 265103071Ssobomax else 266103071Ssobomax snprintf(buf, sizeof(buf), "%s", 267103071Ssobomax *argv); 268103071Ssobomax if ((id = getentry(buf, quotatype)) < 0) 269103071Ssobomax continue; 270207736Smckusick if (pflag) { 271207736Smckusick putprivs(id, protoprivs); 272207736Smckusick continue; 27314961Smpp } 274207736Smckusick for (qup = protoprivs; qup; qup = qup->next) { 275207736Smckusick curprivs = getprivs(id, quotatype, 276207736Smckusick qup->fsname); 277207736Smckusick if (curprivs == NULL) 278207736Smckusick continue; 279207736Smckusick curprivs->dqblk = qup->dqblk; 280207736Smckusick putprivs(id, curprivs); 281207736Smckusick freeprivs(curprivs); 282207736Smckusick } 28314961Smpp } 2841553Srgrimes } 285207736Smckusick if (pflag) 286207736Smckusick freeprivs(protoprivs); 2871553Srgrimes exit(0); 2881553Srgrimes } 2891553Srgrimes tmpfd = mkstemp(tmpfil); 2901553Srgrimes fchown(tmpfd, getuid(), getgid()); 2911553Srgrimes if (tflag) { 292207736Smckusick if ((protoprivs = getprivs(0, quotatype, fspath)) != NULL) { 293207736Smckusick if (writetimes(protoprivs, tmpfd, quotatype) != 0 && 294207736Smckusick editit(tmpfil) && readtimes(protoprivs, tmpfil)) 295207736Smckusick putprivs(0L, protoprivs); 296207736Smckusick freeprivs(protoprivs); 297207736Smckusick } 29828431Sjlemon close(tmpfd); 29928431Sjlemon unlink(tmpfil); 3001553Srgrimes exit(0); 3011553Srgrimes } 3021553Srgrimes for ( ; argc > 0; argc--, argv++) { 3031553Srgrimes if ((id = getentry(*argv, quotatype)) == -1) 3041553Srgrimes continue; 305207736Smckusick if ((curprivs = getprivs(id, quotatype, fspath)) == NULL) 306207736Smckusick exit(1); 3071553Srgrimes if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0) 3081553Srgrimes continue; 3094782Sache if (editit(tmpfil) && readprivs(curprivs, tmpfil)) 310207736Smckusick putprivs(id, curprivs); 3111553Srgrimes freeprivs(curprivs); 3121553Srgrimes } 3131553Srgrimes close(tmpfd); 3141553Srgrimes unlink(tmpfil); 3151553Srgrimes exit(0); 3161553Srgrimes} 3171553Srgrimes 31829529Scharnierstatic void 319180187Sdesusage(void) 3201553Srgrimes{ 321103071Ssobomax fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 322207736Smckusick "usage: edquota [-uh] [-f fspath] [-p username] username ...", 323103071Ssobomax " edquota [-u] -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]", 324103071Ssobomax " username ...", 325207736Smckusick " edquota -g [-h] [-f fspath] [-p groupname] groupname ...", 326103071Ssobomax " edquota -g -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]", 327103071Ssobomax " groupname ...", 32884081Syar " edquota [-u] -t [-f fspath]", 32984081Syar " edquota -g -t [-f fspath]"); 3301553Srgrimes exit(1); 3311553Srgrimes} 3321553Srgrimes 3331553Srgrimes/* 3341553Srgrimes * This routine converts a name for a particular quota type to 3351553Srgrimes * an identifier. This routine must agree with the kernel routine 3361553Srgrimes * getinoquota as to the interpretation of quota types. 3371553Srgrimes */ 33829529Scharnierint 339180187Sdesgetentry(const char *name, int quotatype) 3401553Srgrimes{ 3411553Srgrimes struct passwd *pw; 3421553Srgrimes struct group *gr; 3431553Srgrimes 3441553Srgrimes if (alldigits(name)) 3451553Srgrimes return (atoi(name)); 3461553Srgrimes switch(quotatype) { 3471553Srgrimes case USRQUOTA: 34829529Scharnier if ((pw = getpwnam(name))) 3491553Srgrimes return (pw->pw_uid); 35029529Scharnier warnx("%s: no such user", name); 351207736Smckusick sleep(3); 3521553Srgrimes break; 3531553Srgrimes case GRPQUOTA: 35429529Scharnier if ((gr = getgrnam(name))) 3551553Srgrimes return (gr->gr_gid); 35629529Scharnier warnx("%s: no such group", name); 357207736Smckusick sleep(3); 3581553Srgrimes break; 3591553Srgrimes default: 36029529Scharnier warnx("%d: unknown quota type", quotatype); 361207736Smckusick sleep(3); 3621553Srgrimes break; 3631553Srgrimes } 3641553Srgrimes sleep(1); 3651553Srgrimes return (-1); 3661553Srgrimes} 3671553Srgrimes 3681553Srgrimes/* 3691553Srgrimes * Collect the requested quota information. 3701553Srgrimes */ 3711553Srgrimesstruct quotause * 372180187Sdesgetprivs(long id, int quotatype, char *fspath) 3731553Srgrimes{ 374207736Smckusick struct quotafile *qf; 375180187Sdes struct fstab *fs; 376180187Sdes struct quotause *qup, *quptail; 3771553Srgrimes struct quotause *quphead; 3781553Srgrimes 3791553Srgrimes setfsent(); 380162295Scharnier quphead = quptail = NULL; 38129529Scharnier while ((fs = getfsent())) { 38284081Syar if (fspath && *fspath && strcmp(fspath, fs->fs_spec) && 38384081Syar strcmp(fspath, fs->fs_file)) 38484081Syar continue; 3851553Srgrimes if (strcmp(fs->fs_vfstype, "ufs")) 3861553Srgrimes continue; 387207736Smckusick if ((qf = quota_open(fs, quotatype, O_CREAT|O_RDWR)) == NULL) { 388207736Smckusick if (errno != EOPNOTSUPP) 389207736Smckusick warn("cannot open quotas on %s", fs->fs_file); 3901553Srgrimes continue; 391207736Smckusick } 392207736Smckusick if ((qup = (struct quotause *)calloc(1, sizeof(*qup))) == NULL) 39329529Scharnier errx(2, "out of memory"); 394207736Smckusick qup->qf = qf; 395207736Smckusick strncpy(qup->fsname, fs->fs_file, sizeof(qup->fsname)); 396207736Smckusick if (quota_read(qf, &qup->dqblk, id) == -1) { 397207736Smckusick warn("cannot read quotas on %s", fs->fs_file); 398207736Smckusick freeprivs(qup); 399207736Smckusick continue; 4001553Srgrimes } 4011553Srgrimes if (quphead == NULL) 4021553Srgrimes quphead = qup; 4031553Srgrimes else 4041553Srgrimes quptail->next = qup; 4051553Srgrimes quptail = qup; 4061553Srgrimes qup->next = 0; 4071553Srgrimes } 408207736Smckusick if (quphead == NULL) { 409207736Smckusick warnx("No quotas on %s", fspath ? fspath : "any filesystems"); 410207736Smckusick } 4111553Srgrimes endfsent(); 4121553Srgrimes return (quphead); 4131553Srgrimes} 4141553Srgrimes 4151553Srgrimes/* 4161553Srgrimes * Store the requested quota information. 4171553Srgrimes */ 41829529Scharniervoid 419207736Smckusickputprivs(long id, struct quotause *quplist) 4201553Srgrimes{ 421180187Sdes struct quotause *qup; 4221553Srgrimes 423207736Smckusick for (qup = quplist; qup; qup = qup->next) 424207736Smckusick if (quota_write_limits(qup->qf, &qup->dqblk, id) == -1) 425207736Smckusick warn("%s", qup->fsname); 4261553Srgrimes} 4271553Srgrimes 4281553Srgrimes/* 4291553Srgrimes * Take a list of priviledges and get it edited. 4301553Srgrimes */ 43129529Scharnierint 432180187Sdeseditit(char *tmpf) 4331553Srgrimes{ 4341553Srgrimes long omask; 43587596Smikeh int pid, status; 4361553Srgrimes 4371553Srgrimes omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 4381553Srgrimes top: 4391553Srgrimes if ((pid = fork()) < 0) { 4401553Srgrimes 4411553Srgrimes if (errno == EPROCLIM) { 44229529Scharnier warnx("you have too many processes"); 4431553Srgrimes return(0); 4441553Srgrimes } 4451553Srgrimes if (errno == EAGAIN) { 4461553Srgrimes sleep(1); 4471553Srgrimes goto top; 4481553Srgrimes } 44929529Scharnier warn("fork"); 4501553Srgrimes return (0); 4511553Srgrimes } 4521553Srgrimes if (pid == 0) { 453180187Sdes const char *ed; 4541553Srgrimes 4551553Srgrimes sigsetmask(omask); 456242166Seadler if (setgid(getgid()) != 0) 457242166Seadler err(1, "setgid failed"); 458242166Seadler if (setuid(getuid()) != 0) 459242166Seadler err(1, "setuid failed"); 4601553Srgrimes if ((ed = getenv("EDITOR")) == (char *)0) 4611553Srgrimes ed = _PATH_VI; 46287596Smikeh execlp(ed, ed, tmpf, (char *)0); 46329529Scharnier err(1, "%s", ed); 4641553Srgrimes } 46587596Smikeh waitpid(pid, &status, 0); 4661553Srgrimes sigsetmask(omask); 46787596Smikeh if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 4681553Srgrimes return (0); 4691553Srgrimes return (1); 4701553Srgrimes} 4711553Srgrimes 4721553Srgrimes/* 4731553Srgrimes * Convert a quotause list to an ASCII file. 4741553Srgrimes */ 47529529Scharnierint 476180187Sdeswriteprivs(struct quotause *quplist, int outfd, char *name, int quotatype) 4771553Srgrimes{ 478180187Sdes struct quotause *qup; 4791553Srgrimes FILE *fd; 4801553Srgrimes 4811553Srgrimes ftruncate(outfd, 0); 4821553Srgrimes lseek(outfd, 0, L_SET); 48329529Scharnier if ((fd = fdopen(dup(outfd), "w")) == NULL) 48429529Scharnier err(1, "%s", tmpfil); 4851553Srgrimes fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name); 4861553Srgrimes for (qup = quplist; qup; qup = qup->next) { 487207736Smckusick fprintf(fd, "%s: in use: %s, ", qup->fsname, 488207736Smckusick fmthumanvalblks(qup->dqblk.dqb_curblocks)); 489207736Smckusick fprintf(fd, "limits (soft = %s, ", 490207736Smckusick fmthumanvalblks(qup->dqblk.dqb_bsoftlimit)); 491207736Smckusick fprintf(fd, "hard = %s)\n", 492207736Smckusick fmthumanvalblks(qup->dqblk.dqb_bhardlimit)); 493207736Smckusick fprintf(fd, "\tinodes in use: %s, ", 494207736Smckusick fmthumanvalinos(qup->dqblk.dqb_curinodes)); 495207736Smckusick fprintf(fd, "limits (soft = %s, ", 496207736Smckusick fmthumanvalinos(qup->dqblk.dqb_isoftlimit)); 497207736Smckusick fprintf(fd, "hard = %s)\n", 498207736Smckusick fmthumanvalinos(qup->dqblk.dqb_ihardlimit)); 4991553Srgrimes } 5001553Srgrimes fclose(fd); 5011553Srgrimes return (1); 5021553Srgrimes} 5031553Srgrimes 504207736Smckusickchar * 505207736Smckusickfmthumanvalblks(int64_t blocks) 506207736Smckusick{ 507207736Smckusick static char numbuf[20]; 508207736Smckusick 509207736Smckusick if (hflag) { 510207736Smckusick humanize_number(numbuf, blocks < 0 ? 7 : 6, 511207736Smckusick dbtob(blocks), "", HN_AUTOSCALE, HN_NOSPACE); 512207736Smckusick return (numbuf); 513207736Smckusick } 514207736Smckusick snprintf(numbuf, sizeof(numbuf), "%juk", (uintmax_t)dbtokb(blocks)); 515207736Smckusick return(numbuf); 516207736Smckusick} 517207736Smckusick 518207736Smckusickchar * 519207736Smckusickfmthumanvalinos(int64_t inos) 520207736Smckusick{ 521207736Smckusick static char numbuf[20]; 522207736Smckusick 523207736Smckusick if (hflag) { 524207736Smckusick humanize_number(numbuf, inos < 0 ? 7 : 6, 525207736Smckusick inos, "", HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000); 526207736Smckusick return (numbuf); 527207736Smckusick } 528207736Smckusick snprintf(numbuf, sizeof(numbuf), "%ju", (uintmax_t)inos); 529207736Smckusick return(numbuf); 530207736Smckusick} 531207736Smckusick 5321553Srgrimes/* 5331553Srgrimes * Merge changes to an ASCII file into a quotause list. 5341553Srgrimes */ 53529529Scharnierint 536180187Sdesreadprivs(struct quotause *quplist, char *inname) 5371553Srgrimes{ 538180187Sdes struct quotause *qup; 5391553Srgrimes FILE *fd; 540207736Smckusick uintmax_t hardlimit, softlimit, curitems; 541207736Smckusick char hardunits, softunits, curitemunits; 5421553Srgrimes int cnt; 543180187Sdes char *cp; 5441553Srgrimes struct dqblk dqblk; 5451553Srgrimes char *fsp, line1[BUFSIZ], line2[BUFSIZ]; 5461553Srgrimes 5474782Sache fd = fopen(inname, "r"); 5481553Srgrimes if (fd == NULL) { 54929529Scharnier warnx("can't re-read temp file!!"); 5501553Srgrimes return (0); 5511553Srgrimes } 5521553Srgrimes /* 5531553Srgrimes * Discard title line, then read pairs of lines to process. 5541553Srgrimes */ 5551553Srgrimes (void) fgets(line1, sizeof (line1), fd); 5561553Srgrimes while (fgets(line1, sizeof (line1), fd) != NULL && 5571553Srgrimes fgets(line2, sizeof (line2), fd) != NULL) { 5581553Srgrimes if ((fsp = strtok(line1, " \t:")) == NULL) { 55929529Scharnier warnx("%s: bad format", line1); 5601553Srgrimes return (0); 5611553Srgrimes } 5621553Srgrimes if ((cp = strtok((char *)0, "\n")) == NULL) { 56329529Scharnier warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]); 5641553Srgrimes return (0); 5651553Srgrimes } 5661553Srgrimes cnt = sscanf(cp, 567207736Smckusick " in use: %ju%c, limits (soft = %ju%c, hard = %ju%c)", 568207736Smckusick &curitems, &curitemunits, &softlimit, &softunits, 569207736Smckusick &hardlimit, &hardunits); 570207736Smckusick /* 571207736Smckusick * The next three check for old-style input formats. 572207736Smckusick */ 573207736Smckusick if (cnt != 6) 574207736Smckusick cnt = sscanf(cp, 575207736Smckusick " in use: %ju%c, limits (soft = %ju%c hard = %ju%c", 576207736Smckusick &curitems, &curitemunits, &softlimit, 577207736Smckusick &softunits, &hardlimit, &hardunits); 578207736Smckusick if (cnt != 6) 579207736Smckusick cnt = sscanf(cp, 580207736Smckusick " in use: %ju%c, limits (soft = %ju%c hard = %ju%c)", 581207736Smckusick &curitems, &curitemunits, &softlimit, 582207736Smckusick &softunits, &hardlimit, &hardunits); 583207736Smckusick if (cnt != 6) 584207736Smckusick cnt = sscanf(cp, 585207736Smckusick " in use: %ju%c, limits (soft = %ju%c, hard = %ju%c", 586207736Smckusick &curitems, &curitemunits, &softlimit, 587207736Smckusick &softunits, &hardlimit, &hardunits); 588207736Smckusick if (cnt != 6) { 58929529Scharnier warnx("%s:%s: bad format", fsp, cp); 5901553Srgrimes return (0); 5911553Srgrimes } 592207736Smckusick dqblk.dqb_curblocks = cvtblkval(curitems, curitemunits, 593207736Smckusick "current block count"); 594207736Smckusick dqblk.dqb_bsoftlimit = cvtblkval(softlimit, softunits, 595207736Smckusick "block soft limit"); 596207736Smckusick dqblk.dqb_bhardlimit = cvtblkval(hardlimit, hardunits, 597207736Smckusick "block hard limit"); 5981553Srgrimes if ((cp = strtok(line2, "\n")) == NULL) { 59929529Scharnier warnx("%s: %s: bad format", fsp, line2); 6001553Srgrimes return (0); 6011553Srgrimes } 602207736Smckusick cnt = sscanf(&cp[7], 603207736Smckusick " in use: %ju%c limits (soft = %ju%c, hard = %ju%c)", 604207736Smckusick &curitems, &curitemunits, &softlimit, 605207736Smckusick &softunits, &hardlimit, &hardunits); 606207736Smckusick /* 607207736Smckusick * The next three check for old-style input formats. 608207736Smckusick */ 609207736Smckusick if (cnt != 6) 610207736Smckusick cnt = sscanf(&cp[7], 611207736Smckusick " in use: %ju%c limits (soft = %ju%c hard = %ju%c", 612207736Smckusick &curitems, &curitemunits, &softlimit, 613207736Smckusick &softunits, &hardlimit, &hardunits); 614207736Smckusick if (cnt != 6) 615207736Smckusick cnt = sscanf(&cp[7], 616207736Smckusick " in use: %ju%c limits (soft = %ju%c hard = %ju%c)", 617207736Smckusick &curitems, &curitemunits, &softlimit, 618207736Smckusick &softunits, &hardlimit, &hardunits); 619207736Smckusick if (cnt != 6) 620207736Smckusick cnt = sscanf(&cp[7], 621207736Smckusick " in use: %ju%c limits (soft = %ju%c, hard = %ju%c", 622207736Smckusick &curitems, &curitemunits, &softlimit, 623207736Smckusick &softunits, &hardlimit, &hardunits); 624207736Smckusick if (cnt != 6) { 625207736Smckusick warnx("%s: %s: bad format cnt %d", fsp, &cp[7], cnt); 6261553Srgrimes return (0); 6271553Srgrimes } 628207736Smckusick dqblk.dqb_curinodes = cvtinoval(curitems, curitemunits, 629207736Smckusick "current inode count"); 630207736Smckusick dqblk.dqb_isoftlimit = cvtinoval(softlimit, softunits, 631207736Smckusick "inode soft limit"); 632207736Smckusick dqblk.dqb_ihardlimit = cvtinoval(hardlimit, hardunits, 633207736Smckusick "inode hard limit"); 6341553Srgrimes for (qup = quplist; qup; qup = qup->next) { 6351553Srgrimes if (strcmp(fsp, qup->fsname)) 6361553Srgrimes continue; 6371553Srgrimes /* 6381553Srgrimes * Cause time limit to be reset when the quota 6391553Srgrimes * is next used if previously had no soft limit 6401553Srgrimes * or were under it, but now have a soft limit 6411553Srgrimes * and are over it. 6421553Srgrimes */ 6431553Srgrimes if (dqblk.dqb_bsoftlimit && 6441553Srgrimes qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit && 6451553Srgrimes (qup->dqblk.dqb_bsoftlimit == 0 || 6461553Srgrimes qup->dqblk.dqb_curblocks < 6471553Srgrimes qup->dqblk.dqb_bsoftlimit)) 6481553Srgrimes qup->dqblk.dqb_btime = 0; 6491553Srgrimes if (dqblk.dqb_isoftlimit && 6501553Srgrimes qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit && 6511553Srgrimes (qup->dqblk.dqb_isoftlimit == 0 || 6521553Srgrimes qup->dqblk.dqb_curinodes < 6531553Srgrimes qup->dqblk.dqb_isoftlimit)) 6541553Srgrimes qup->dqblk.dqb_itime = 0; 6551553Srgrimes qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit; 6561553Srgrimes qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit; 6571553Srgrimes qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit; 6581553Srgrimes qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit; 6591553Srgrimes qup->flags |= FOUND; 660207736Smckusick /* Humanized input returns only approximate counts */ 661207736Smckusick if (hflag || 662207736Smckusick (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks && 663207736Smckusick dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)) 6641553Srgrimes break; 66529529Scharnier warnx("%s: cannot change current allocation", fsp); 6661553Srgrimes break; 6671553Srgrimes } 6681553Srgrimes } 6691553Srgrimes fclose(fd); 6701553Srgrimes /* 6711553Srgrimes * Disable quotas for any filesystems that have not been found. 6721553Srgrimes */ 6731553Srgrimes for (qup = quplist; qup; qup = qup->next) { 6741553Srgrimes if (qup->flags & FOUND) { 6751553Srgrimes qup->flags &= ~FOUND; 6761553Srgrimes continue; 6771553Srgrimes } 6781553Srgrimes qup->dqblk.dqb_bsoftlimit = 0; 6791553Srgrimes qup->dqblk.dqb_bhardlimit = 0; 6801553Srgrimes qup->dqblk.dqb_isoftlimit = 0; 6811553Srgrimes qup->dqblk.dqb_ihardlimit = 0; 6821553Srgrimes } 6831553Srgrimes return (1); 6841553Srgrimes} 6851553Srgrimes 6861553Srgrimes/* 6871553Srgrimes * Convert a quotause list to an ASCII file of grace times. 6881553Srgrimes */ 68929529Scharnierint 690180187Sdeswritetimes(struct quotause *quplist, int outfd, int quotatype) 6911553Srgrimes{ 692180187Sdes struct quotause *qup; 6931553Srgrimes FILE *fd; 6941553Srgrimes 6951553Srgrimes ftruncate(outfd, 0); 6961553Srgrimes lseek(outfd, 0, L_SET); 69729529Scharnier if ((fd = fdopen(dup(outfd), "w")) == NULL) 69829529Scharnier err(1, "%s", tmpfil); 6991553Srgrimes fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n"); 7001553Srgrimes fprintf(fd, "Grace period before enforcing soft limits for %ss:\n", 7011553Srgrimes qfextension[quotatype]); 7021553Srgrimes for (qup = quplist; qup; qup = qup->next) { 7031553Srgrimes fprintf(fd, "%s: block grace period: %s, ", 7041553Srgrimes qup->fsname, cvtstoa(qup->dqblk.dqb_btime)); 7051553Srgrimes fprintf(fd, "file grace period: %s\n", 7061553Srgrimes cvtstoa(qup->dqblk.dqb_itime)); 7071553Srgrimes } 7081553Srgrimes fclose(fd); 7091553Srgrimes return (1); 7101553Srgrimes} 7111553Srgrimes 7121553Srgrimes/* 7131553Srgrimes * Merge changes of grace times in an ASCII file into a quotause list. 7141553Srgrimes */ 71529529Scharnierint 716180187Sdesreadtimes(struct quotause *quplist, char *inname) 7171553Srgrimes{ 718180187Sdes struct quotause *qup; 7191553Srgrimes FILE *fd; 7201553Srgrimes int cnt; 721180187Sdes char *cp; 722207736Smckusick uintmax_t itime, btime, iseconds, bseconds; 7231553Srgrimes char *fsp, bunits[10], iunits[10], line1[BUFSIZ]; 7241553Srgrimes 7254782Sache fd = fopen(inname, "r"); 7261553Srgrimes if (fd == NULL) { 72729529Scharnier warnx("can't re-read temp file!!"); 7281553Srgrimes return (0); 7291553Srgrimes } 7301553Srgrimes /* 7311553Srgrimes * Discard two title lines, then read lines to process. 7321553Srgrimes */ 7331553Srgrimes (void) fgets(line1, sizeof (line1), fd); 7341553Srgrimes (void) fgets(line1, sizeof (line1), fd); 7351553Srgrimes while (fgets(line1, sizeof (line1), fd) != NULL) { 7361553Srgrimes if ((fsp = strtok(line1, " \t:")) == NULL) { 73729529Scharnier warnx("%s: bad format", line1); 7381553Srgrimes return (0); 7391553Srgrimes } 7401553Srgrimes if ((cp = strtok((char *)0, "\n")) == NULL) { 74129529Scharnier warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]); 7421553Srgrimes return (0); 7431553Srgrimes } 7441553Srgrimes cnt = sscanf(cp, 745207736Smckusick " block grace period: %ju %s file grace period: %ju %s", 746207736Smckusick &btime, bunits, &itime, iunits); 7471553Srgrimes if (cnt != 4) { 74829529Scharnier warnx("%s:%s: bad format", fsp, cp); 7491553Srgrimes return (0); 7501553Srgrimes } 7511553Srgrimes if (cvtatos(btime, bunits, &bseconds) == 0) 7521553Srgrimes return (0); 7531553Srgrimes if (cvtatos(itime, iunits, &iseconds) == 0) 7541553Srgrimes return (0); 7551553Srgrimes for (qup = quplist; qup; qup = qup->next) { 7561553Srgrimes if (strcmp(fsp, qup->fsname)) 7571553Srgrimes continue; 7581553Srgrimes qup->dqblk.dqb_btime = bseconds; 7591553Srgrimes qup->dqblk.dqb_itime = iseconds; 7601553Srgrimes qup->flags |= FOUND; 7611553Srgrimes break; 7621553Srgrimes } 7631553Srgrimes } 7641553Srgrimes fclose(fd); 7651553Srgrimes /* 7661553Srgrimes * reset default grace periods for any filesystems 7671553Srgrimes * that have not been found. 7681553Srgrimes */ 7691553Srgrimes for (qup = quplist; qup; qup = qup->next) { 7701553Srgrimes if (qup->flags & FOUND) { 7711553Srgrimes qup->flags &= ~FOUND; 7721553Srgrimes continue; 7731553Srgrimes } 7741553Srgrimes qup->dqblk.dqb_btime = 0; 7751553Srgrimes qup->dqblk.dqb_itime = 0; 7761553Srgrimes } 7771553Srgrimes return (1); 7781553Srgrimes} 7791553Srgrimes 7801553Srgrimes/* 7811553Srgrimes * Convert seconds to ASCII times. 7821553Srgrimes */ 7831553Srgrimeschar * 784207736Smckusickcvtstoa(uint64_t secs) 7851553Srgrimes{ 7861553Srgrimes static char buf[20]; 7871553Srgrimes 78887596Smikeh if (secs % (24 * 60 * 60) == 0) { 78987596Smikeh secs /= 24 * 60 * 60; 790207736Smckusick sprintf(buf, "%ju day%s", (uintmax_t)secs, 791207736Smckusick secs == 1 ? "" : "s"); 79287596Smikeh } else if (secs % (60 * 60) == 0) { 79387596Smikeh secs /= 60 * 60; 794207736Smckusick sprintf(buf, "%ju hour%s", (uintmax_t)secs, 795207736Smckusick secs == 1 ? "" : "s"); 79687596Smikeh } else if (secs % 60 == 0) { 79787596Smikeh secs /= 60; 798207736Smckusick sprintf(buf, "%ju minute%s", (uintmax_t)secs, 799207736Smckusick secs == 1 ? "" : "s"); 8001553Srgrimes } else 801207736Smckusick sprintf(buf, "%ju second%s", (uintmax_t)secs, 802207736Smckusick secs == 1 ? "" : "s"); 8031553Srgrimes return (buf); 8041553Srgrimes} 8051553Srgrimes 8061553Srgrimes/* 8071553Srgrimes * Convert ASCII input times to seconds. 8081553Srgrimes */ 80929529Scharnierint 810207736Smckusickcvtatos(uint64_t period, char *units, uint64_t *seconds) 8111553Srgrimes{ 8121553Srgrimes 8131553Srgrimes if (bcmp(units, "second", 6) == 0) 81487596Smikeh *seconds = period; 8151553Srgrimes else if (bcmp(units, "minute", 6) == 0) 81687596Smikeh *seconds = period * 60; 8171553Srgrimes else if (bcmp(units, "hour", 4) == 0) 81887596Smikeh *seconds = period * 60 * 60; 8191553Srgrimes else if (bcmp(units, "day", 3) == 0) 82087596Smikeh *seconds = period * 24 * 60 * 60; 8211553Srgrimes else { 822207736Smckusick warnx("%s: bad units, specify %s\n", units, 8231553Srgrimes "days, hours, minutes, or seconds"); 8241553Srgrimes return (0); 8251553Srgrimes } 8261553Srgrimes return (1); 8271553Srgrimes} 8281553Srgrimes 8291553Srgrimes/* 830207736Smckusick * Convert a limit to number of disk blocks. 831207736Smckusick */ 832207736Smckusickuint64_t 833207736Smckusickcvtblkval(uint64_t limit, char units, const char *itemname) 834207736Smckusick{ 835207736Smckusick 836207736Smckusick switch(units) { 837207736Smckusick case 'B': 838207736Smckusick case 'b': 839207736Smckusick limit = btodb(limit); 840207736Smckusick break; 841207736Smckusick case '\0': /* historic behavior */ 842207736Smckusick case ',': /* historic behavior */ 843207736Smckusick case ')': /* historic behavior */ 844207736Smckusick case 'K': 845207736Smckusick case 'k': 846207736Smckusick limit *= btodb(1024); 847207736Smckusick break; 848207736Smckusick case 'M': 849207736Smckusick case 'm': 850207736Smckusick limit *= btodb(1048576); 851207736Smckusick break; 852207736Smckusick case 'G': 853207736Smckusick case 'g': 854207736Smckusick limit *= btodb(1073741824); 855207736Smckusick break; 856207736Smckusick case 'T': 857207736Smckusick case 't': 858207736Smckusick limit *= btodb(1099511627776); 859207736Smckusick break; 860207736Smckusick case 'P': 861207736Smckusick case 'p': 862207736Smckusick limit *= btodb(1125899906842624); 863207736Smckusick break; 864207736Smckusick case 'E': 865207736Smckusick case 'e': 866207736Smckusick limit *= btodb(1152921504606846976); 867207736Smckusick break; 868207736Smckusick case ' ': 869207736Smckusick errx(2, "No space permitted between value and units for %s\n", 870207736Smckusick itemname); 871207736Smckusick break; 872207736Smckusick default: 873207736Smckusick errx(2, "%ju%c: unknown units for %s, specify " 874207736Smckusick "none, K, M, G, T, P, or E\n", 875207736Smckusick (uintmax_t)limit, units, itemname); 876207736Smckusick break; 877207736Smckusick } 878207736Smckusick return (limit); 879207736Smckusick} 880207736Smckusick 881207736Smckusick/* 882207736Smckusick * Convert a limit to number of inodes. 883207736Smckusick */ 884207736Smckusickuint64_t 885207736Smckusickcvtinoval(uint64_t limit, char units, const char *itemname) 886207736Smckusick{ 887207736Smckusick 888207736Smckusick switch(units) { 889207736Smckusick case 'B': 890207736Smckusick case 'b': 891207736Smckusick case '\0': /* historic behavior */ 892207736Smckusick case ',': /* historic behavior */ 893207736Smckusick case ')': /* historic behavior */ 894207736Smckusick break; 895207736Smckusick case 'K': 896207736Smckusick case 'k': 897207736Smckusick limit *= 1000; 898207736Smckusick break; 899207736Smckusick case 'M': 900207736Smckusick case 'm': 901207736Smckusick limit *= 1000000; 902207736Smckusick break; 903207736Smckusick case 'G': 904207736Smckusick case 'g': 905207736Smckusick limit *= 1000000000; 906207736Smckusick break; 907207736Smckusick case 'T': 908207736Smckusick case 't': 909207736Smckusick limit *= 1000000000000; 910207736Smckusick break; 911207736Smckusick case 'P': 912207736Smckusick case 'p': 913207736Smckusick limit *= 1000000000000000; 914207736Smckusick break; 915207736Smckusick case 'E': 916207736Smckusick case 'e': 917207736Smckusick limit *= 1000000000000000000; 918207736Smckusick break; 919207736Smckusick case ' ': 920207736Smckusick errx(2, "No space permitted between value and units for %s\n", 921207736Smckusick itemname); 922207736Smckusick break; 923207736Smckusick default: 924207736Smckusick errx(2, "%ju%c: unknown units for %s, specify " 925207736Smckusick "none, K, M, G, T, P, or E\n", 926207736Smckusick (uintmax_t)limit, units, itemname); 927207736Smckusick break; 928207736Smckusick } 929207736Smckusick return (limit); 930207736Smckusick} 931207736Smckusick 932207736Smckusick/* 9331553Srgrimes * Free a list of quotause structures. 9341553Srgrimes */ 93529529Scharniervoid 936180187Sdesfreeprivs(struct quotause *quplist) 9371553Srgrimes{ 938180187Sdes struct quotause *qup, *nextqup; 9391553Srgrimes 9401553Srgrimes for (qup = quplist; qup; qup = nextqup) { 941207736Smckusick quota_close(qup->qf); 9421553Srgrimes nextqup = qup->next; 9431553Srgrimes free(qup); 9441553Srgrimes } 9451553Srgrimes} 9461553Srgrimes 9471553Srgrimes/* 9481553Srgrimes * Check whether a string is completely composed of digits. 9491553Srgrimes */ 95029529Scharnierint 951180187Sdesalldigits(const char *s) 9521553Srgrimes{ 953180187Sdes int c; 9541553Srgrimes 9551553Srgrimes c = *s++; 9561553Srgrimes do { 9571553Srgrimes if (!isdigit(c)) 9581553Srgrimes return (0); 95929529Scharnier } while ((c = *s++)); 9601553Srgrimes return (1); 9611553Srgrimes} 962