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