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