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