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.19 1999/12/16 17:29:53 bouyer 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 extern char *optarg; 111 extern int optind; 112 long id, protoid; 113 int quotatype, tmpfd; 114 char *protoname; 115 int ch; 116 int tflag = 0, pflag = 0; 117 118 if (argc < 2) 119 usage(); 120 if (getuid()) 121 errx(1, "permission denied"); 122 protoname = NULL; 123 quotatype = USRQUOTA; 124 while ((ch = getopt(argc, argv, "ugtp:")) != -1) { 125 switch(ch) { 126 case 'p': 127 protoname = optarg; 128 pflag++; 129 break; 130 case 'g': 131 quotatype = GRPQUOTA; 132 break; 133 case 'u': 134 quotatype = USRQUOTA; 135 break; 136 case 't': 137 tflag++; 138 break; 139 default: 140 usage(); 141 } 142 } 143 argc -= optind; 144 argv += optind; 145 if (pflag) { 146 if ((protoid = getentry(protoname, quotatype)) == -1) 147 exit(1); 148 protoprivs = getprivs(protoid, quotatype); 149 for (qup = protoprivs; qup; qup = qup->next) { 150 qup->dqblk.dqb_btime = 0; 151 qup->dqblk.dqb_itime = 0; 152 } 153 while (argc-- > 0) { 154 if ((id = getentry(*argv++, quotatype)) < 0) 155 continue; 156 putprivs(id, quotatype, protoprivs); 157 } 158 exit(0); 159 } 160 tmpfd = mkstemp(tmpfil); 161 fchown(tmpfd, getuid(), getgid()); 162 if (tflag) { 163 protoprivs = getprivs(0, quotatype); 164 if (writetimes(protoprivs, tmpfd, quotatype) == 0) 165 exit(1); 166 if (editit(tmpfil) && readtimes(protoprivs, tmpfd)) 167 putprivs(0, quotatype, protoprivs); 168 freeprivs(protoprivs); 169 exit(0); 170 } 171 for ( ; argc > 0; argc--, argv++) { 172 if ((id = getentry(*argv, quotatype)) == -1) 173 continue; 174 curprivs = getprivs(id, quotatype); 175 if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0) 176 continue; 177 if (editit(tmpfil) && readprivs(curprivs, tmpfd)) 178 putprivs(id, quotatype, curprivs); 179 freeprivs(curprivs); 180 } 181 close(tmpfd); 182 unlink(tmpfil); 183 exit(0); 184 } 185 186 void 187 usage() 188 { 189 fprintf(stderr, "%s%s%s%s", 190 "Usage: edquota [-u] [-p username] username ...\n", 191 "\tedquota -g [-p groupname] groupname ...\n", 192 "\tedquota [-u] -t\n", "\tedquota -g -t\n"); 193 exit(1); 194 } 195 196 /* 197 * This routine converts a name for a particular quota type to 198 * an identifier. This routine must agree with the kernel routine 199 * getinoquota as to the interpretation of quota types. 200 */ 201 int 202 getentry(name, quotatype) 203 char *name; 204 int quotatype; 205 { 206 struct passwd *pw; 207 struct group *gr; 208 209 if (alldigits(name)) 210 return (atoi(name)); 211 switch(quotatype) { 212 case USRQUOTA: 213 if ((pw = getpwnam(name)) != NULL) 214 return (pw->pw_uid); 215 warnx("%s: no such user", name); 216 break; 217 case GRPQUOTA: 218 if ((gr = getgrnam(name)) != NULL) 219 return (gr->gr_gid); 220 warnx("%s: no such group", name); 221 break; 222 default: 223 warnx("%d: unknown quota type", quotatype); 224 break; 225 } 226 sleep(1); 227 return (-1); 228 } 229 230 /* 231 * Collect the requested quota information. 232 */ 233 struct quotause * 234 getprivs(id, quotatype) 235 long id; 236 int quotatype; 237 { 238 struct fstab *fs; 239 struct quotause *qup, *quptail; 240 struct quotause *quphead; 241 int qcmd, qupsize, fd; 242 char *qfpathname; 243 static int warned = 0; 244 extern int errno; 245 246 setfsent(); 247 quptail = NULL; 248 quphead = (struct quotause *)0; 249 qcmd = QCMD(Q_GETQUOTA, quotatype); 250 while ((fs = getfsent()) != NULL) { 251 if (strcmp(fs->fs_vfstype, "ffs")) 252 continue; 253 if (!hasquota(fs, quotatype, &qfpathname)) 254 continue; 255 qupsize = sizeof(*qup) + strlen(qfpathname); 256 if ((qup = (struct quotause *)malloc(qupsize)) == NULL) 257 errx(2, "out of memory"); 258 if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) { 259 if (errno == EOPNOTSUPP && !warned) { 260 warned++; 261 warnx( 262 "Quotas are not compiled into this kernel"); 263 sleep(3); 264 } 265 if ((fd = open(qfpathname, O_RDONLY)) < 0) { 266 fd = open(qfpathname, O_RDWR|O_CREAT, 0640); 267 if (fd < 0 && errno != ENOENT) { 268 warnx("open `%s'", qfpathname); 269 free(qup); 270 continue; 271 } 272 warnx("Creating quota file %s", qfpathname); 273 sleep(3); 274 (void) fchown(fd, getuid(), 275 getentry(quotagroup, GRPQUOTA)); 276 (void) fchmod(fd, 0640); 277 } 278 (void)lseek(fd, (off_t)(id * sizeof(struct dqblk)), 279 SEEK_SET); 280 switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 281 case 0: /* EOF */ 282 /* 283 * Convert implicit 0 quota (EOF) 284 * into an explicit one (zero'ed dqblk) 285 */ 286 memset((caddr_t)&qup->dqblk, 0, 287 sizeof(struct dqblk)); 288 break; 289 290 case sizeof(struct dqblk): /* OK */ 291 break; 292 293 default: /* ERROR */ 294 warn("read error in `%s'", qfpathname); 295 close(fd); 296 free(qup); 297 continue; 298 } 299 close(fd); 300 } 301 strcpy(qup->qfname, qfpathname); 302 strcpy(qup->fsname, fs->fs_file); 303 if (quphead == NULL) 304 quphead = qup; 305 else 306 quptail->next = qup; 307 quptail = qup; 308 qup->next = 0; 309 } 310 endfsent(); 311 return (quphead); 312 } 313 314 /* 315 * Store the requested quota information. 316 */ 317 void 318 putprivs(id, quotatype, quplist) 319 long id; 320 int quotatype; 321 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, &qup->dqblk) == 0) 329 continue; 330 if ((fd = open(qup->qfname, O_WRONLY)) < 0) { 331 warnx("open `%s'", qup->qfname); 332 } else { 333 (void)lseek(fd, 334 (off_t)(id * (long)sizeof (struct dqblk)), 335 SEEK_SET); 336 if (write(fd, &qup->dqblk, sizeof (struct dqblk)) != 337 sizeof (struct dqblk)) 338 warnx("writing `%s'", qup->qfname); 339 close(fd); 340 } 341 } 342 } 343 344 /* 345 * Take a list of privileges and get it edited. 346 */ 347 int 348 editit(tmpfile) 349 char *tmpfile; 350 { 351 long omask; 352 int pid, stat; 353 354 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 355 top: 356 if ((pid = fork()) < 0) { 357 358 if (errno == EPROCLIM) { 359 warnx("You have too many processes"); 360 return(0); 361 } 362 if (errno == EAGAIN) { 363 sleep(1); 364 goto top; 365 } 366 warn("fork"); 367 return (0); 368 } 369 if (pid == 0) { 370 char *ed; 371 372 sigsetmask(omask); 373 setgid(getgid()); 374 setuid(getuid()); 375 if ((ed = getenv("EDITOR")) == (char *)0) 376 ed = _PATH_VI; 377 execlp(ed, ed, tmpfile, 0); 378 err(1, "%s", ed); 379 } 380 waitpid(pid, &stat, 0); 381 sigsetmask(omask); 382 if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0) 383 return (0); 384 return (1); 385 } 386 387 /* 388 * Convert a quotause list to an ASCII file. 389 */ 390 int 391 writeprivs(quplist, outfd, name, quotatype) 392 struct quotause *quplist; 393 int outfd; 394 char *name; 395 int quotatype; 396 { 397 struct quotause *qup; 398 FILE *fd; 399 400 ftruncate(outfd, 0); 401 (void)lseek(outfd, (off_t)0, SEEK_SET); 402 if ((fd = fdopen(dup(outfd), "w")) == NULL) 403 errx(1, "fdopen `%s'", tmpfil); 404 fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name); 405 for (qup = quplist; qup; qup = qup->next) { 406 fprintf(fd, "%s: %s %d, limits (soft = %d, hard = %d)\n", 407 qup->fsname, "blocks in use:", 408 (int)(dbtob((u_quad_t)qup->dqblk.dqb_curblocks) / 1024), 409 (int)(dbtob((u_quad_t)qup->dqblk.dqb_bsoftlimit) / 1024), 410 (int)(dbtob((u_quad_t)qup->dqblk.dqb_bhardlimit) / 1024)); 411 fprintf(fd, "%s %d, limits (soft = %d, hard = %d)\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 */ 422 int 423 readprivs(quplist, infd) 424 struct quotause *quplist; 425 int infd; 426 { 427 struct quotause *qup; 428 FILE *fd; 429 int cnt; 430 char *cp; 431 struct dqblk dqblk; 432 char *fsp, line1[BUFSIZ], line2[BUFSIZ]; 433 434 (void)lseek(infd, (off_t)0, SEEK_SET); 435 fd = fdopen(dup(infd), "r"); 436 if (fd == NULL) { 437 warn("Can't re-read temp file"); 438 return (0); 439 } 440 /* 441 * Discard title line, then read pairs of lines to process. 442 */ 443 (void) fgets(line1, sizeof (line1), fd); 444 while (fgets(line1, sizeof (line1), fd) != NULL && 445 fgets(line2, sizeof (line2), fd) != NULL) { 446 if ((fsp = strtok(line1, " \t:")) == NULL) { 447 warnx("%s: bad format", line1); 448 return (0); 449 } 450 if ((cp = strtok((char *)0, "\n")) == NULL) { 451 warnx("%s: %s: bad format", fsp, 452 &fsp[strlen(fsp) + 1]); 453 return (0); 454 } 455 cnt = sscanf(cp, 456 " blocks 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(fd); 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 535 writetimes(quplist, outfd, quotatype) 536 struct quotause *quplist; 537 int outfd; 538 int quotatype; 539 { 540 struct quotause *qup; 541 FILE *fd; 542 543 ftruncate(outfd, 0); 544 (void)lseek(outfd, (off_t)0, SEEK_SET); 545 if ((fd = fdopen(dup(outfd), "w")) == NULL) 546 err(1, "fdopen `%s'", tmpfil); 547 fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n"); 548 fprintf(fd, "Grace period before enforcing soft limits for %ss:\n", 549 qfextension[quotatype]); 550 for (qup = quplist; qup; qup = qup->next) { 551 fprintf(fd, "%s: block grace period: %s, ", 552 qup->fsname, cvtstoa(qup->dqblk.dqb_btime)); 553 fprintf(fd, "file grace period: %s\n", 554 cvtstoa(qup->dqblk.dqb_itime)); 555 } 556 fclose(fd); 557 return (1); 558 } 559 560 /* 561 * Merge changes of grace times in an ASCII file into a quotause list. 562 */ 563 int 564 readtimes(quplist, infd) 565 struct quotause *quplist; 566 int infd; 567 { 568 struct quotause *qup; 569 FILE *fd; 570 int cnt; 571 char *cp; 572 long litime, lbtime; 573 time_t itime, btime, iseconds, bseconds; 574 char *fsp, bunits[10], iunits[10], line1[BUFSIZ]; 575 576 (void)lseek(infd, (off_t)0, SEEK_SET); 577 fd = fdopen(dup(infd), "r"); 578 if (fd == NULL) { 579 warnx("Can't re-read temp file!!"); 580 return (0); 581 } 582 /* 583 * Discard two title lines, then read lines to process. 584 */ 585 (void) fgets(line1, sizeof (line1), fd); 586 (void) fgets(line1, sizeof (line1), fd); 587 while (fgets(line1, sizeof (line1), fd) != NULL) { 588 if ((fsp = strtok(line1, " \t:")) == NULL) { 589 warnx("%s: bad format", line1); 590 return (0); 591 } 592 if ((cp = strtok((char *)0, "\n")) == NULL) { 593 warnx("%s: %s: bad format", fsp, 594 &fsp[strlen(fsp) + 1]); 595 return (0); 596 } 597 cnt = sscanf(cp, 598 " block grace period: %ld %s file grace period: %ld %s", 599 &lbtime, bunits, &litime, iunits); 600 if (cnt != 4) { 601 warnx("%s:%s: bad format", fsp, cp); 602 return (0); 603 } 604 itime = (time_t)litime; 605 btime = (time_t)lbtime; 606 if (cvtatos(btime, bunits, &bseconds) == 0) 607 return (0); 608 if (cvtatos(itime, iunits, &iseconds) == 0) 609 return (0); 610 for (qup = quplist; qup; qup = qup->next) { 611 if (strcmp(fsp, qup->fsname)) 612 continue; 613 qup->dqblk.dqb_btime = bseconds; 614 qup->dqblk.dqb_itime = iseconds; 615 qup->flags |= FOUND; 616 break; 617 } 618 } 619 fclose(fd); 620 /* 621 * reset default grace periods for any filesystems 622 * that have not been found. 623 */ 624 for (qup = quplist; qup; qup = qup->next) { 625 if (qup->flags & FOUND) { 626 qup->flags &= ~FOUND; 627 continue; 628 } 629 qup->dqblk.dqb_btime = 0; 630 qup->dqblk.dqb_itime = 0; 631 } 632 return (1); 633 } 634 635 /* 636 * Convert seconds to ASCII times. 637 */ 638 char * 639 cvtstoa(time) 640 time_t time; 641 { 642 static char buf[20]; 643 644 if (time % (24 * 60 * 60) == 0) { 645 time /= 24 * 60 * 60; 646 snprintf(buf, sizeof buf, "%ld day%s", (long)time, 647 time == 1 ? "" : "s"); 648 } else if (time % (60 * 60) == 0) { 649 time /= 60 * 60; 650 sprintf(buf, "%ld hour%s", (long)time, time == 1 ? "" : "s"); 651 } else if (time % 60 == 0) { 652 time /= 60; 653 sprintf(buf, "%ld minute%s", (long)time, time == 1 ? "" : "s"); 654 } else 655 sprintf(buf, "%ld second%s", (long)time, time == 1 ? "" : "s"); 656 return (buf); 657 } 658 659 /* 660 * Convert ASCII input times to seconds. 661 */ 662 int 663 cvtatos(time, units, seconds) 664 time_t time; 665 char *units; 666 time_t *seconds; 667 { 668 669 if (memcmp(units, "second", 6) == 0) 670 *seconds = time; 671 else if (memcmp(units, "minute", 6) == 0) 672 *seconds = time * 60; 673 else if (memcmp(units, "hour", 4) == 0) 674 *seconds = time * 60 * 60; 675 else if (memcmp(units, "day", 3) == 0) 676 *seconds = time * 24 * 60 * 60; 677 else { 678 printf("%s: bad units, specify %s\n", units, 679 "days, hours, minutes, or seconds"); 680 return (0); 681 } 682 return (1); 683 } 684 685 /* 686 * Free a list of quotause structures. 687 */ 688 void 689 freeprivs(quplist) 690 struct quotause *quplist; 691 { 692 struct quotause *qup, *nextqup; 693 694 for (qup = quplist; qup; qup = nextqup) { 695 nextqup = qup->next; 696 free(qup); 697 } 698 } 699 700 /* 701 * Check whether a string is completely composed of digits. 702 */ 703 int 704 alldigits(s) 705 char *s; 706 { 707 int c; 708 709 c = *s++; 710 do { 711 if (!isdigit(c)) 712 return (0); 713 } while ((c = *s++) != 0); 714 return (1); 715 } 716 717 /* 718 * Check to see if a particular quota is to be enabled. 719 */ 720 int 721 hasquota(fs, type, qfnamep) 722 struct fstab *fs; 723 int type; 724 char **qfnamep; 725 { 726 char *opt; 727 char *cp; 728 static char initname, usrname[100], grpname[100]; 729 static char buf[BUFSIZ]; 730 731 if (!initname) { 732 sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 733 sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 734 initname = 1; 735 } 736 strcpy(buf, fs->fs_mntops); 737 cp = NULL; 738 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 739 if ((cp = strchr(opt, '=')) != NULL) 740 *cp++ = '\0'; 741 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 742 break; 743 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 744 break; 745 } 746 if (!opt) 747 return (0); 748 if (cp) { 749 *qfnamep = cp; 750 return (1); 751 } 752 (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 753 *qfnamep = buf; 754 return (1); 755 } 756