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/file.h> 53166485Smpp#include <sys/mount.h> 541553Srgrimes#include <sys/wait.h> 551553Srgrimes#include <ufs/ufs/quota.h> 56207736Smckusick 5729529Scharnier#include <ctype.h> 5829529Scharnier#include <err.h> 591553Srgrimes#include <errno.h> 601553Srgrimes#include <fstab.h> 6129529Scharnier#include <grp.h> 62207736Smckusick#include <inttypes.h> 63207736Smckusick#include <libutil.h> 641553Srgrimes#include <pwd.h> 6529529Scharnier#include <signal.h> 661553Srgrimes#include <stdio.h> 6729529Scharnier#include <stdlib.h> 681553Srgrimes#include <string.h> 691553Srgrimes#include <unistd.h> 70207736Smckusick 711553Srgrimes#include "pathnames.h" 721553Srgrimes 73207736Smckusick/* Let's be paranoid about block size */ 74207736Smckusick#if 10 > DEV_BSHIFT 75207736Smckusick#define dbtokb(db) \ 76207736Smckusick ((off_t)(db) >> (10-DEV_BSHIFT)) 77207736Smckusick#elif 10 < DEV_BSHIFT 78207736Smckusick#define dbtokb(db) \ 79207736Smckusick ((off_t)(db) << (DEV_BSHIFT-10)) 80207736Smckusick#else 81207736Smckusick#define dbtokb(db) (db) 82207736Smckusick#endif 83207736Smckusick 8487596Smikehconst char *qfextension[] = INITQFNAMES; 851553Srgrimeschar tmpfil[] = _PATH_TMP; 86207736Smckusickint hflag; 871553Srgrimes 881553Srgrimesstruct quotause { 891553Srgrimes struct quotause *next; 90207736Smckusick struct quotafile *qf; 911553Srgrimes struct dqblk dqblk; 92207736Smckusick int flags; 931553Srgrimes char fsname[MAXPATHLEN + 1]; 9429529Scharnier}; 951553Srgrimes#define FOUND 0x01 961553Srgrimes 9799800Salfredint alldigits(const char *s); 98207736Smckusickint cvtatos(uint64_t, char *, uint64_t *); 99207736Smckusickchar *cvtstoa(uint64_t); 100207736Smckusickuint64_t cvtblkval(uint64_t, char, const char *); 101207736Smckusickuint64_t cvtinoval(uint64_t, char, const char *); 10299800Salfredint editit(char *); 103207736Smckusickchar *fmthumanvalblks(int64_t); 104207736Smckusickchar *fmthumanvalinos(int64_t); 10599800Salfredvoid freeprivs(struct quotause *); 10699800Salfredint getentry(const char *, int); 10799800Salfredstruct quotause *getprivs(long, int, char *); 108207736Smckusickvoid putprivs(long, struct quotause *); 10999800Salfredint readprivs(struct quotause *, char *); 11099800Salfredint readtimes(struct quotause *, char *); 11199800Salfredstatic void usage(void); 11299800Salfredint writetimes(struct quotause *, int, int); 11399800Salfredint writeprivs(struct quotause *, int, char *, int); 11429529Scharnier 11529529Scharnierint 116180187Sdesmain(int argc, char *argv[]) 1171553Srgrimes{ 118103071Ssobomax struct quotause *qup, *protoprivs, *curprivs; 119103071Ssobomax long id, protoid; 120103071Ssobomax int i, quotatype, range, tmpfd; 121103071Ssobomax uid_t startuid, enduid; 122207736Smckusick uint64_t lim; 123207736Smckusick char *protoname, *cp, *endpt, *oldoptarg; 124124830Sgrehan int eflag = 0, tflag = 0, pflag = 0, ch; 12584081Syar char *fspath = NULL; 126126201Sceri char buf[MAXLOGNAME]; 1271553Srgrimes 1281553Srgrimes if (argc < 2) 1291553Srgrimes usage(); 13029529Scharnier if (getuid()) 13129529Scharnier errx(1, "permission denied"); 1321553Srgrimes quotatype = USRQUOTA; 133103071Ssobomax protoprivs = NULL; 134162295Scharnier curprivs = NULL; 135162295Scharnier protoname = NULL; 136207736Smckusick while ((ch = getopt(argc, argv, "ughtf:p:e:")) != -1) { 1371553Srgrimes switch(ch) { 13884081Syar case 'f': 13984081Syar fspath = optarg; 14084081Syar break; 1411553Srgrimes case 'p': 142207736Smckusick if (eflag) { 143207736Smckusick warnx("cannot specify both -e and -p"); 144207736Smckusick usage(); 145207736Smckusick /* not reached */ 146207736Smckusick } 1471553Srgrimes protoname = optarg; 1481553Srgrimes pflag++; 1491553Srgrimes break; 1501553Srgrimes case 'g': 1511553Srgrimes quotatype = GRPQUOTA; 1521553Srgrimes break; 153207736Smckusick case 'h': 154207736Smckusick hflag++; 155207736Smckusick break; 1561553Srgrimes case 'u': 1571553Srgrimes quotatype = USRQUOTA; 1581553Srgrimes break; 1591553Srgrimes case 't': 1601553Srgrimes tflag++; 1611553Srgrimes break; 162103071Ssobomax case 'e': 163207736Smckusick if (pflag) { 164207736Smckusick warnx("cannot specify both -e and -p"); 165207736Smckusick usage(); 166207736Smckusick /* not reached */ 167207736Smckusick } 168207736Smckusick if ((qup = calloc(1, sizeof(*qup))) == NULL) 169103071Ssobomax errx(2, "out of memory"); 170103071Ssobomax oldoptarg = optarg; 171207736Smckusick for (i = 0, cp = optarg; 172207736Smckusick (cp = strsep(&optarg, ":")) != NULL; i++) { 173103071Ssobomax if (cp != oldoptarg) 174103071Ssobomax *(cp - 1) = ':'; 175207736Smckusick if (i > 0 && !isdigit(*cp)) { 176207736Smckusick warnx("incorrect quota specification: " 177207736Smckusick "%s", oldoptarg); 178207736Smckusick usage(); 179207736Smckusick /* Not Reached */ 180207736Smckusick } 181103071Ssobomax switch (i) { 182103071Ssobomax case 0: 183103071Ssobomax strlcpy(qup->fsname, cp, 184103071Ssobomax sizeof(qup->fsname)); 185103071Ssobomax break; 186103071Ssobomax case 1: 187207736Smckusick lim = strtoll(cp, &endpt, 10); 188207736Smckusick qup->dqblk.dqb_bsoftlimit = 189207736Smckusick cvtblkval(lim, *endpt, 190207736Smckusick "block soft limit"); 191207736Smckusick continue; 192103071Ssobomax case 2: 193207736Smckusick lim = strtoll(cp, &endpt, 10); 194207736Smckusick qup->dqblk.dqb_bhardlimit = 195207736Smckusick cvtblkval(lim, *endpt, 196207736Smckusick "block hard limit"); 197207736Smckusick continue; 198103071Ssobomax case 3: 199207736Smckusick lim = strtoll(cp, &endpt, 10); 200207736Smckusick qup->dqblk.dqb_isoftlimit = 201207736Smckusick cvtinoval(lim, *endpt, 202207736Smckusick "inode soft limit"); 203207736Smckusick continue; 204103071Ssobomax case 4: 205207736Smckusick lim = strtoll(cp, &endpt, 10); 206207736Smckusick qup->dqblk.dqb_ihardlimit = 207207736Smckusick cvtinoval(lim, *endpt, 208207736Smckusick "inode hard limit"); 209207736Smckusick continue; 210103071Ssobomax default: 211103071Ssobomax warnx("incorrect quota specification: " 212103071Ssobomax "%s", oldoptarg); 213103071Ssobomax usage(); 214207736Smckusick /* Not Reached */ 215103071Ssobomax } 216103071Ssobomax } 217103071Ssobomax if (protoprivs == NULL) { 218103071Ssobomax protoprivs = curprivs = qup; 219103071Ssobomax } else { 220103071Ssobomax curprivs->next = qup; 221103071Ssobomax curprivs = qup; 222103071Ssobomax } 223103071Ssobomax eflag++; 224103071Ssobomax break; 2251553Srgrimes default: 2261553Srgrimes usage(); 227207736Smckusick /* Not Reached */ 2281553Srgrimes } 2291553Srgrimes } 2301553Srgrimes argc -= optind; 2311553Srgrimes argv += optind; 232207736Smckusick if (pflag || eflag) { 233207736Smckusick if (pflag) { 234103071Ssobomax if ((protoid = getentry(protoname, quotatype)) == -1) 235103071Ssobomax exit(1); 236103071Ssobomax protoprivs = getprivs(protoid, quotatype, fspath); 237207736Smckusick if (protoprivs == NULL) 238207736Smckusick exit(0); 239103071Ssobomax for (qup = protoprivs; qup; qup = qup->next) { 240103071Ssobomax qup->dqblk.dqb_btime = 0; 241103071Ssobomax qup->dqblk.dqb_itime = 0; 242103071Ssobomax } 2431553Srgrimes } 244101546Siedowse for (; argc-- > 0; argv++) { 245101546Siedowse if (strspn(*argv, "0123456789-") == strlen(*argv) && 24614961Smpp (cp = strchr(*argv, '-')) != NULL) { 24714961Smpp *cp++ = '\0'; 24814961Smpp startuid = atoi(*argv); 24914961Smpp enduid = atoi(cp); 25029529Scharnier if (enduid < startuid) 25129529Scharnier errx(1, 25229529Scharnier "ending uid (%d) must be >= starting uid (%d) when using uid ranges", 25314961Smpp enduid, startuid); 254103071Ssobomax range = 1; 255103071Ssobomax } else { 256103071Ssobomax startuid = enduid = 0; 257103071Ssobomax range = 0; 258103071Ssobomax } 259103071Ssobomax for ( ; startuid <= enduid; startuid++) { 260103071Ssobomax if (range) 261103071Ssobomax snprintf(buf, sizeof(buf), "%d", 26214961Smpp startuid); 263103071Ssobomax else 264103071Ssobomax snprintf(buf, sizeof(buf), "%s", 265103071Ssobomax *argv); 266103071Ssobomax if ((id = getentry(buf, quotatype)) < 0) 267103071Ssobomax continue; 268207736Smckusick if (pflag) { 269207736Smckusick putprivs(id, protoprivs); 270207736Smckusick continue; 27114961Smpp } 272207736Smckusick for (qup = protoprivs; qup; qup = qup->next) { 273207736Smckusick curprivs = getprivs(id, quotatype, 274207736Smckusick qup->fsname); 275207736Smckusick if (curprivs == NULL) 276207736Smckusick continue; 277207736Smckusick curprivs->dqblk = qup->dqblk; 278207736Smckusick putprivs(id, curprivs); 279207736Smckusick freeprivs(curprivs); 280207736Smckusick } 28114961Smpp } 2821553Srgrimes } 283207736Smckusick if (pflag) 284207736Smckusick freeprivs(protoprivs); 2851553Srgrimes exit(0); 2861553Srgrimes } 2871553Srgrimes tmpfd = mkstemp(tmpfil); 2881553Srgrimes fchown(tmpfd, getuid(), getgid()); 2891553Srgrimes if (tflag) { 290207736Smckusick if ((protoprivs = getprivs(0, quotatype, fspath)) != NULL) { 291207736Smckusick if (writetimes(protoprivs, tmpfd, quotatype) != 0 && 292207736Smckusick editit(tmpfil) && readtimes(protoprivs, tmpfil)) 293207736Smckusick putprivs(0L, protoprivs); 294207736Smckusick freeprivs(protoprivs); 295207736Smckusick } 29628431Sjlemon close(tmpfd); 29728431Sjlemon unlink(tmpfil); 2981553Srgrimes exit(0); 2991553Srgrimes } 3001553Srgrimes for ( ; argc > 0; argc--, argv++) { 3011553Srgrimes if ((id = getentry(*argv, quotatype)) == -1) 3021553Srgrimes continue; 303207736Smckusick if ((curprivs = getprivs(id, quotatype, fspath)) == NULL) 304207736Smckusick exit(1); 3051553Srgrimes if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0) 3061553Srgrimes continue; 3074782Sache if (editit(tmpfil) && readprivs(curprivs, tmpfil)) 308207736Smckusick putprivs(id, curprivs); 3091553Srgrimes freeprivs(curprivs); 3101553Srgrimes } 3111553Srgrimes close(tmpfd); 3121553Srgrimes unlink(tmpfil); 3131553Srgrimes exit(0); 3141553Srgrimes} 3151553Srgrimes 31629529Scharnierstatic void 317180187Sdesusage(void) 3181553Srgrimes{ 319103071Ssobomax fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 320207736Smckusick "usage: edquota [-uh] [-f fspath] [-p username] username ...", 321103071Ssobomax " edquota [-u] -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]", 322103071Ssobomax " username ...", 323207736Smckusick " edquota -g [-h] [-f fspath] [-p groupname] groupname ...", 324103071Ssobomax " edquota -g -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]", 325103071Ssobomax " groupname ...", 32684081Syar " edquota [-u] -t [-f fspath]", 32784081Syar " edquota -g -t [-f fspath]"); 3281553Srgrimes exit(1); 3291553Srgrimes} 3301553Srgrimes 3311553Srgrimes/* 3321553Srgrimes * This routine converts a name for a particular quota type to 3331553Srgrimes * an identifier. This routine must agree with the kernel routine 3341553Srgrimes * getinoquota as to the interpretation of quota types. 3351553Srgrimes */ 33629529Scharnierint 337180187Sdesgetentry(const char *name, int quotatype) 3381553Srgrimes{ 3391553Srgrimes struct passwd *pw; 3401553Srgrimes struct group *gr; 3411553Srgrimes 3421553Srgrimes if (alldigits(name)) 3431553Srgrimes return (atoi(name)); 3441553Srgrimes switch(quotatype) { 3451553Srgrimes case USRQUOTA: 34629529Scharnier if ((pw = getpwnam(name))) 3471553Srgrimes return (pw->pw_uid); 34829529Scharnier warnx("%s: no such user", name); 349207736Smckusick sleep(3); 3501553Srgrimes break; 3511553Srgrimes case GRPQUOTA: 35229529Scharnier if ((gr = getgrnam(name))) 3531553Srgrimes return (gr->gr_gid); 35429529Scharnier warnx("%s: no such group", name); 355207736Smckusick sleep(3); 3561553Srgrimes break; 3571553Srgrimes default: 35829529Scharnier warnx("%d: unknown quota type", quotatype); 359207736Smckusick sleep(3); 3601553Srgrimes break; 3611553Srgrimes } 3621553Srgrimes sleep(1); 3631553Srgrimes return (-1); 3641553Srgrimes} 3651553Srgrimes 3661553Srgrimes/* 3671553Srgrimes * Collect the requested quota information. 3681553Srgrimes */ 3691553Srgrimesstruct quotause * 370180187Sdesgetprivs(long id, int quotatype, char *fspath) 3711553Srgrimes{ 372207736Smckusick struct quotafile *qf; 373180187Sdes struct fstab *fs; 374180187Sdes struct quotause *qup, *quptail; 3751553Srgrimes struct quotause *quphead; 3761553Srgrimes 3771553Srgrimes setfsent(); 378162295Scharnier quphead = quptail = NULL; 37929529Scharnier while ((fs = getfsent())) { 38084081Syar if (fspath && *fspath && strcmp(fspath, fs->fs_spec) && 38184081Syar strcmp(fspath, fs->fs_file)) 38284081Syar continue; 3831553Srgrimes if (strcmp(fs->fs_vfstype, "ufs")) 3841553Srgrimes continue; 385207736Smckusick if ((qf = quota_open(fs, quotatype, O_CREAT|O_RDWR)) == NULL) { 386207736Smckusick if (errno != EOPNOTSUPP) 387207736Smckusick warn("cannot open quotas on %s", fs->fs_file); 3881553Srgrimes continue; 389207736Smckusick } 390207736Smckusick if ((qup = (struct quotause *)calloc(1, sizeof(*qup))) == NULL) 39129529Scharnier errx(2, "out of memory"); 392207736Smckusick qup->qf = qf; 393207736Smckusick strncpy(qup->fsname, fs->fs_file, sizeof(qup->fsname)); 394207736Smckusick if (quota_read(qf, &qup->dqblk, id) == -1) { 395207736Smckusick warn("cannot read quotas on %s", fs->fs_file); 396207736Smckusick freeprivs(qup); 397207736Smckusick continue; 3981553Srgrimes } 3991553Srgrimes if (quphead == NULL) 4001553Srgrimes quphead = qup; 4011553Srgrimes else 4021553Srgrimes quptail->next = qup; 4031553Srgrimes quptail = qup; 4041553Srgrimes qup->next = 0; 4051553Srgrimes } 406207736Smckusick if (quphead == NULL) { 407207736Smckusick warnx("No quotas on %s", fspath ? fspath : "any filesystems"); 408207736Smckusick } 4091553Srgrimes endfsent(); 4101553Srgrimes return (quphead); 4111553Srgrimes} 4121553Srgrimes 4131553Srgrimes/* 4141553Srgrimes * Store the requested quota information. 4151553Srgrimes */ 41629529Scharniervoid 417207736Smckusickputprivs(long id, struct quotause *quplist) 4181553Srgrimes{ 419180187Sdes struct quotause *qup; 4201553Srgrimes 421207736Smckusick for (qup = quplist; qup; qup = qup->next) 422207736Smckusick if (quota_write_limits(qup->qf, &qup->dqblk, id) == -1) 423207736Smckusick warn("%s", qup->fsname); 4241553Srgrimes} 4251553Srgrimes 4261553Srgrimes/* 427228990Suqs * Take a list of privileges and get it edited. 4281553Srgrimes */ 42929529Scharnierint 430180187Sdeseditit(char *tmpf) 4311553Srgrimes{ 4321553Srgrimes long omask; 43387596Smikeh int pid, status; 4341553Srgrimes 4351553Srgrimes omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 4361553Srgrimes top: 4371553Srgrimes if ((pid = fork()) < 0) { 4381553Srgrimes 4391553Srgrimes if (errno == EPROCLIM) { 44029529Scharnier warnx("you have too many processes"); 4411553Srgrimes return(0); 4421553Srgrimes } 4431553Srgrimes if (errno == EAGAIN) { 4441553Srgrimes sleep(1); 4451553Srgrimes goto top; 4461553Srgrimes } 44729529Scharnier warn("fork"); 4481553Srgrimes return (0); 4491553Srgrimes } 4501553Srgrimes if (pid == 0) { 451180187Sdes const char *ed; 4521553Srgrimes 4531553Srgrimes sigsetmask(omask); 454241848Seadler if (setgid(getgid()) != 0) 455241848Seadler err(1, "setgid failed"); 456241848Seadler if (setuid(getuid()) != 0) 457241848Seadler err(1, "setuid failed"); 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