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