1/* 2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * Copyright (c) 1980, 1990, 1993 25 * The Regents of the University of California. All rights reserved. 26 * 27 * This code is derived from software contributed to Berkeley by 28 * Robert Elz at The University of Melbourne. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in the 37 * documentation and/or other materials provided with the distribution. 38 * 3. All advertising materials mentioning features or use of this software 39 * must display the following acknowledgement: 40 * This product includes software developed by the University of 41 * California, Berkeley and its contributors. 42 * 4. Neither the name of the University nor the names of its contributors 43 * may be used to endorse or promote products derived from this software 44 * without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 */ 58 59#include <sys/cdefs.h> 60 61#ifndef lint 62__unused static char copyright[] = 63"@(#) Copyright (c) 1980, 1990, 1993\n\ 64 The Regents of the University of California. All rights reserved.\n"; 65#endif /* not lint */ 66 67#ifndef lint 68__unused static char sccsid[] = "@(#)edquota.c 8.3 (Berkeley) 4/27/95"; 69#endif /* not lint */ 70 71/* 72 * Disk quota editor. 73 */ 74#include <sys/param.h> 75#include <sys/stat.h> 76#include <sys/file.h> 77#ifdef __APPLE__ 78#include <sys/mount.h> 79#endif /* __APPLE__ */ 80#include <sys/wait.h> 81#include <sys/queue.h> 82#include <sys/quota.h> 83#include <errno.h> 84#include <fstab.h> 85#include <pwd.h> 86#include <grp.h> 87#include <ctype.h> 88#include <stdio.h> 89#include <stdlib.h> 90#include <string.h> 91#include <unistd.h> 92#include <signal.h> 93#include "pathnames.h" 94 95#ifdef __APPLE__ 96#include <libkern/OSByteOrder.h> 97#endif /* __APPLE__ */ 98 99char *qfname = QUOTAFILENAME; 100char *qfextension[] = INITQFNAMES; 101char *quotagroup = QUOTAGROUP; 102char tmpfil[] = _PATH_TMP; 103 104#ifdef __APPLE__ 105u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS; 106#endif /* __APPLE__ */ 107 108struct quotause { 109 struct quotause *next; 110 long flags; 111 struct dqblk dqblk; 112 char fsname[MAXPATHLEN + 1]; 113 char qfname[1]; /* actually longer */ 114} *getprivs(); 115#define FOUND 0x01 116 117int alldigits __P((char *)); 118int cvtatos __P((time_t, char *, time_t *)); 119int editit __P((char *)); 120void freeprivs __P((struct quotause *)); 121int getentry __P((char *, int)); 122int hasquota __P((struct statfs *, int, char **)); 123void putprivs __P((uid_t, int, struct quotause *)); 124int readprivs __P((struct quotause *, int)); 125int readtimes __P((struct quotause *, int)); 126void usage __P((void)); 127int writeprivs __P((struct quotause *, int, char *, int)); 128int writetimes __P((struct quotause *, int, int)); 129 130#ifdef __APPLE__ 131int qfinit(int, struct statfs *, int); 132int qflookup(int, uid_t, int, struct dqblk *); 133int qfupdate(int, uid_t, int, struct dqblk *); 134#endif /* __APPLE__ */ 135 136 137int 138main(argc, argv) 139 register char **argv; 140 int argc; 141{ 142 register struct quotause *qup, *protoprivs, *curprivs; 143 extern char *optarg; 144 extern int optind; 145 register uid_t id, protoid; 146 register int quotatype, tmpfd; 147 char *protoname = NULL, ch; 148 int tflag = 0, pflag = 0; 149 150 if (argc < 2) 151 usage(); 152 if (getuid()) { 153 fprintf(stderr, "edquota: permission denied\n"); 154 exit(1); 155 } 156 quotatype = USRQUOTA; 157 158 while ((ch = getopt(argc, argv, "ugtp:")) != EOF) { 159 switch(ch) { 160 case 'p': 161 protoname = optarg; 162 pflag++; 163 break; 164 case 'g': 165 quotatype = GRPQUOTA; 166 break; 167 case 'u': 168 quotatype = USRQUOTA; 169 break; 170 case 't': 171 tflag++; 172 break; 173 default: 174 usage(); 175 } 176 } 177 argc -= optind; 178 argv += optind; 179 if (pflag) { 180 if ((protoid = getentry(protoname, quotatype)) == -1) 181 exit(1); 182 protoprivs = getprivs(protoid, quotatype); 183#ifdef __APPLE__ 184 if (protoprivs == (struct quotause *) NULL) 185 exit(0); 186#endif /* __APPLE__ */ 187 for (qup = protoprivs; qup; qup = qup->next) { 188 qup->dqblk.dqb_btime = 0; 189 qup->dqblk.dqb_itime = 0; 190 } 191 while (argc-- > 0) { 192 if ((id = getentry(*argv++, quotatype)) == -1) 193 continue; 194 /* 195 * Set the ID in each disk quota block to match 196 * the ID it's supposed to go with. 197 */ 198 for (qup = protoprivs; qup; qup = qup->next) { 199 qup->dqblk.dqb_id = id; 200 } 201 putprivs(id, quotatype, protoprivs); 202 } 203 exit(0); 204 } 205 tmpfd = mkstemp(tmpfil); 206 fchown(tmpfd, getuid(), getgid()); 207 if (tflag) { 208 protoprivs = getprivs(0, quotatype); 209#ifdef __APPLE__ 210 if (protoprivs == (struct quotause *) NULL) 211 exit(0); 212#endif /* __APPLE__ */ 213 if (writetimes(protoprivs, tmpfd, quotatype) == 0) 214 exit(1); 215 if (editit(tmpfil)) { 216 /* 217 * Re-open tmpfil to be editor independent. 218 */ 219 close(tmpfd); 220 tmpfd = open(tmpfil, O_RDWR, 0); 221 if (tmpfd < 0) { 222 freeprivs(protoprivs); 223 unlink(tmpfil); 224 exit(1); 225 } 226 if (readtimes(protoprivs, tmpfd)) 227 putprivs(0, quotatype, protoprivs); 228 } 229 freeprivs(protoprivs); 230 exit(0); 231 } 232 for ( ; argc > 0; argc--, argv++) { 233 if ((id = getentry(*argv, quotatype)) == -1) 234 continue; 235 curprivs = getprivs(id, quotatype); 236#ifdef __APPLE__ 237 if (curprivs == (struct quotause *) NULL) 238 exit(0); 239#endif /* __APPLE__ */ 240 241 242 if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0) { 243 freeprivs(curprivs); 244 continue; 245 } 246 if (editit(tmpfil)) { 247 /* 248 * Re-open tmpfil to be editor independent. 249 */ 250 close(tmpfd); 251 tmpfd = open(tmpfil, O_RDWR, 0); 252 if (tmpfd < 0) { 253 freeprivs(curprivs); 254 unlink(tmpfil); 255 exit(1); 256 } 257 if (readprivs(curprivs, tmpfd)) 258 putprivs(id, quotatype, curprivs); 259 } 260 freeprivs(curprivs); 261 } 262 close(tmpfd); 263 unlink(tmpfil); 264 exit(0); 265} 266 267void 268usage() 269{ 270 fprintf(stderr, "%s%s%s%s", 271 "Usage: edquota [-u] [-p username] username ...\n", 272 "\tedquota -g [-p groupname] groupname ...\n", 273 "\tedquota [-u] -t\n", "\tedquota -g -t\n"); 274#ifdef __APPLE__ 275 fprintf(stderr, "\nQuota file editing triggers only on filesystems with a\n"); 276 fprintf(stderr, "%s.%s or %s.%s file located at its root.\n", 277 QUOTAOPSNAME, qfextension[USRQUOTA], 278 QUOTAOPSNAME, qfextension[GRPQUOTA]); 279#endif /* __APPLE__ */ 280 exit(1); 281} 282 283/* 284 * This routine converts a name for a particular quota type to 285 * an identifier. This routine must agree with the kernel routine 286 * getinoquota as to the interpretation of quota types. 287 */ 288int 289getentry(name, quotatype) 290 char *name; 291 int quotatype; 292{ 293 struct passwd *pw; 294 struct group *gr; 295 296 if (alldigits(name)) 297 return (atoi(name)); 298 switch(quotatype) { 299 case USRQUOTA: 300 if ((pw = getpwnam(name)) != NULL) 301 return (pw->pw_uid); 302 fprintf(stderr, "%s: no such user\n", name); 303 break; 304 case GRPQUOTA: 305 if ((gr = getgrnam(name))) 306 return (gr->gr_gid); 307 fprintf(stderr, "%s: no such group\n", name); 308 break; 309 default: 310 fprintf(stderr, "%d: unknown quota type\n", quotatype); 311 break; 312 } 313 sleep(1); 314 return (-1); 315} 316 317/* 318 * Collect the requested quota information. 319 */ 320#ifdef __APPLE__ 321struct quotause * 322getprivs(id, quotatype) 323 register uid_t id; 324 int quotatype; 325{ 326 struct statfs *fst; 327 register struct quotause *qup, *quptail; 328 struct quotause *quphead; 329 int qcmd, fd; 330 size_t qupsize; 331 char *qfpathname; 332 static int warned = 0; 333 int nfst, i; 334 extern int errno; 335 336 337 quptail = quphead = (struct quotause *)0; 338 qcmd = QCMD(Q_GETQUOTA, quotatype); 339 340 nfst = getmntinfo(&fst, MNT_WAIT); 341 if (nfst==0) { 342 fprintf(stderr, "edquota: no mounted filesystems\n"); 343 exit(1); 344 } 345 346 for (i=0; i<nfst; i++) { 347 if (strcmp(fst[i].f_fstypename, "hfs")) { 348 continue; 349 } 350 if (!hasquota(&fst[i], quotatype, &qfpathname)) 351 continue; 352 qupsize = sizeof(*qup) + strlen(qfpathname); 353 if ((qup = (struct quotause *)malloc(qupsize)) == NULL) { 354 fprintf(stderr, "edquota: out of memory\n"); 355 exit(2); 356 } 357 if (quotactl(fst[i].f_mntonname, qcmd, id, (char *)&qup->dqblk) != 0) { 358 if (errno == ENOTSUP && !warned) { 359 warned++; 360 fprintf(stderr, "Warning: %s\n", 361 "Quotas are not compiled into this kernel"); 362 sleep(3); 363 } 364 if ((fd = open(qfpathname, O_RDONLY)) < 0) { 365 fd = open(qfpathname, O_RDWR|O_CREAT, 0640); 366 if (fd < 0 && errno != ENOENT) { 367 perror(qfpathname); 368 free(qup); 369 continue; 370 } 371 fprintf(stderr, "Creating quota file %s\n", 372 qfpathname); 373 sleep(3); 374 (void) fchown(fd, getuid(), 375 getentry(quotagroup, GRPQUOTA)); 376 (void) fchmod(fd, 0640); 377 if (qfinit(fd, &fst[i], quotatype)) { 378 perror(qfpathname); 379 close(fd); 380 free(qup); 381 continue; 382 } 383 } 384 if (qflookup(fd, id, quotatype, &qup->dqblk) != 0) { 385 fprintf(stderr, "edquota: lookup error in "); 386 perror(qfpathname); 387 close(fd); 388 free(qup); 389 continue; 390 } 391 close(fd); 392 } 393 strcpy(qup->qfname, qfpathname); // malloc'd size is correct for this 394 strlcpy(qup->fsname, fst[i].f_mntonname, sizeof(qup->fsname)); 395 396 if (quphead == NULL) 397 quphead = qup; 398 else 399 quptail->next = qup; 400 quptail = qup; 401 qup->next = 0; 402 } 403 return (quphead); 404} 405#else 406struct quotause * 407getprivs(id, quotatype) 408 register long id; 409 int quotatype; 410{ 411 register struct fstab *fs; 412 register struct quotause *qup, *quptail; 413 struct quotause *quphead; 414 int qcmd, fd; 415 size_t qupsize; 416 char *qfpathname; 417 static int warned = 0; 418 extern int errno; 419 420 setfsent(); 421 quphead = (struct quotause *)0; 422 qcmd = QCMD(Q_GETQUOTA, quotatype); 423 while (fs = getfsent()) { 424 if (!hasquota(fs, quotatype, &qfpathname)) 425 continue; 426 qupsize = sizeof(*qup) + strlen(qfpathname); 427 if ((qup = (struct quotause *)malloc(qupsize)) == NULL) { 428 fprintf(stderr, "edquota: out of memory\n"); 429 exit(2); 430 } 431 if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) { 432 if (errno == ENOTSUP && !warned) { 433 warned++; 434 fprintf(stderr, "Warning: %s\n", 435 "Quotas are not compiled into this kernel"); 436 sleep(3); 437 } 438 if ((fd = open(qfpathname, O_RDONLY)) < 0) { 439 fd = open(qfpathname, O_RDWR|O_CREAT, 0640); 440 if (fd < 0 && errno != ENOENT) { 441 perror(qfpathname); 442 free(qup); 443 continue; 444 } 445 fprintf(stderr, "Creating quota file %s\n", 446 qfpathname); 447 sleep(3); 448 (void) fchown(fd, getuid(), 449 getentry(quotagroup, GRPQUOTA)); 450 (void) fchmod(fd, 0640); 451 } 452 lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET); 453 switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 454 case 0: /* EOF */ 455 /* 456 * Convert implicit 0 quota (EOF) 457 * into an explicit one (zero'ed dqblk) 458 */ 459 bzero((caddr_t)&qup->dqblk, 460 sizeof(struct dqblk)); 461 break; 462 463 case sizeof(struct dqblk): /* OK */ 464 break; 465 466 default: /* ERROR */ 467 fprintf(stderr, "edquota: read error in "); 468 perror(qfpathname); 469 close(fd); 470 free(qup); 471 continue; 472 } 473 close(fd); 474 } 475 strcpy(qup->qfname, qfpathname); // malloc'd size is correct for this 476 strlcpy(qup->fsname, fs->fs_file, sizeof(qup->fsname)); 477 if (quphead == NULL) 478 quphead = qup; 479 else 480 quptail->next = qup; 481 quptail = qup; 482 qup->next = 0; 483 } 484 endfsent(); 485 return (quphead); 486} 487#endif /* __APPLE */ 488 489#ifdef __APPLE__ 490#define ONEGIGABYTE (1024*1024*1024) 491/* 492 * Initialize a new quota file. 493 */ 494int 495qfinit(fd, fst, type) 496 int fd; 497 struct statfs *fst; 498 int type; 499{ 500 struct dqfilehdr dqhdr = {0}; 501 u_int64_t fs_size; 502 int64_t max = 0; 503 504 /* 505 * Calculate the size of the hash table from the size of 506 * the file system. Note that the open addressing hashing 507 * used by the quota file assumes that this table will not 508 * be more than 90% full. 509 */ 510 fs_size = (u_int64_t)fst->f_blocks * (u_int64_t)fst->f_bsize; 511 512 if (type == USRQUOTA) { 513 max = QF_USERS_PER_GB * (fs_size / ONEGIGABYTE); 514 515 if (max < QF_MIN_USERS) 516 max = QF_MIN_USERS; 517 else if (max > QF_MAX_USERS) 518 max = QF_MAX_USERS; 519 } else if (type == GRPQUOTA) { 520 max = QF_GROUPS_PER_GB * (fs_size / ONEGIGABYTE); 521 522 if (max < QF_MIN_GROUPS) 523 max = QF_MIN_GROUPS; 524 else if (max > QF_MAX_GROUPS) 525 max = QF_MAX_GROUPS; 526 } 527 /* Round up to a power of 2 */ 528 if (max && !powerof2(max)) { 529 int64_t x = max; 530 max = 4; 531 while (x>>1 != 1) { 532 x = x >> 1; 533 max = max << 1; 534 } 535 } 536 537 (void) ftruncate(fd, (off_t)((max + 1) * sizeof(struct dqblk))); 538 dqhdr.dqh_magic = OSSwapHostToBigInt32(quotamagic[type]); 539 dqhdr.dqh_version = OSSwapHostToBigConstInt32(QF_VERSION); 540 dqhdr.dqh_maxentries = OSSwapHostToBigInt32((int32_t)max); 541 dqhdr.dqh_btime = OSSwapHostToBigConstInt32(MAX_DQ_TIME); 542 dqhdr.dqh_itime = OSSwapHostToBigConstInt32(MAX_IQ_TIME); 543 memmove(dqhdr.dqh_string, QF_STRING_TAG, strlen(QF_STRING_TAG)); 544 (void) lseek(fd, 0, L_SET); 545 (void) write(fd, &dqhdr, sizeof(dqhdr)); 546 547 return (0); 548} 549 550/* 551 * Lookup an entry in a quota file. 552 */ 553int 554qflookup(fd, id, type, dqbp) 555 int fd; 556 uid_t id; 557 int type; 558 struct dqblk *dqbp; 559{ 560 struct dqfilehdr dqhdr; 561 int i, skip, last, m; 562 int mask; 563 564 bzero(dqbp, sizeof(struct dqblk)); 565 566 if (id == 0) 567 return (0); 568 569 lseek(fd, 0, L_SET); 570 if (read(fd, &dqhdr, sizeof(struct dqfilehdr)) != sizeof(struct dqfilehdr)) 571 return (-1); 572 573 /* Sanity check the quota file header. */ 574 if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) || 575 (OSSwapBigToHostInt32(dqhdr.dqh_version) > 1) || 576 (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) { 577 fprintf(stderr, "quota: invalid quota file header\n"); 578 return (-1); 579 } 580 581 m = OSSwapBigToHostInt32(dqhdr.dqh_maxentries); 582 mask = m - 1; 583 i = dqhash1(id, dqhashshift(m), mask); 584 skip = dqhash2(id, mask); 585 586 for (last = (i + (m-1) * skip) & mask; 587 i != last; 588 i = (i + skip) & mask) { 589 lseek(fd, dqoffset(i), L_SET); 590 if (read(fd, dqbp, sizeof(struct dqblk)) < sizeof(struct dqblk)) 591 return (-1); 592 /* 593 * Stop when an empty entry is found 594 * or we encounter a matching id. 595 */ 596 if (dqbp->dqb_id == 0 || OSSwapBigToHostInt32(dqbp->dqb_id) == id) 597 break; 598 } 599 /* Put data in host native byte order. */ 600 dqbp->dqb_bhardlimit = OSSwapBigToHostInt64(dqbp->dqb_bhardlimit); 601 dqbp->dqb_bsoftlimit = OSSwapBigToHostInt64(dqbp->dqb_bsoftlimit); 602 dqbp->dqb_curbytes = OSSwapBigToHostInt64(dqbp->dqb_curbytes); 603 dqbp->dqb_ihardlimit = OSSwapBigToHostInt32(dqbp->dqb_ihardlimit); 604 dqbp->dqb_isoftlimit = OSSwapBigToHostInt32(dqbp->dqb_isoftlimit); 605 dqbp->dqb_curinodes = OSSwapBigToHostInt32(dqbp->dqb_curinodes); 606 dqbp->dqb_btime = OSSwapBigToHostInt32(dqbp->dqb_btime); 607 dqbp->dqb_itime = OSSwapBigToHostInt32(dqbp->dqb_itime); 608 dqbp->dqb_id = OSSwapBigToHostInt32(dqbp->dqb_id); 609 610 return (0); 611} 612#endif /* __APPLE */ 613 614 615/* 616 * Store the requested quota information. 617 */ 618void 619putprivs(id, quotatype, quplist) 620 uid_t id; 621 int quotatype; 622 struct quotause *quplist; 623{ 624 register struct quotause *qup; 625 int qcmd, fd; 626 627 qcmd = QCMD(Q_SETQUOTA, quotatype); 628 for (qup = quplist; qup; qup = qup->next) { 629 if (quotactl(qup->fsname, qcmd, id, (char *)&qup->dqblk) == 0) 630 continue; 631#ifdef __APPLE__ 632 if ((fd = open(qup->qfname, O_RDWR)) < 0) { 633 perror(qup->qfname); 634 } else { 635 if (qfupdate(fd, id, quotatype, &qup->dqblk) != 0) { 636 fprintf(stderr, "edquota: "); 637 perror(qup->qfname); 638 } 639#else 640 if ((fd = open(qup->qfname, O_WRONLY)) < 0) { 641 perror(qup->qfname); 642 } else { 643 lseek(fd, 644 (off_t)(id * (long)sizeof (struct dqblk)), L_SET); 645 if (write(fd, &qup->dqblk, sizeof (struct dqblk)) != 646 sizeof (struct dqblk)) { 647 fprintf(stderr, "edquota: "); 648 perror(qup->qfname); 649 } 650#endif /* __APPLE */ 651 close(fd); 652 } 653 } 654} 655 656#ifdef __APPLE__ 657/* 658 * Update an entry in a quota file. 659 */ 660int 661qfupdate(fd, id, type, dqbp) 662 int fd; 663 uid_t id; 664 int type; 665 struct dqblk *dqbp; 666{ 667 struct dqblk dqbuf; 668 struct dqfilehdr dqhdr; 669 int i, skip, last, m; 670 unsigned int mask; 671 672 if (id == 0) 673 return (0); 674 675 lseek(fd, 0, L_SET); 676 if (read(fd, &dqhdr, sizeof(struct dqfilehdr)) != sizeof(struct dqfilehdr)) 677 return (-1); 678 679 /* Sanity check the quota file header. */ 680 if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) || 681 (OSSwapBigToHostInt32(dqhdr.dqh_version) > QF_VERSION) || 682 (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) { 683 fprintf(stderr, "quota: invalid quota file header\n"); 684 return (EINVAL); 685 } 686 687 m = OSSwapBigToHostInt32(dqhdr.dqh_maxentries); 688 mask = m - 1; 689 i = dqhash1(id, dqhashshift(m), mask); 690 skip = dqhash2(id, mask); 691 692 for (last = (i + (m-1) * skip) & mask; 693 i != last; 694 i = (i + skip) & mask) { 695 lseek(fd, dqoffset(i), L_SET); 696 if (read(fd, &dqbuf, sizeof(struct dqblk)) < sizeof(struct dqblk)) 697 return (-1); 698 /* 699 * Stop when an empty entry is found 700 * or we encounter a matching id. 701 */ 702 if (dqbuf.dqb_id == 0 || OSSwapBigToHostInt32(dqbuf.dqb_id) == id) { 703 /* Convert buffer to big endian before writing. */ 704 struct dqblk tblk; 705 706 tblk.dqb_bhardlimit = OSSwapHostToBigInt64(dqbp->dqb_bhardlimit); 707 tblk.dqb_bsoftlimit = OSSwapHostToBigInt64(dqbp->dqb_bsoftlimit); 708 tblk.dqb_curbytes = OSSwapHostToBigInt64(dqbp->dqb_curbytes); 709 tblk.dqb_ihardlimit = OSSwapHostToBigInt32(dqbp->dqb_ihardlimit); 710 tblk.dqb_isoftlimit = OSSwapHostToBigInt32(dqbp->dqb_isoftlimit); 711 tblk.dqb_curinodes = OSSwapHostToBigInt32(dqbp->dqb_curinodes); 712 tblk.dqb_btime = OSSwapHostToBigInt32((int)dqbp->dqb_btime); 713 tblk.dqb_itime = OSSwapHostToBigInt32((int)dqbp->dqb_itime); 714 tblk.dqb_id = OSSwapHostToBigInt32(id); 715 716 lseek(fd, dqoffset(i), L_SET); 717 if (write(fd, &tblk, sizeof (struct dqblk)) != 718 sizeof (struct dqblk)) { 719 return (-1); 720 } 721 return (0); 722 } 723 } 724 errno = ENOSPC; 725 return (-1); 726} 727#endif /* __APPLE__ */ 728 729/* 730 * Take a list of priviledges and get it edited. 731 */ 732int 733editit(tmpfile) 734 char *tmpfile; 735{ 736 int omask; 737 int pid, stat; 738 extern char *getenv(); 739 740 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 741 top: 742 if ((pid = fork()) < 0) { 743 extern int errno; 744 745 if (errno == EPROCLIM) { 746 fprintf(stderr, "You have too many processes\n"); 747 return(0); 748 } 749 if (errno == EAGAIN) { 750 sleep(1); 751 goto top; 752 } 753 perror("fork"); 754 return (0); 755 } 756 if (pid == 0) { 757 register char *ed; 758 struct passwd *pwd = getpwuid(getuid()); 759 gid_t newgid = getgid(); 760 761 setgid(newgid); 762 if (pwd) initgroups(pwd->pw_name, newgid); 763 setuid(getuid()); 764 sigsetmask(omask); 765 766 if ((ed = getenv("EDITOR")) == (char *)0) 767 ed = _PATH_VI; 768 execlp(ed, ed, tmpfile, NULL); 769 perror(ed); 770 exit(1); 771 } 772 waitpid(pid, &stat, 0); 773 sigsetmask(omask); 774 if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0) 775 return (0); 776 return (1); 777} 778 779/* 780 * Convert a quotause list to an ASCII file. 781 */ 782int 783writeprivs(quplist, outfd, name, quotatype) 784 struct quotause *quplist; 785 int outfd; 786 char *name; 787 int quotatype; 788{ 789 register struct quotause *qup; 790 FILE *fd; 791 792 ftruncate(outfd, 0); 793 lseek(outfd, 0, L_SET); 794 if ((fd = fdopen(dup(outfd), "w")) == NULL) { 795 fprintf(stderr, "edquota: "); 796 perror(tmpfil); 797 exit(1); 798 } 799 fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name); 800 for (qup = quplist; qup; qup = qup->next) { 801#ifdef __APPLE__ 802 fprintf(fd, "%s: %s %qd, limits (soft = %qd, hard = %qd)\n", 803 qup->fsname, "1K blocks in use:", 804 qup->dqblk.dqb_curbytes / 1024, 805 qup->dqblk.dqb_bsoftlimit / 1024, 806 qup->dqblk.dqb_bhardlimit / 1024); 807#else 808 fprintf(fd, "%s: %s %d, limits (soft = %d, hard = %d)\n", 809 qup->fsname, "blocks in use:", 810 dbtob(qup->dqblk.dqb_curblocks) / 1024, 811 dbtob(qup->dqblk.dqb_bsoftlimit) / 1024, 812 dbtob(qup->dqblk.dqb_bhardlimit) / 1024); 813#endif /* __APPLE__ */ 814 815 fprintf(fd, "%s %d, limits (soft = %d, hard = %d)\n", 816 "\tinodes in use:", qup->dqblk.dqb_curinodes, 817 qup->dqblk.dqb_isoftlimit, qup->dqblk.dqb_ihardlimit); 818 } 819 fclose(fd); 820 return (1); 821} 822 823/* 824 * Merge changes to an ASCII file into a quotause list. 825 */ 826int 827readprivs(quplist, infd) 828 struct quotause *quplist; 829 int infd; 830{ 831 register struct quotause *qup; 832 FILE *fd; 833 int cnt; 834 register char *cp; 835 struct dqblk dqblk; 836 char fsp[BUFSIZ], line1[BUFSIZ], line2[BUFSIZ]; 837 838 lseek(infd, 0, L_SET); 839 fd = fdopen(dup(infd), "r"); 840 if (fd == NULL) { 841 fprintf(stderr, "Can't re-read temp file!!\n"); 842 return (0); 843 } 844 /* 845 * Discard title line, then read pairs of lines to process. 846 */ 847 (void) fgets(line1, sizeof (line1), fd); 848 while (fgets(line1, sizeof (line1), fd) != NULL && 849 fgets(line2, sizeof (line2), fd) != NULL) { 850 cp = strstr(line1, ": 1K blocks in use:"); 851 if (cp == NULL) { 852 fprintf(stderr, "%s: bad format for line\n", line1); 853 return 0; 854 } 855 /* Copy up to the template text */ 856 strlcpy(fsp, line1, cp - line1 + 1); 857 /* And point cp right after it. */ 858 cp = line1 + strlen(fsp) + 1; 859#ifdef __APPLE__ 860 /* We expect input to be in 1K blocks */ 861 cnt = sscanf(cp, 862 " 1K blocks in use: %qd, limits (soft = %qd, hard = %qd)", 863 &dqblk.dqb_curbytes, &dqblk.dqb_bsoftlimit, 864 &dqblk.dqb_bhardlimit); 865 if (cnt != 3) { 866 fprintf(stderr, "%s:%s: bad format\n", fsp, cp); 867 return (0); 868 } 869 870 /* convert default 1K blocks to byte count */ 871 dqblk.dqb_curbytes = dqblk.dqb_curbytes * 1024; 872 dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit * 1024; 873 dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit * 1024; 874#else 875 cnt = sscanf(cp, 876 " blocks in use: %d, limits (soft = %d, hard = %d)", 877 &dqblk.dqb_curblocks, &dqblk.dqb_bsoftlimit, 878 &dqblk.dqb_bhardlimit); 879 if (cnt != 3) { 880 fprintf(stderr, "%s:%s: bad format\n", fsp, cp); 881 return (0); 882 } 883 dqblk.dqb_curblocks = btodb(dqblk.dqb_curblocks * 1024); 884 dqblk.dqb_bsoftlimit = btodb(dqblk.dqb_bsoftlimit * 1024); 885 dqblk.dqb_bhardlimit = btodb(dqblk.dqb_bhardlimit * 1024); 886#endif /* __APPLE__ */ 887 888 if ((cp = strtok(line2, "\n")) == NULL) { 889 fprintf(stderr, "%s: %s: bad format\n", fsp, line2); 890 return (0); 891 } 892 cnt = sscanf(cp, 893 "\tinodes in use: %d, limits (soft = %d, hard = %d)", 894 &dqblk.dqb_curinodes, &dqblk.dqb_isoftlimit, 895 &dqblk.dqb_ihardlimit); 896 if (cnt != 3) { 897 fprintf(stderr, "%s: %s: bad format\n", fsp, line2); 898 return (0); 899 } 900 for (qup = quplist; qup; qup = qup->next) { 901 if (strcmp(fsp, qup->fsname)) 902 continue; 903 /* 904 * Cause time limit to be reset when the quota 905 * is next used if previously had no soft limit 906 * or were under it, but now have a soft limit 907 * and are over it. 908 */ 909#ifdef __APPLE__ 910 if (dqblk.dqb_bsoftlimit && 911 qup->dqblk.dqb_curbytes >= dqblk.dqb_bsoftlimit && 912 (qup->dqblk.dqb_bsoftlimit == 0 || 913 qup->dqblk.dqb_curbytes < 914 qup->dqblk.dqb_bsoftlimit)) 915 qup->dqblk.dqb_btime = 0; 916#else 917 if (dqblk.dqb_bsoftlimit && 918 qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit && 919 (qup->dqblk.dqb_bsoftlimit == 0 || 920 qup->dqblk.dqb_curblocks < 921 qup->dqblk.dqb_bsoftlimit)) 922 qup->dqblk.dqb_btime = 0; 923#endif /* __APPLE__ */ 924 if (dqblk.dqb_isoftlimit && 925 qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit && 926 (qup->dqblk.dqb_isoftlimit == 0 || 927 qup->dqblk.dqb_curinodes < 928 qup->dqblk.dqb_isoftlimit)) 929 qup->dqblk.dqb_itime = 0; 930 qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit; 931 qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit; 932 qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit; 933 qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit; 934 qup->flags |= FOUND; 935#ifdef __APPLE__ 936 if (dqblk.dqb_curbytes == qup->dqblk.dqb_curbytes && 937 dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes) 938 break; 939#else 940 if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks && 941 dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes) 942 break; 943#endif /* __APPLE__ */ 944 fprintf(stderr, 945 "%s: cannot change current allocation\n", fsp); 946 break; 947 } 948 } 949 fclose(fd); 950 /* 951 * Disable quotas for any filesystems that have not been found. 952 */ 953 for (qup = quplist; qup; qup = qup->next) { 954 if (qup->flags & FOUND) { 955 qup->flags &= ~FOUND; 956 continue; 957 } 958 qup->dqblk.dqb_bsoftlimit = 0; 959 qup->dqblk.dqb_bhardlimit = 0; 960 qup->dqblk.dqb_isoftlimit = 0; 961 qup->dqblk.dqb_ihardlimit = 0; 962 } 963 return (1); 964} 965 966/* 967 * Convert a quotause list to an ASCII file of grace times. 968 */ 969int 970writetimes(quplist, outfd, quotatype) 971 struct quotause *quplist; 972 int outfd; 973 int quotatype; 974{ 975 register struct quotause *qup; 976 char *cvtstoa(); 977 FILE *fd; 978 979 ftruncate(outfd, 0); 980 lseek(outfd, 0, L_SET); 981 if ((fd = fdopen(dup(outfd), "w")) == NULL) { 982 fprintf(stderr, "edquota: "); 983 perror(tmpfil); 984 exit(1); 985 } 986 fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n"); 987 fprintf(fd, "Grace period before enforcing soft limits for %ss:\n", 988 qfextension[quotatype]); 989 for (qup = quplist; qup; qup = qup->next) { 990 fprintf(fd, "%s: block grace period: %s, ", 991 qup->fsname, cvtstoa(qup->dqblk.dqb_btime)); 992 fprintf(fd, "file grace period: %s\n", 993 cvtstoa(qup->dqblk.dqb_itime)); 994 } 995 fclose(fd); 996 return (1); 997} 998 999/* 1000 * Merge changes of grace times in an ASCII file into a quotause list. 1001 */ 1002int 1003readtimes(quplist, infd) 1004 struct quotause *quplist; 1005 int infd; 1006{ 1007 register struct quotause *qup; 1008 FILE *fd; 1009 int cnt; 1010 register char *cp; 1011 time_t itime, btime, iseconds, bseconds; 1012 char *fsp, bunits[10], iunits[10], line1[BUFSIZ]; 1013 1014 lseek(infd, 0, L_SET); 1015 fd = fdopen(dup(infd), "r"); 1016 if (fd == NULL) { 1017 fprintf(stderr, "Can't re-read temp file!!\n"); 1018 return (0); 1019 } 1020 /* 1021 * Discard two title lines, then read lines to process. 1022 */ 1023 (void) fgets(line1, sizeof (line1), fd); 1024 (void) fgets(line1, sizeof (line1), fd); 1025 while (fgets(line1, sizeof (line1), fd) != NULL) { 1026 if ((fsp = strtok(line1, " \t:")) == NULL) { 1027 fprintf(stderr, "%s: bad format\n", line1); 1028 return (0); 1029 } 1030 if ((cp = strtok((char *)0, "\n")) == NULL) { 1031 fprintf(stderr, "%s: %s: bad format\n", fsp, 1032 &fsp[strlen(fsp) + 1]); 1033 return (0); 1034 } 1035 cnt = sscanf(cp, 1036 " block grace period: %ld %s file grace period: %ld %s", 1037 &btime, bunits, &itime, iunits); 1038 if (cnt != 4) { 1039 fprintf(stderr, "%s:%s: bad format\n", fsp, cp); 1040 return (0); 1041 } 1042 if (cvtatos(btime, bunits, &bseconds) == 0) 1043 return (0); 1044 if (cvtatos(itime, iunits, &iseconds) == 0) 1045 return (0); 1046 for (qup = quplist; qup; qup = qup->next) { 1047 if (strcmp(fsp, qup->fsname)) 1048 continue; 1049 qup->dqblk.dqb_btime = (uint32_t) bseconds; 1050 qup->dqblk.dqb_itime = (uint32_t) iseconds; 1051 qup->flags |= FOUND; 1052 break; 1053 } 1054 } 1055 fclose(fd); 1056 /* 1057 * reset default grace periods for any filesystems 1058 * that have not been found. 1059 */ 1060 for (qup = quplist; qup; qup = qup->next) { 1061 if (qup->flags & FOUND) { 1062 qup->flags &= ~FOUND; 1063 continue; 1064 } 1065 qup->dqblk.dqb_btime = 0; 1066 qup->dqblk.dqb_itime = 0; 1067 } 1068 return (1); 1069} 1070 1071/* 1072 * Convert seconds to ASCII times. 1073 */ 1074char * 1075cvtstoa(time) 1076 time_t time; 1077{ 1078 static char buf[20]; 1079 1080 if (time % (24 * 60 * 60) == 0) { 1081 time /= 24 * 60 * 60; 1082 snprintf(buf, sizeof(buf), "%d day%s", (int)time, time == 1 ? "" : "s"); 1083 } else if (time % (60 * 60) == 0) { 1084 time /= 60 * 60; 1085 snprintf(buf, sizeof(buf), "%d hour%s", (int)time, time == 1 ? "" : "s"); 1086 } else if (time % 60 == 0) { 1087 time /= 60; 1088 snprintf(buf, sizeof(buf), "%d minute%s", (int)time, time == 1 ? "" : "s"); 1089 } else 1090 snprintf(buf, sizeof(buf), "%d second%s", (int)time, time == 1 ? "" : "s"); 1091 return (buf); 1092} 1093 1094/* 1095 * Convert ASCII input times to seconds. 1096 */ 1097int 1098cvtatos(time, units, seconds) 1099 time_t time; 1100 char *units; 1101 time_t *seconds; 1102{ 1103 1104 if (bcmp(units, "second", 6) == 0) 1105 *seconds = time; 1106 else if (bcmp(units, "minute", 6) == 0) 1107 *seconds = time * 60; 1108 else if (bcmp(units, "hour", 4) == 0) 1109 *seconds = time * 60 * 60; 1110 else if (bcmp(units, "day", 3) == 0) 1111 *seconds = time * 24 * 60 * 60; 1112 else { 1113 printf("%s: bad units, specify %s\n", units, 1114 "days, hours, minutes, or seconds"); 1115 return (0); 1116 } 1117 return (1); 1118} 1119 1120/* 1121 * Free a list of quotause structures. 1122 */ 1123void 1124freeprivs(quplist) 1125 struct quotause *quplist; 1126{ 1127 register struct quotause *qup, *nextqup; 1128 1129 for (qup = quplist; qup; qup = nextqup) { 1130 nextqup = qup->next; 1131 free(qup); 1132 } 1133} 1134 1135/* 1136 * Check whether a string is completely composed of digits. 1137 */ 1138int 1139alldigits(s) 1140 register char *s; 1141{ 1142 register int c; 1143 1144 c = *s++; 1145 do { 1146 if (!isdigit(c)) 1147 return (0); 1148 } while ((c = *s++)); 1149 return (1); 1150} 1151 1152/* 1153 * Check to see if a particular quota is to be enabled. 1154 */ 1155#ifdef __APPLE__ 1156int 1157hasquota(fst, type, qfnamep) 1158 register struct statfs *fst; 1159 int type; 1160 char **qfnamep; 1161{ 1162 struct stat sb; 1163 static char initname, usrname[100], grpname[100]; 1164 static char buf[BUFSIZ]; 1165 1166 if (!initname) { 1167 snprintf(usrname, sizeof(usrname), "%s%s", qfextension[USRQUOTA], qfname); 1168 snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname); 1169 initname = 1; 1170 } 1171 1172 /* 1173 We only support the default path to the 1174 on disk quota files. 1175 */ 1176 1177 (void)snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, 1178 QUOTAOPSNAME, qfextension[type] ); 1179 if (stat(buf, &sb) != 0) { 1180 /* There appears to be no mount option file */ 1181 return(0); 1182 } 1183 1184 (void) snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, qfname, qfextension[type]); 1185 *qfnamep = buf; 1186 return (1); 1187} 1188#else 1189hasquota(fs, type, qfnamep) 1190 register struct fstab *fs; 1191 int type; 1192 char **qfnamep; 1193{ 1194 register char *opt; 1195 char *cp, *index(), *strtok(); 1196 static char initname, usrname[100], grpname[100]; 1197 static char buf[BUFSIZ]; 1198 1199 if (!initname) { 1200 snprintf(usrname, sizeof(usrname), "%s%s", qfextension[USRQUOTA], qfname); 1201 snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname); 1202 initname = 1; 1203 } 1204 strlcpy(buf, fs->fs_mntops, sizeof(buf)); 1205 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 1206 if (cp = index(opt, '=')) 1207 *cp++ = '\0'; 1208 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 1209 break; 1210 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 1211 break; 1212 } 1213 if (!opt) 1214 return (0); 1215 if (cp) { 1216 *qfnamep = cp; 1217 return (1); 1218 } 1219 (void) snprintf(buf, sizeof(buf), "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 1220 *qfnamep = buf; 1221 return (1); 1222} 1223#endif /* __APPLE */ 1224