edquota.c revision 207736
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: head/usr.sbin/edquota/edquota.c 207736 2010-05-07 00:41:12Z mckusick $"); 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); 4561553Srgrimes setgid(getgid()); 4571553Srgrimes setuid(getuid()); 4581553Srgrimes if ((ed = getenv("EDITOR")) == (char *)0) 4591553Srgrimes ed = _PATH_VI; 46087596Smikeh execlp(ed, ed, tmpf, (char *)0); 46129529Scharnier err(1, "%s", ed); 4621553Srgrimes } 46387596Smikeh waitpid(pid, &status, 0); 4641553Srgrimes sigsetmask(omask); 46587596Smikeh if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 4661553Srgrimes return (0); 4671553Srgrimes return (1); 4681553Srgrimes} 4691553Srgrimes 4701553Srgrimes/* 4711553Srgrimes * Convert a quotause list to an ASCII file. 4721553Srgrimes */ 47329529Scharnierint 474180187Sdeswriteprivs(struct quotause *quplist, int outfd, char *name, int quotatype) 4751553Srgrimes{ 476180187Sdes struct quotause *qup; 4771553Srgrimes FILE *fd; 4781553Srgrimes 4791553Srgrimes ftruncate(outfd, 0); 4801553Srgrimes lseek(outfd, 0, L_SET); 48129529Scharnier if ((fd = fdopen(dup(outfd), "w")) == NULL) 48229529Scharnier err(1, "%s", tmpfil); 4831553Srgrimes fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name); 4841553Srgrimes for (qup = quplist; qup; qup = qup->next) { 485207736Smckusick fprintf(fd, "%s: in use: %s, ", qup->fsname, 486207736Smckusick fmthumanvalblks(qup->dqblk.dqb_curblocks)); 487207736Smckusick fprintf(fd, "limits (soft = %s, ", 488207736Smckusick fmthumanvalblks(qup->dqblk.dqb_bsoftlimit)); 489207736Smckusick fprintf(fd, "hard = %s)\n", 490207736Smckusick fmthumanvalblks(qup->dqblk.dqb_bhardlimit)); 491207736Smckusick fprintf(fd, "\tinodes in use: %s, ", 492207736Smckusick fmthumanvalinos(qup->dqblk.dqb_curinodes)); 493207736Smckusick fprintf(fd, "limits (soft = %s, ", 494207736Smckusick fmthumanvalinos(qup->dqblk.dqb_isoftlimit)); 495207736Smckusick fprintf(fd, "hard = %s)\n", 496207736Smckusick fmthumanvalinos(qup->dqblk.dqb_ihardlimit)); 4971553Srgrimes } 4981553Srgrimes fclose(fd); 4991553Srgrimes return (1); 5001553Srgrimes} 5011553Srgrimes 502207736Smckusickchar * 503207736Smckusickfmthumanvalblks(int64_t blocks) 504207736Smckusick{ 505207736Smckusick static char numbuf[20]; 506207736Smckusick 507207736Smckusick if (hflag) { 508207736Smckusick humanize_number(numbuf, blocks < 0 ? 7 : 6, 509207736Smckusick dbtob(blocks), "", HN_AUTOSCALE, HN_NOSPACE); 510207736Smckusick return (numbuf); 511207736Smckusick } 512207736Smckusick snprintf(numbuf, sizeof(numbuf), "%juk", (uintmax_t)dbtokb(blocks)); 513207736Smckusick return(numbuf); 514207736Smckusick} 515207736Smckusick 516207736Smckusickchar * 517207736Smckusickfmthumanvalinos(int64_t inos) 518207736Smckusick{ 519207736Smckusick static char numbuf[20]; 520207736Smckusick 521207736Smckusick if (hflag) { 522207736Smckusick humanize_number(numbuf, inos < 0 ? 7 : 6, 523207736Smckusick inos, "", HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000); 524207736Smckusick return (numbuf); 525207736Smckusick } 526207736Smckusick snprintf(numbuf, sizeof(numbuf), "%ju", (uintmax_t)inos); 527207736Smckusick return(numbuf); 528207736Smckusick} 529207736Smckusick 5301553Srgrimes/* 5311553Srgrimes * Merge changes to an ASCII file into a quotause list. 5321553Srgrimes */ 53329529Scharnierint 534180187Sdesreadprivs(struct quotause *quplist, char *inname) 5351553Srgrimes{ 536180187Sdes struct quotause *qup; 5371553Srgrimes FILE *fd; 538207736Smckusick uintmax_t hardlimit, softlimit, curitems; 539207736Smckusick char hardunits, softunits, curitemunits; 5401553Srgrimes int cnt; 541180187Sdes char *cp; 5421553Srgrimes struct dqblk dqblk; 5431553Srgrimes char *fsp, line1[BUFSIZ], line2[BUFSIZ]; 5441553Srgrimes 5454782Sache fd = fopen(inname, "r"); 5461553Srgrimes if (fd == NULL) { 54729529Scharnier warnx("can't re-read temp file!!"); 5481553Srgrimes return (0); 5491553Srgrimes } 5501553Srgrimes /* 5511553Srgrimes * Discard title line, then read pairs of lines to process. 5521553Srgrimes */ 5531553Srgrimes (void) fgets(line1, sizeof (line1), fd); 5541553Srgrimes while (fgets(line1, sizeof (line1), fd) != NULL && 5551553Srgrimes fgets(line2, sizeof (line2), fd) != NULL) { 5561553Srgrimes if ((fsp = strtok(line1, " \t:")) == NULL) { 55729529Scharnier warnx("%s: bad format", line1); 5581553Srgrimes return (0); 5591553Srgrimes } 5601553Srgrimes if ((cp = strtok((char *)0, "\n")) == NULL) { 56129529Scharnier warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]); 5621553Srgrimes return (0); 5631553Srgrimes } 5641553Srgrimes cnt = sscanf(cp, 565207736Smckusick " in use: %ju%c, limits (soft = %ju%c, hard = %ju%c)", 566207736Smckusick &curitems, &curitemunits, &softlimit, &softunits, 567207736Smckusick &hardlimit, &hardunits); 568207736Smckusick /* 569207736Smckusick * The next three check for old-style input formats. 570207736Smckusick */ 571207736Smckusick if (cnt != 6) 572207736Smckusick cnt = sscanf(cp, 573207736Smckusick " in use: %ju%c, limits (soft = %ju%c hard = %ju%c", 574207736Smckusick &curitems, &curitemunits, &softlimit, 575207736Smckusick &softunits, &hardlimit, &hardunits); 576207736Smckusick if (cnt != 6) 577207736Smckusick cnt = sscanf(cp, 578207736Smckusick " in use: %ju%c, limits (soft = %ju%c hard = %ju%c)", 579207736Smckusick &curitems, &curitemunits, &softlimit, 580207736Smckusick &softunits, &hardlimit, &hardunits); 581207736Smckusick if (cnt != 6) 582207736Smckusick cnt = sscanf(cp, 583207736Smckusick " in use: %ju%c, limits (soft = %ju%c, hard = %ju%c", 584207736Smckusick &curitems, &curitemunits, &softlimit, 585207736Smckusick &softunits, &hardlimit, &hardunits); 586207736Smckusick if (cnt != 6) { 58729529Scharnier warnx("%s:%s: bad format", fsp, cp); 5881553Srgrimes return (0); 5891553Srgrimes } 590207736Smckusick dqblk.dqb_curblocks = cvtblkval(curitems, curitemunits, 591207736Smckusick "current block count"); 592207736Smckusick dqblk.dqb_bsoftlimit = cvtblkval(softlimit, softunits, 593207736Smckusick "block soft limit"); 594207736Smckusick dqblk.dqb_bhardlimit = cvtblkval(hardlimit, hardunits, 595207736Smckusick "block hard limit"); 5961553Srgrimes if ((cp = strtok(line2, "\n")) == NULL) { 59729529Scharnier warnx("%s: %s: bad format", fsp, line2); 5981553Srgrimes return (0); 5991553Srgrimes } 600207736Smckusick cnt = sscanf(&cp[7], 601207736Smckusick " in use: %ju%c limits (soft = %ju%c, hard = %ju%c)", 602207736Smckusick &curitems, &curitemunits, &softlimit, 603207736Smckusick &softunits, &hardlimit, &hardunits); 604207736Smckusick /* 605207736Smckusick * The next three check for old-style input formats. 606207736Smckusick */ 607207736Smckusick if (cnt != 6) 608207736Smckusick cnt = sscanf(&cp[7], 609207736Smckusick " in use: %ju%c limits (soft = %ju%c hard = %ju%c", 610207736Smckusick &curitems, &curitemunits, &softlimit, 611207736Smckusick &softunits, &hardlimit, &hardunits); 612207736Smckusick if (cnt != 6) 613207736Smckusick cnt = sscanf(&cp[7], 614207736Smckusick " in use: %ju%c limits (soft = %ju%c hard = %ju%c)", 615207736Smckusick &curitems, &curitemunits, &softlimit, 616207736Smckusick &softunits, &hardlimit, &hardunits); 617207736Smckusick if (cnt != 6) 618207736Smckusick cnt = sscanf(&cp[7], 619207736Smckusick " in use: %ju%c limits (soft = %ju%c, hard = %ju%c", 620207736Smckusick &curitems, &curitemunits, &softlimit, 621207736Smckusick &softunits, &hardlimit, &hardunits); 622207736Smckusick if (cnt != 6) { 623207736Smckusick warnx("%s: %s: bad format cnt %d", fsp, &cp[7], cnt); 6241553Srgrimes return (0); 6251553Srgrimes } 626207736Smckusick dqblk.dqb_curinodes = cvtinoval(curitems, curitemunits, 627207736Smckusick "current inode count"); 628207736Smckusick dqblk.dqb_isoftlimit = cvtinoval(softlimit, softunits, 629207736Smckusick "inode soft limit"); 630207736Smckusick dqblk.dqb_ihardlimit = cvtinoval(hardlimit, hardunits, 631207736Smckusick "inode hard limit"); 6321553Srgrimes for (qup = quplist; qup; qup = qup->next) { 6331553Srgrimes if (strcmp(fsp, qup->fsname)) 6341553Srgrimes continue; 6351553Srgrimes /* 6361553Srgrimes * Cause time limit to be reset when the quota 6371553Srgrimes * is next used if previously had no soft limit 6381553Srgrimes * or were under it, but now have a soft limit 6391553Srgrimes * and are over it. 6401553Srgrimes */ 6411553Srgrimes if (dqblk.dqb_bsoftlimit && 6421553Srgrimes qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit && 6431553Srgrimes (qup->dqblk.dqb_bsoftlimit == 0 || 6441553Srgrimes qup->dqblk.dqb_curblocks < 6451553Srgrimes qup->dqblk.dqb_bsoftlimit)) 6461553Srgrimes qup->dqblk.dqb_btime = 0; 6471553Srgrimes if (dqblk.dqb_isoftlimit && 6481553Srgrimes qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit && 6491553Srgrimes (qup->dqblk.dqb_isoftlimit == 0 || 6501553Srgrimes qup->dqblk.dqb_curinodes < 6511553Srgrimes qup->dqblk.dqb_isoftlimit)) 6521553Srgrimes qup->dqblk.dqb_itime = 0; 6531553Srgrimes qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit; 6541553Srgrimes qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit; 6551553Srgrimes qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit; 6561553Srgrimes qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit; 6571553Srgrimes qup->flags |= FOUND; 658207736Smckusick /* Humanized input returns only approximate counts */ 659207736Smckusick if (hflag || 660207736Smckusick (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks && 661207736Smckusick dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)) 6621553Srgrimes break; 66329529Scharnier warnx("%s: cannot change current allocation", fsp); 6641553Srgrimes break; 6651553Srgrimes } 6661553Srgrimes } 6671553Srgrimes fclose(fd); 6681553Srgrimes /* 6691553Srgrimes * Disable quotas for any filesystems that have not been found. 6701553Srgrimes */ 6711553Srgrimes for (qup = quplist; qup; qup = qup->next) { 6721553Srgrimes if (qup->flags & FOUND) { 6731553Srgrimes qup->flags &= ~FOUND; 6741553Srgrimes continue; 6751553Srgrimes } 6761553Srgrimes qup->dqblk.dqb_bsoftlimit = 0; 6771553Srgrimes qup->dqblk.dqb_bhardlimit = 0; 6781553Srgrimes qup->dqblk.dqb_isoftlimit = 0; 6791553Srgrimes qup->dqblk.dqb_ihardlimit = 0; 6801553Srgrimes } 6811553Srgrimes return (1); 6821553Srgrimes} 6831553Srgrimes 6841553Srgrimes/* 6851553Srgrimes * Convert a quotause list to an ASCII file of grace times. 6861553Srgrimes */ 68729529Scharnierint 688180187Sdeswritetimes(struct quotause *quplist, int outfd, int quotatype) 6891553Srgrimes{ 690180187Sdes struct quotause *qup; 6911553Srgrimes FILE *fd; 6921553Srgrimes 6931553Srgrimes ftruncate(outfd, 0); 6941553Srgrimes lseek(outfd, 0, L_SET); 69529529Scharnier if ((fd = fdopen(dup(outfd), "w")) == NULL) 69629529Scharnier err(1, "%s", tmpfil); 6971553Srgrimes fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n"); 6981553Srgrimes fprintf(fd, "Grace period before enforcing soft limits for %ss:\n", 6991553Srgrimes qfextension[quotatype]); 7001553Srgrimes for (qup = quplist; qup; qup = qup->next) { 7011553Srgrimes fprintf(fd, "%s: block grace period: %s, ", 7021553Srgrimes qup->fsname, cvtstoa(qup->dqblk.dqb_btime)); 7031553Srgrimes fprintf(fd, "file grace period: %s\n", 7041553Srgrimes cvtstoa(qup->dqblk.dqb_itime)); 7051553Srgrimes } 7061553Srgrimes fclose(fd); 7071553Srgrimes return (1); 7081553Srgrimes} 7091553Srgrimes 7101553Srgrimes/* 7111553Srgrimes * Merge changes of grace times in an ASCII file into a quotause list. 7121553Srgrimes */ 71329529Scharnierint 714180187Sdesreadtimes(struct quotause *quplist, char *inname) 7151553Srgrimes{ 716180187Sdes struct quotause *qup; 7171553Srgrimes FILE *fd; 7181553Srgrimes int cnt; 719180187Sdes char *cp; 720207736Smckusick uintmax_t itime, btime, iseconds, bseconds; 7211553Srgrimes char *fsp, bunits[10], iunits[10], line1[BUFSIZ]; 7221553Srgrimes 7234782Sache fd = fopen(inname, "r"); 7241553Srgrimes if (fd == NULL) { 72529529Scharnier warnx("can't re-read temp file!!"); 7261553Srgrimes return (0); 7271553Srgrimes } 7281553Srgrimes /* 7291553Srgrimes * Discard two title lines, then read lines to process. 7301553Srgrimes */ 7311553Srgrimes (void) fgets(line1, sizeof (line1), fd); 7321553Srgrimes (void) fgets(line1, sizeof (line1), fd); 7331553Srgrimes while (fgets(line1, sizeof (line1), fd) != NULL) { 7341553Srgrimes if ((fsp = strtok(line1, " \t:")) == NULL) { 73529529Scharnier warnx("%s: bad format", line1); 7361553Srgrimes return (0); 7371553Srgrimes } 7381553Srgrimes if ((cp = strtok((char *)0, "\n")) == NULL) { 73929529Scharnier warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]); 7401553Srgrimes return (0); 7411553Srgrimes } 7421553Srgrimes cnt = sscanf(cp, 743207736Smckusick " block grace period: %ju %s file grace period: %ju %s", 744207736Smckusick &btime, bunits, &itime, iunits); 7451553Srgrimes if (cnt != 4) { 74629529Scharnier warnx("%s:%s: bad format", fsp, cp); 7471553Srgrimes return (0); 7481553Srgrimes } 7491553Srgrimes if (cvtatos(btime, bunits, &bseconds) == 0) 7501553Srgrimes return (0); 7511553Srgrimes if (cvtatos(itime, iunits, &iseconds) == 0) 7521553Srgrimes return (0); 7531553Srgrimes for (qup = quplist; qup; qup = qup->next) { 7541553Srgrimes if (strcmp(fsp, qup->fsname)) 7551553Srgrimes continue; 7561553Srgrimes qup->dqblk.dqb_btime = bseconds; 7571553Srgrimes qup->dqblk.dqb_itime = iseconds; 7581553Srgrimes qup->flags |= FOUND; 7591553Srgrimes break; 7601553Srgrimes } 7611553Srgrimes } 7621553Srgrimes fclose(fd); 7631553Srgrimes /* 7641553Srgrimes * reset default grace periods for any filesystems 7651553Srgrimes * that have not been found. 7661553Srgrimes */ 7671553Srgrimes for (qup = quplist; qup; qup = qup->next) { 7681553Srgrimes if (qup->flags & FOUND) { 7691553Srgrimes qup->flags &= ~FOUND; 7701553Srgrimes continue; 7711553Srgrimes } 7721553Srgrimes qup->dqblk.dqb_btime = 0; 7731553Srgrimes qup->dqblk.dqb_itime = 0; 7741553Srgrimes } 7751553Srgrimes return (1); 7761553Srgrimes} 7771553Srgrimes 7781553Srgrimes/* 7791553Srgrimes * Convert seconds to ASCII times. 7801553Srgrimes */ 7811553Srgrimeschar * 782207736Smckusickcvtstoa(uint64_t secs) 7831553Srgrimes{ 7841553Srgrimes static char buf[20]; 7851553Srgrimes 78687596Smikeh if (secs % (24 * 60 * 60) == 0) { 78787596Smikeh secs /= 24 * 60 * 60; 788207736Smckusick sprintf(buf, "%ju day%s", (uintmax_t)secs, 789207736Smckusick secs == 1 ? "" : "s"); 79087596Smikeh } else if (secs % (60 * 60) == 0) { 79187596Smikeh secs /= 60 * 60; 792207736Smckusick sprintf(buf, "%ju hour%s", (uintmax_t)secs, 793207736Smckusick secs == 1 ? "" : "s"); 79487596Smikeh } else if (secs % 60 == 0) { 79587596Smikeh secs /= 60; 796207736Smckusick sprintf(buf, "%ju minute%s", (uintmax_t)secs, 797207736Smckusick secs == 1 ? "" : "s"); 7981553Srgrimes } else 799207736Smckusick sprintf(buf, "%ju second%s", (uintmax_t)secs, 800207736Smckusick secs == 1 ? "" : "s"); 8011553Srgrimes return (buf); 8021553Srgrimes} 8031553Srgrimes 8041553Srgrimes/* 8051553Srgrimes * Convert ASCII input times to seconds. 8061553Srgrimes */ 80729529Scharnierint 808207736Smckusickcvtatos(uint64_t period, char *units, uint64_t *seconds) 8091553Srgrimes{ 8101553Srgrimes 8111553Srgrimes if (bcmp(units, "second", 6) == 0) 81287596Smikeh *seconds = period; 8131553Srgrimes else if (bcmp(units, "minute", 6) == 0) 81487596Smikeh *seconds = period * 60; 8151553Srgrimes else if (bcmp(units, "hour", 4) == 0) 81687596Smikeh *seconds = period * 60 * 60; 8171553Srgrimes else if (bcmp(units, "day", 3) == 0) 81887596Smikeh *seconds = period * 24 * 60 * 60; 8191553Srgrimes else { 820207736Smckusick warnx("%s: bad units, specify %s\n", units, 8211553Srgrimes "days, hours, minutes, or seconds"); 8221553Srgrimes return (0); 8231553Srgrimes } 8241553Srgrimes return (1); 8251553Srgrimes} 8261553Srgrimes 8271553Srgrimes/* 828207736Smckusick * Convert a limit to number of disk blocks. 829207736Smckusick */ 830207736Smckusickuint64_t 831207736Smckusickcvtblkval(uint64_t limit, char units, const char *itemname) 832207736Smckusick{ 833207736Smckusick 834207736Smckusick switch(units) { 835207736Smckusick case 'B': 836207736Smckusick case 'b': 837207736Smckusick limit = btodb(limit); 838207736Smckusick break; 839207736Smckusick case '\0': /* historic behavior */ 840207736Smckusick case ',': /* historic behavior */ 841207736Smckusick case ')': /* historic behavior */ 842207736Smckusick case 'K': 843207736Smckusick case 'k': 844207736Smckusick limit *= btodb(1024); 845207736Smckusick break; 846207736Smckusick case 'M': 847207736Smckusick case 'm': 848207736Smckusick limit *= btodb(1048576); 849207736Smckusick break; 850207736Smckusick case 'G': 851207736Smckusick case 'g': 852207736Smckusick limit *= btodb(1073741824); 853207736Smckusick break; 854207736Smckusick case 'T': 855207736Smckusick case 't': 856207736Smckusick limit *= btodb(1099511627776); 857207736Smckusick break; 858207736Smckusick case 'P': 859207736Smckusick case 'p': 860207736Smckusick limit *= btodb(1125899906842624); 861207736Smckusick break; 862207736Smckusick case 'E': 863207736Smckusick case 'e': 864207736Smckusick limit *= btodb(1152921504606846976); 865207736Smckusick break; 866207736Smckusick case ' ': 867207736Smckusick errx(2, "No space permitted between value and units for %s\n", 868207736Smckusick itemname); 869207736Smckusick break; 870207736Smckusick default: 871207736Smckusick errx(2, "%ju%c: unknown units for %s, specify " 872207736Smckusick "none, K, M, G, T, P, or E\n", 873207736Smckusick (uintmax_t)limit, units, itemname); 874207736Smckusick break; 875207736Smckusick } 876207736Smckusick return (limit); 877207736Smckusick} 878207736Smckusick 879207736Smckusick/* 880207736Smckusick * Convert a limit to number of inodes. 881207736Smckusick */ 882207736Smckusickuint64_t 883207736Smckusickcvtinoval(uint64_t limit, char units, const char *itemname) 884207736Smckusick{ 885207736Smckusick 886207736Smckusick switch(units) { 887207736Smckusick case 'B': 888207736Smckusick case 'b': 889207736Smckusick case '\0': /* historic behavior */ 890207736Smckusick case ',': /* historic behavior */ 891207736Smckusick case ')': /* historic behavior */ 892207736Smckusick break; 893207736Smckusick case 'K': 894207736Smckusick case 'k': 895207736Smckusick limit *= 1000; 896207736Smckusick break; 897207736Smckusick case 'M': 898207736Smckusick case 'm': 899207736Smckusick limit *= 1000000; 900207736Smckusick break; 901207736Smckusick case 'G': 902207736Smckusick case 'g': 903207736Smckusick limit *= 1000000000; 904207736Smckusick break; 905207736Smckusick case 'T': 906207736Smckusick case 't': 907207736Smckusick limit *= 1000000000000; 908207736Smckusick break; 909207736Smckusick case 'P': 910207736Smckusick case 'p': 911207736Smckusick limit *= 1000000000000000; 912207736Smckusick break; 913207736Smckusick case 'E': 914207736Smckusick case 'e': 915207736Smckusick limit *= 1000000000000000000; 916207736Smckusick break; 917207736Smckusick case ' ': 918207736Smckusick errx(2, "No space permitted between value and units for %s\n", 919207736Smckusick itemname); 920207736Smckusick break; 921207736Smckusick default: 922207736Smckusick errx(2, "%ju%c: unknown units for %s, specify " 923207736Smckusick "none, K, M, G, T, P, or E\n", 924207736Smckusick (uintmax_t)limit, units, itemname); 925207736Smckusick break; 926207736Smckusick } 927207736Smckusick return (limit); 928207736Smckusick} 929207736Smckusick 930207736Smckusick/* 9311553Srgrimes * Free a list of quotause structures. 9321553Srgrimes */ 93329529Scharniervoid 934180187Sdesfreeprivs(struct quotause *quplist) 9351553Srgrimes{ 936180187Sdes struct quotause *qup, *nextqup; 9371553Srgrimes 9381553Srgrimes for (qup = quplist; qup; qup = nextqup) { 939207736Smckusick quota_close(qup->qf); 9401553Srgrimes nextqup = qup->next; 9411553Srgrimes free(qup); 9421553Srgrimes } 9431553Srgrimes} 9441553Srgrimes 9451553Srgrimes/* 9461553Srgrimes * Check whether a string is completely composed of digits. 9471553Srgrimes */ 94829529Scharnierint 949180187Sdesalldigits(const char *s) 9501553Srgrimes{ 951180187Sdes int c; 9521553Srgrimes 9531553Srgrimes c = *s++; 9541553Srgrimes do { 9551553Srgrimes if (!isdigit(c)) 9561553Srgrimes return (0); 95729529Scharnier } while ((c = *s++)); 9581553Srgrimes return (1); 9591553Srgrimes} 960