1 /*
2  * Copyright (c) 1980, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Robert Elz at The University of Melbourne.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 static char copyright[] =
35 "@(#) Copyright (c) 1980, 1990, 1993\n\
36 	The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38 
39 #ifndef lint
40 /*static char sccsid[] = "from: @(#)edquota.c	8.1 (Berkeley) 6/6/93";*/
41 static char *rcsid = "$Id: edquota.c,v 1.42 2005/04/01 04:31:11 deraadt Exp $";
42 #endif /* not lint */
43 
44 /*
45  * Disk quota editor.
46  */
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/file.h>
50 #include <sys/wait.h>
51 #include <ufs/ufs/quota.h>
52 #include <err.h>
53 #include <errno.h>
54 #include <fstab.h>
55 #include <pwd.h>
56 #include <grp.h>
57 #include <ctype.h>
58 #include <stdio.h>
59 #include <signal.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include "pathnames.h"
64 
65 char *qfname = QUOTAFILENAME;
66 char *qfextension[] = INITQFNAMES;
67 char *quotagroup = QUOTAGROUP;
68 char tmpfil[] = _PATH_TMPFILE;
69 
70 struct quotause {
71 	struct	quotause *next;
72 	long	flags;
73 	struct	dqblk dqblk;
74 	char	fsname[MAXPATHLEN];
75 	char	qfname[1];	/* actually longer */
76 } *getprivs(u_int, int);
77 #define	FOUND	0x01
78 
79 void	usage(void);
80 int	getentry(char *, int, u_int *);
81 struct quotause *
82 	getprivs(u_int, int);
83 void	putprivs(long, int, struct quotause *);
84 int	editit(char *);
85 int	writeprivs(struct quotause *, int, char *, int);
86 int	readprivs(struct quotause *, int);
87 int	writetimes(struct quotause *, int, int);
88 int	readtimes(struct quotause *, int);
89 char *	cvtstoa(time_t);
90 int	cvtatos(time_t, char *, time_t *);
91 void	freeprivs(struct quotause *);
92 int	alldigits(char *s);
93 int	hasquota(struct fstab *, int, char **);
94 
95 void
usage(void)96 usage(void)
97 {
98 	(void)fprintf(stderr, "%s%s%s%s",
99 		"Usage: edquota [-u] [-p username] username ...\n",
100 		"\tedquota -g [-p groupname] groupname ...\n",
101 		"\tedquota [-u] -t\n", "\tedquota -g -t\n");
102 	exit(1);
103 }
104 
105 int
main(int argc,char * argv[])106 main(int argc, char *argv[])
107 {
108 	struct quotause *qup, *protoprivs, *curprivs;
109 	u_int id, protoid;
110 	int quotatype, tmpfd;
111 	char *protoname = NULL;
112 	int ch;
113 	int tflag = 0, pflag = 0;
114 
115 	if (argc < 2)
116 		usage();
117 	if (getuid())
118 		errx(1, "%s", strerror(EPERM));
119 	quotatype = USRQUOTA;
120 	while ((ch = getopt(argc, argv, "ugtp:")) != -1) {
121 		switch(ch) {
122 		case 'p':
123 			protoname = optarg;
124 			pflag++;
125 			break;
126 		case 'g':
127 			quotatype = GRPQUOTA;
128 			break;
129 		case 'u':
130 			quotatype = USRQUOTA;
131 			break;
132 		case 't':
133 			tflag++;
134 			break;
135 		default:
136 			usage();
137 		}
138 	}
139 	argc -= optind;
140 	argv += optind;
141 	if (pflag) {
142 		if (getentry(protoname, quotatype, &protoid) == -1)
143 			exit(1);
144 		protoprivs = getprivs(protoid, quotatype);
145 		for (qup = protoprivs; qup; qup = qup->next) {
146 			qup->dqblk.dqb_btime = 0;
147 			qup->dqblk.dqb_itime = 0;
148 		}
149 		while (argc-- > 0) {
150 			if (getentry(*argv++, quotatype, &id) == -1)
151 				continue;
152 			putprivs(id, quotatype, protoprivs);
153 		}
154 		exit(0);
155 	}
156 	if ((tmpfd = mkstemp(tmpfil)) == -1)
157 		errx(1, "%s", tmpfil);
158 	if (tflag) {
159 		protoprivs = getprivs(0, quotatype);
160 		if (writetimes(protoprivs, tmpfd, quotatype) == 0) {
161 			unlink(tmpfil);
162 			exit(1);
163 		}
164 		if (editit(tmpfil) && readtimes(protoprivs, tmpfd))
165 			putprivs(0, quotatype, protoprivs);
166 		freeprivs(protoprivs);
167 		unlink(tmpfil);
168 		exit(0);
169 	}
170 	for ( ; argc > 0; argc--, argv++) {
171 		if (getentry(*argv, quotatype, &id) == -1)
172 			continue;
173 		curprivs = getprivs(id, quotatype);
174 		if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
175 			continue;
176 		if (editit(tmpfil) && readprivs(curprivs, tmpfd))
177 			putprivs(id, quotatype, curprivs);
178 		freeprivs(curprivs);
179 	}
180 	close(tmpfd);
181 	unlink(tmpfil);
182 	exit(0);
183 }
184 
185 /*
186  * This routine converts a name for a particular quota type to
187  * an identifier. This routine must agree with the kernel routine
188  * getinoquota as to the interpretation of quota types.
189  */
190 int
getentry(char * name,int quotatype,u_int * idp)191 getentry(char *name, int quotatype, u_int *idp)
192 {
193 	struct passwd *pw;
194 	struct group *gr;
195 	u_int id;
196 
197 	switch(quotatype) {
198 	case USRQUOTA:
199 		if ((pw = getpwnam(name))) {
200 			*idp = pw->pw_uid;
201 			return 0;
202 		} else if (alldigits(name)) {
203 			if ((id = strtoul(name, NULL, 10)) <= UID_MAX) {
204 				*idp = id;
205 				return 0;
206 			}
207 		}
208 		warnx("%s: no such user", name);
209 		break;
210 	case GRPQUOTA:
211 		if ((gr = getgrnam(name))) {
212 			*idp = gr->gr_gid;
213 			return 0;
214 		} else if (alldigits(name)) {
215 			if ((id = strtoul(name, NULL, 10)) <= GID_MAX) {
216 				*idp = id;
217 				return (0);
218 			}
219 		}
220 		warnx("%s: no such group", name);
221 		break;
222 	default:
223 		warnx("%d: unknown quota type", quotatype);
224 		break;
225 	}
226 	sleep(1);
227 	return(-1);
228 }
229 
230 /*
231  * Collect the requested quota information.
232  */
233 struct quotause *
getprivs(u_int id,int quotatype)234 getprivs(u_int id, int quotatype)
235 {
236 	struct fstab *fs;
237 	struct quotause *qup, *quptail;
238 	struct quotause *quphead;
239 	int qcmd, qupsize, fd;
240 	u_int mid;
241 	char *qfpathname;
242 	static int warned = 0;
243 	size_t qfpathnamelen;
244 
245 	setfsent();
246 	quphead = (struct quotause *)0;
247 	qcmd = QCMD(Q_GETQUOTA, quotatype);
248 	while ((fs = getfsent())) {
249 		if (strcmp(fs->fs_vfstype, "ffs") &&
250 		    strcmp(fs->fs_vfstype, "ufs") &&
251 		    strcmp(fs->fs_vfstype, "mfs"))
252 			continue;
253 		if (!hasquota(fs, quotatype, &qfpathname))
254 			continue;
255 		qfpathnamelen = strlen(qfpathname);
256 		qupsize = sizeof(*qup) + qfpathnamelen;
257 		if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
258 			errx(2, "out of memory");
259 		if (quotactl(fs->fs_file, qcmd, id, (char *)&qup->dqblk) != 0) {
260 	    		if (errno == EOPNOTSUPP && !warned) {
261 				warned++;
262 				(void)fprintf(stderr, "Warning: %s\n",
263 				    "Quotas are not compiled into this kernel");
264 				sleep(3);
265 			}
266 			if (getentry(quotagroup, GRPQUOTA, &mid) == -1) {
267 				warned++;
268 				(void)fprintf(stderr, "Warning: "
269 				    "group %s not known, skipping %s\n",
270 				    quotagroup, fs->fs_file);
271 			}
272 			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
273 				fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
274 				if (fd < 0 && errno != ENOENT) {
275 					perror(qfpathname);
276 					free(qup);
277 					continue;
278 				}
279 				(void)fprintf(stderr, "Creating quota file %s\n",
280 				    qfpathname);
281 				sleep(3);
282 				(void)fchown(fd, getuid(), mid);
283 				(void)fchmod(fd, 0640);
284 			}
285 			lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET);
286 			switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
287 			case 0:			/* EOF */
288 				/*
289 				 * Convert implicit 0 quota (EOF)
290 				 * into an explicit one (zero'ed dqblk)
291 				 */
292 				bzero((caddr_t)&qup->dqblk,
293 				    sizeof(struct dqblk));
294 				break;
295 
296 			case sizeof(struct dqblk):	/* OK */
297 				break;
298 
299 			default:		/* ERROR */
300 				warn("read error in %s", qfpathname);
301 				close(fd);
302 				free(qup);
303 				continue;
304 			}
305 			close(fd);
306 		}
307 		strlcpy(qup->qfname, qfpathname, qfpathnamelen + 1);
308 		strlcpy(qup->fsname, fs->fs_file, sizeof qup->fsname);
309 		if (quphead == NULL)
310 			quphead = qup;
311 		else
312 			quptail->next = qup;
313 		quptail = qup;
314 		qup->next = 0;
315 	}
316 	endfsent();
317 	return(quphead);
318 }
319 
320 /*
321  * Store the requested quota information.
322  */
323 void
putprivs(long id,int quotatype,struct quotause * quplist)324 putprivs(long id, int quotatype, struct quotause *quplist)
325 {
326 	struct quotause *qup;
327 	int qcmd, fd;
328 
329 	qcmd = QCMD(Q_SETQUOTA, quotatype);
330 	for (qup = quplist; qup; qup = qup->next) {
331 		if (quotactl(qup->fsname, qcmd, id, (char *)&qup->dqblk) == 0)
332 			continue;
333 		if ((fd = open(qup->qfname, O_WRONLY)) < 0) {
334 			perror(qup->qfname);
335 		} else {
336 			lseek(fd, (off_t)(id * sizeof (struct dqblk)), 0);
337 			if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
338 			    sizeof (struct dqblk))
339 				warn("%s", qup->qfname);
340 			close(fd);
341 		}
342 	}
343 }
344 
345 /*
346  * Take a list of privileges and get it edited.
347  */
348 int
editit(char * tmpfile)349 editit(char *tmpfile)
350 {
351 	pid_t pid, xpid;
352 	char *argp[] = {"sh", "-c", NULL, NULL};
353 	char *ed, *p;
354 	sigset_t mask, omask;
355 	int stat, len;
356 
357 	if ((ed = getenv("EDITOR")) == (char *)0)
358 		ed = _PATH_VI;
359 	len = strlen(ed) + 1 + strlen(tmpfile) + 1;
360 	p = (char *)malloc(len);
361 	if (!p)
362 		return(0);
363 	(void)snprintf(p, len, "%s %s", ed, tmpfile);
364 	argp[2] = p;
365 
366 	sigemptyset(&mask);
367 	sigaddset(&mask, SIGINT);
368 	sigaddset(&mask, SIGQUIT);
369 	sigaddset(&mask, SIGHUP);
370 	sigprocmask(SIG_SETMASK, &mask, &omask);
371  top:
372 	if ((pid = fork()) < 0) {
373 		if (errno == EPROCLIM) {
374 			warnx("you have too many processes");
375 			free(p);
376 			return(0);
377 		}
378 		if (errno == EAGAIN) {
379 			sleep(1);
380 			goto top;
381 		}
382 		perror("fork");
383 		free(p);
384 		return(0);
385 	}
386 	if (pid == 0) {
387 		sigprocmask(SIG_SETMASK, &omask, NULL);
388 		setgid(getgid());
389 		setuid(getuid());
390 		execv(_PATH_BSHELL, argp);
391 		_exit(127);
392 	}
393 	free(p);
394 	for (;;) {
395 		xpid = waitpid(pid, (int *)&stat, WUNTRACED);
396 		if (WIFSTOPPED(stat))
397 			raise(WSTOPSIG(stat));
398 		else if (WIFEXITED(stat))
399 			break;
400 	}
401 	sigprocmask(SIG_SETMASK, &omask, NULL);
402 	if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0)
403 		return(0);
404 	return(1);
405 }
406 
407 /*
408  * Convert a quotause list to an ASCII file.
409  */
410 int
writeprivs(struct quotause * quplist,int outfd,char * name,int quotatype)411 writeprivs(struct quotause *quplist, int outfd, char *name, int quotatype)
412 {
413 	struct quotause *qup;
414 	FILE *fd;
415 
416 	ftruncate(outfd, 0);
417 	lseek(outfd, 0, SEEK_SET);
418 	if ((fd = fdopen(dup(outfd), "w")) == NULL)
419 		err(1, "%s", tmpfil);
420 	(void)fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
421 	for (qup = quplist; qup; qup = qup->next) {
422 		(void)fprintf(fd, "%s: %s %d, limits (soft = %d, hard = %d)\n",
423 		    qup->fsname, "KBytes in use:",
424 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_curblocks) / 1024),
425 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bsoftlimit) / 1024),
426 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bhardlimit) / 1024));
427 		(void)fprintf(fd, "%s %d, limits (soft = %d, hard = %d)\n",
428 		    "\tinodes in use:", qup->dqblk.dqb_curinodes,
429 		    qup->dqblk.dqb_isoftlimit, qup->dqblk.dqb_ihardlimit);
430 	}
431 	fclose(fd);
432 	return(1);
433 }
434 
435 /*
436  * Merge changes to an ASCII file into a quotause list.
437  */
438 int
readprivs(struct quotause * quplist,int infd)439 readprivs(struct quotause *quplist, int infd)
440 {
441 	struct quotause *qup;
442 	FILE *fd;
443 	int cnt;
444 	char *cp;
445 	struct dqblk dqblk;
446 	char *fsp, line1[BUFSIZ], line2[BUFSIZ];
447 
448 	lseek(infd, 0, SEEK_SET);
449 	fd = fdopen(dup(infd), "r");
450 	if (fd == NULL) {
451 		warnx("can't re-read temp file!!");
452 		return(0);
453 	}
454 	/*
455 	 * Discard title line, then read pairs of lines to process.
456 	 */
457 	(void)fgets(line1, sizeof (line1), fd);
458 	while (fgets(line1, sizeof (line1), fd) != NULL &&
459 	       fgets(line2, sizeof (line2), fd) != NULL) {
460 		if ((fsp = strtok(line1, " \t:")) == NULL) {
461 			warnx("%s: bad format", line1);
462 			return(0);
463 		}
464 		if ((cp = strtok((char *)0, "\n")) == NULL) {
465 			warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
466 			return(0);
467 		}
468 		cnt = sscanf(cp,
469 		    " KBytes in use: %d, limits (soft = %d, hard = %d)",
470 		    &dqblk.dqb_curblocks, &dqblk.dqb_bsoftlimit,
471 		    &dqblk.dqb_bhardlimit);
472 		if (cnt != 3) {
473 			warnx("%s:%s: bad format", fsp, cp);
474 			return(0);
475 		}
476 		dqblk.dqb_curblocks = btodb((u_quad_t)
477 		    dqblk.dqb_curblocks * 1024);
478 		dqblk.dqb_bsoftlimit = btodb((u_quad_t)
479 		    dqblk.dqb_bsoftlimit * 1024);
480 		dqblk.dqb_bhardlimit = btodb((u_quad_t)
481 		    dqblk.dqb_bhardlimit * 1024);
482 		if ((cp = strtok(line2, "\n")) == NULL) {
483 			warnx("%s: %s: bad format", fsp, line2);
484 			return(0);
485 		}
486 		cnt = sscanf(cp,
487 		    "\tinodes in use: %d, limits (soft = %d, hard = %d)",
488 		    &dqblk.dqb_curinodes, &dqblk.dqb_isoftlimit,
489 		    &dqblk.dqb_ihardlimit);
490 		if (cnt != 3) {
491 			warnx("%s: %s: bad format", fsp, line2);
492 			return(0);
493 		}
494 		for (qup = quplist; qup; qup = qup->next) {
495 			if (strcmp(fsp, qup->fsname))
496 				continue;
497 			/*
498 			 * Cause time limit to be reset when the quota
499 			 * is next used if previously had no soft limit
500 			 * or were under it, but now have a soft limit
501 			 * and are over it.
502 			 */
503 			if (dqblk.dqb_bsoftlimit &&
504 			    qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
505 			    (qup->dqblk.dqb_bsoftlimit == 0 ||
506 			     qup->dqblk.dqb_curblocks <
507 			     qup->dqblk.dqb_bsoftlimit))
508 				qup->dqblk.dqb_btime = 0;
509 			if (dqblk.dqb_isoftlimit &&
510 			    qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
511 			    (qup->dqblk.dqb_isoftlimit == 0 ||
512 			     qup->dqblk.dqb_curinodes <
513 			     qup->dqblk.dqb_isoftlimit))
514 				qup->dqblk.dqb_itime = 0;
515 			qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
516 			qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
517 			qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
518 			qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
519 			qup->flags |= FOUND;
520 			if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
521 			    dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
522 				break;
523 			warnx("%s: cannot change current allocation", fsp);
524 			break;
525 		}
526 	}
527 	fclose(fd);
528 	/*
529 	 * Disable quotas for any filesystems that have not been found.
530 	 */
531 	for (qup = quplist; qup; qup = qup->next) {
532 		if (qup->flags & FOUND) {
533 			qup->flags &= ~FOUND;
534 			continue;
535 		}
536 		qup->dqblk.dqb_bsoftlimit = 0;
537 		qup->dqblk.dqb_bhardlimit = 0;
538 		qup->dqblk.dqb_isoftlimit = 0;
539 		qup->dqblk.dqb_ihardlimit = 0;
540 	}
541 	return(1);
542 }
543 
544 /*
545  * Convert a quotause list to an ASCII file of grace times.
546  */
547 int
writetimes(struct quotause * quplist,int outfd,int quotatype)548 writetimes(struct quotause *quplist, int outfd, int quotatype)
549 {
550 	struct quotause *qup;
551 	FILE *fd;
552 
553 	ftruncate(outfd, 0);
554 	lseek(outfd, 0, SEEK_SET);
555 	if ((fd = fdopen(dup(outfd), "w")) == NULL)
556 		err(1, "%s", tmpfil);
557 	(void)fprintf(fd,
558 	    "Time units may be: days, hours, minutes, or seconds\n");
559 	(void)fprintf(fd,
560 	    "Grace period before enforcing soft limits for %ss:\n",
561 	    qfextension[quotatype]);
562 	for (qup = quplist; qup; qup = qup->next) {
563 		(void)fprintf(fd, "%s: block grace period: %s, ",
564 		    qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
565 		(void)fprintf(fd, "file grace period: %s\n",
566 		    cvtstoa(qup->dqblk.dqb_itime));
567 	}
568 	fclose(fd);
569 	return(1);
570 }
571 
572 /*
573  * Merge changes of grace times in an ASCII file into a quotause list.
574  */
575 int
readtimes(struct quotause * quplist,int infd)576 readtimes(struct quotause *quplist, int infd)
577 {
578 	struct quotause *qup;
579 	FILE *fd;
580 	int cnt;
581 	char *cp;
582 	time_t itime, btime, iseconds, bseconds;
583 	char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
584 
585 	lseek(infd, 0, SEEK_SET);
586 	fd = fdopen(dup(infd), "r");
587 	if (fd == NULL) {
588 		warnx("can't re-read temp file!!");
589 		return(0);
590 	}
591 	/*
592 	 * Discard two title lines, then read lines to process.
593 	 */
594 	(void)fgets(line1, sizeof (line1), fd);
595 	(void)fgets(line1, sizeof (line1), fd);
596 	while (fgets(line1, sizeof (line1), fd) != NULL) {
597 		if ((fsp = strtok(line1, " \t:")) == NULL) {
598 			warnx("%s: bad format", line1);
599 			return(0);
600 		}
601 		if ((cp = strtok((char *)0, "\n")) == NULL) {
602 			warnx("%s: %s: bad format", fsp,
603 			    &fsp[strlen(fsp) + 1]);
604 			return(0);
605 		}
606 		cnt = sscanf(cp,
607 		    " block grace period: %d %9s file grace period: %d %9s",
608 		    (int *)&btime, bunits, (int *)&itime, iunits);
609 		if (cnt != 4) {
610 			warnx("%s:%s: bad format", fsp, cp);
611 			return(0);
612 		}
613 		if (cvtatos(btime, bunits, &bseconds) == 0)
614 			return(0);
615 		if (cvtatos(itime, iunits, &iseconds) == 0)
616 			return(0);
617 		for (qup = quplist; qup; qup = qup->next) {
618 			if (strcmp(fsp, qup->fsname))
619 				continue;
620 			qup->dqblk.dqb_btime = bseconds;
621 			qup->dqblk.dqb_itime = iseconds;
622 			qup->flags |= FOUND;
623 			break;
624 		}
625 	}
626 	fclose(fd);
627 	/*
628 	 * reset default grace periods for any filesystems
629 	 * that have not been found.
630 	 */
631 	for (qup = quplist; qup; qup = qup->next) {
632 		if (qup->flags & FOUND) {
633 			qup->flags &= ~FOUND;
634 			continue;
635 		}
636 		qup->dqblk.dqb_btime = 0;
637 		qup->dqblk.dqb_itime = 0;
638 	}
639 	return(1);
640 }
641 
642 /*
643  * Convert seconds to ASCII times.
644  */
645 char *
cvtstoa(time_t time)646 cvtstoa(time_t time)
647 {
648 	static char buf[20];
649 
650 	if (time % (24 * 60 * 60) == 0) {
651 		time /= 24 * 60 * 60;
652 		(void)snprintf(buf, sizeof buf, "%d day%s", (int)time,
653 		    time == 1 ? "" : "s");
654 	} else if (time % (60 * 60) == 0) {
655 		time /= 60 * 60;
656 		(void)snprintf(buf, sizeof buf, "%d hour%s", (int)time,
657 		    time == 1 ? "" : "s");
658 	} else if (time % 60 == 0) {
659 		time /= 60;
660 		(void)snprintf(buf, sizeof buf, "%d minute%s", (int)time,
661 		    time == 1 ? "" : "s");
662 	} else
663 		(void)snprintf(buf, sizeof buf, "%d second%s", (int)time,
664 		    time == 1 ? "" : "s");
665 	return(buf);
666 }
667 
668 /*
669  * Convert ASCII input times to seconds.
670  */
671 int
cvtatos(time_t time,char * units,time_t * seconds)672 cvtatos(time_t time, char *units, time_t *seconds)
673 {
674 
675 	if (memcmp(units, "second", 6) == 0)
676 		*seconds = time;
677 	else if (memcmp(units, "minute", 6) == 0)
678 		*seconds = time * 60;
679 	else if (memcmp(units, "hour", 4) == 0)
680 		*seconds = time * 60 * 60;
681 	else if (memcmp(units, "day", 3) == 0)
682 		*seconds = time * 24 * 60 * 60;
683 	else {
684 		(void)printf("%s: bad units, specify %s\n", units,
685 		    "days, hours, minutes, or seconds");
686 		return(0);
687 	}
688 	return(1);
689 }
690 
691 /*
692  * Free a list of quotause structures.
693  */
694 void
freeprivs(struct quotause * quplist)695 freeprivs(struct quotause *quplist)
696 {
697 	struct quotause *qup, *nextqup;
698 
699 	for (qup = quplist; qup; qup = nextqup) {
700 		nextqup = qup->next;
701 		free(qup);
702 	}
703 }
704 
705 /*
706  * Check whether a string is completely composed of digits.
707  */
708 int
alldigits(char * s)709 alldigits(char *s)
710 {
711 	int c;
712 
713 	c = *s++;
714 	do {
715 		if (!isdigit(c))
716 			return(0);
717 	} while ((c = *s++));
718 	return(1);
719 }
720 
721 /*
722  * Check to see if a particular quota is to be enabled.
723  */
724 int
hasquota(struct fstab * fs,int type,char ** qfnamep)725 hasquota(struct fstab *fs, int type, char **qfnamep)
726 {
727 	char *opt;
728 	char *cp;
729 	static char initname, usrname[100], grpname[100];
730 	static char buf[BUFSIZ];
731 
732 	if (!initname) {
733 		(void)snprintf(usrname, sizeof usrname, "%s%s",
734 		    qfextension[USRQUOTA], qfname);
735 		(void)snprintf(grpname, sizeof grpname, "%s%s",
736 		    qfextension[GRPQUOTA], qfname);
737 		initname = 1;
738 	}
739 	strlcpy(buf, fs->fs_mntops, sizeof buf);
740 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
741 		if ((cp = strchr(opt, '=')))
742 			*cp++ = '\0';
743 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
744 			break;
745 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
746 			break;
747 	}
748 	if (!opt)
749 		return(0);
750 	if (cp) {
751 		*qfnamep = cp;
752 		return(1);
753 	}
754 	(void)snprintf(buf, sizeof buf, "%s/%s.%s",
755 	    fs->fs_file, qfname, qfextension[type]);
756 	*qfnamep = buf;
757 	return(1);
758 }
759