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