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 60/* 61 * Fix up / report on disk quotas & usage 62 */ 63#include <sys/param.h> 64#include <sys/stat.h> 65#include <sys/queue.h> 66#ifdef __APPLE__ 67#include <sys/mount.h> 68#endif /* __APPLE__ */ 69 70#include <sys/quota.h> 71 72#include <fcntl.h> 73#include <fstab.h> 74#include <pwd.h> 75#include <grp.h> 76#include <errno.h> 77#include <unistd.h> 78#include <stdio.h> 79#include <stdlib.h> 80#include <string.h> 81#include <err.h> 82 83#ifdef __APPLE__ 84#include <libkern/OSByteOrder.h> 85#endif /* __APPLE__ */ 86 87#include "quotacheck.h" 88 89char *qfname = QUOTAFILENAME; 90char *qfextension[] = INITQFNAMES; 91char *quotagroup = QUOTAGROUP; 92 93#ifdef __APPLE__ 94u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS; 95#endif /* __APPLE__ */ 96 97 98#define FUHASH 1024 /* must be power of two */ 99struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 100 101int aflag; /* all file systems */ 102int gflag; /* check group quotas */ 103int uflag; /* check user quotas */ 104int vflag; /* verbose */ 105 106#ifdef __APPLE__ 107int maxentries; /* maximum entries in disk quota file */ 108#else 109u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 110#endif /* __APPLE__ */ 111 112 113#ifdef __APPLE__ 114char * blockcheck(char *); 115int chkquota(char *, char *, char *, struct quotaname *); 116int getquotagid(void); 117int hasquota(struct statfs *, int, char **); 118struct fileusage * 119 lookup(u_long, int); 120void * needchk(struct statfs *); 121int oneof(char *, char*[], int); 122int qfinsert(FILE *, struct dqblk *, int, int); 123int qfmaxentries(char *, int); 124void usage(void); 125void dqbuftohost(struct dqblk *dqbp); 126 127#else 128struct fileusage * 129 addid __P((uid_t, int, char *)); 130char *blockcheck __P((char *)); 131int chkquota __P((char *, char *, struct quotaname *)); 132int getquotagid __P((void)); 133int hasquota __P((struct fstab *, int, char **)); 134struct fileusage * 135 lookup __P((u_long, int)); 136void *needchk __P((struct fstab *)); 137int oneof __P((char *, char*[], int)); 138int update __P((char *, char *, int)); 139void usage __P((void)); 140#endif /* __APPLE__ */ 141 142 143int 144main(argc, argv) 145 int argc; 146 char *argv[]; 147{ 148#ifndef __APPLE__ 149 register struct fstab *fs; 150 register struct passwd *pw; 151 register struct group *gr; 152#endif /* !__APPLE__ */ 153 struct quotaname *auxdata; 154 int i, argnum, maxrun, errs; 155 long done = 0; 156 char ch, *name; 157 158#ifdef __APPLE__ 159 int nfst; 160 struct statfs *fst; 161#endif /* __APPLE__ */ 162 163 errs = maxrun = 0; 164 while ((ch = getopt(argc, argv, "aguvl:")) != EOF) { 165 switch(ch) { 166 case 'a': 167 aflag++; 168 break; 169 case 'g': 170 gflag++; 171 break; 172 case 'u': 173 uflag++; 174 break; 175 case 'v': 176 vflag++; 177 break; 178 case 'l': 179 maxrun = atoi(optarg); 180 break; 181 default: 182 usage(); 183 } 184 } 185 argc -= optind; 186 argv += optind; 187 if ((argc == 0 && !aflag) || (argc > 0 && aflag)) 188 usage(); 189 if (!gflag && !uflag) { 190 gflag++; 191 uflag++; 192 } 193 194#ifdef __APPLE__ 195 nfst = getmntinfo(&fst, MNT_WAIT); 196 if (nfst==0) { 197 fprintf(stderr, "quotacheck: no mounted filesystems\n"); 198 exit(1); 199 } 200 201 for (i=0; i<nfst; i++) { 202 if(strcmp(fst[i].f_fstypename, "hfs")) { 203 continue; 204 } 205 206 if (aflag) { 207 if ((auxdata = needchk(&fst[i])) && 208 (name = blockcheck(fst[i].f_mntfromname))) { 209 errs += chkquota(fst[i].f_fstypename, name, fst[i].f_mntonname, auxdata); 210 } 211 212 if (i+1 == nfst) 213 exit (errs); 214 else 215 continue; 216 } 217 218 if (((argnum = oneof(fst[i].f_mntonname, argv, argc)) >= 0 || 219 (argnum = oneof(fst[i].f_mntfromname, argv, argc)) >= 0) && 220 (auxdata = needchk(&fst[i])) && 221 (name = blockcheck(fst[i].f_mntfromname))) { 222 done |= 1 << argnum; 223 errs += chkquota(fst[i].f_fstypename, name, fst[i].f_mntonname, auxdata); 224 } 225 } /* end for loop */ 226 227 for (i = 0; i < argc; i++) 228 if ((done & (1 << i)) == 0) 229 fprintf(stderr, "%s not identified as a quota filesystem\n", 230 argv[i]); 231 232 exit(errs); 233#else 234 if (gflag) { 235 setgrent(); 236 while ((gr = getgrent()) != 0) 237 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 238 endgrent(); 239 } 240 if (uflag) { 241 setpwent(); 242 while ((pw = getpwent()) != 0) 243 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); 244 endpwent(); 245 } 246 if (aflag) 247 exit(checkfstab(1, maxrun, needchk, chkquota)); 248 if (setfsent() == 0) 249 err(1, "%s: can't open", FSTAB); 250 while ((fs = getfsent()) != NULL) { 251 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 252 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && 253 (auxdata = needchk(fs)) && 254 (name = blockcheck(fs->fs_spec))) { 255 done |= 1 << argnum; 256 errs += chkquota(name, fs->fs_file, auxdata); 257 } 258 } 259 endfsent(); 260 for (i = 0; i < argc; i++) 261 if ((done & (1 << i)) == 0) 262 fprintf(stderr, "%s not found in %s\n", 263 argv[i], FSTAB); 264 exit(errs); 265#endif /* __APPLE__ */ 266} 267 268void 269usage() 270{ 271 (void)fprintf(stderr, "usage:\t%s\n\t%s\n", 272 "quotacheck -a [-guv]", 273 "quotacheck [-guv] filesys ..."); 274 exit(1); 275} 276 277#ifdef __APPLE__ 278void * 279needchk(fst) 280 struct statfs *fst; 281{ 282 register struct quotaname *qnp; 283 char *qfnp; 284 285 if(strcmp(fst->f_fstypename, "hfs")) { 286 return(NULL); 287 } 288 if(fst->f_flags & MNT_RDONLY) 289 return (NULL); 290 if ((qnp = malloc(sizeof(*qnp))) == NULL) 291 err(1, "%s", strerror(errno)); 292 qnp->flags = 0; 293 if (gflag && hasquota(fst, GRPQUOTA, &qfnp)) { 294 strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname)); 295 qnp->flags |= HASGRP; 296 } 297 if (uflag && hasquota(fst, USRQUOTA, &qfnp)) { 298 strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname)); 299 qnp->flags |= HASUSR; 300 } 301 if (qnp->flags) 302 return (qnp); 303 free(qnp); 304 return (NULL); 305} 306#else 307void * 308needchk(fs) 309 register struct fstab *fs; 310{ 311 register struct quotaname *qnp; 312 char *qfnp; 313 314 if (strcmp(fs->fs_type, FSTAB_RW)) 315 return (NULL); 316 317 if ((qnp = malloc(sizeof(*qnp))) == NULL) 318 err(1, "%s", strerror(errno)); 319 qnp->flags = 0; 320 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 321 strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname)); 322 qnp->flags |= HASGRP; 323 } 324 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 325 strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname)); 326 qnp->flags |= HASUSR; 327 } 328 if (qnp->flags) 329 return (qnp); 330 free(qnp); 331 return (NULL); 332} 333#endif /* __APPLE__ */ 334 335/* 336 * Scan the specified filesystem to check quota(s) present on it. 337 */ 338int 339chkquota(fstype, fsname, mntpt, qnp) 340 char *fstype, *fsname, *mntpt; 341 register struct quotaname *qnp; 342{ 343 int errs = 1; 344 345 if (vflag) { 346 fprintf(stdout, "*** Checking "); 347 if (qnp->flags & HASUSR) 348 fprintf(stdout, "%s%s", qfextension[USRQUOTA], 349 (qnp->flags & HASGRP) ? " and " : ""); 350 if (qnp->flags & HASGRP) 351 fprintf(stdout, "%s", qfextension[GRPQUOTA]); 352 fprintf(stdout, " quotas for %s (%s)\n", fsname, mntpt); 353 } 354 355 if(strcmp(fstype, "hfs") == 0) 356 errs = chkquota_hfs(fsname, mntpt, qnp); 357 358 return (errs); 359} 360 361/* 362 * Update a specified quota file. 363 */ 364#ifdef __APPLE__ 365int 366update(fsname, quotafile, type) 367 char *fsname, *quotafile; 368 register int type; 369{ 370 register struct fileusage *fup; 371 register FILE *qfi, *qfo; 372 register u_long i; 373 struct dqblk dqbuf; 374 struct dqfilehdr dqhdr = {0}; 375 int m, shift; 376 int idcnt = 0; 377 static int warned = 0; 378 static struct dqblk zerodqbuf; 379 static struct fileusage zerofileusage; 380 381 if ((qfo = fopen(quotafile, "r+")) == NULL) { 382 if (errno == ENOENT) 383 qfo = fopen(quotafile, "w+"); 384 if (qfo) { 385 fprintf(stderr, 386 "quotacheck: creating quota file %s\n", quotafile); 387#define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 388 (void) fchown(fileno(qfo), getuid(), getquotagid()); 389 (void) fchmod(fileno(qfo), MODE); 390 } else { 391 fprintf(stderr, 392 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 393 return (1); 394 } 395 } 396 if ((qfi = fopen(quotafile, "r")) == NULL) { 397 fprintf(stderr, 398 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 399 (void) fclose(qfo); 400 return (1); 401 } 402 if (quotactl(fsname, QCMD(Q_SYNC, type), 0, 0) < 0 && 403 errno == ENOTSUP && !warned && vflag) { 404 warned++; 405 fprintf(stdout, "*** Warning: %s\n", 406 "Quotas are not compiled into this kernel"); 407 } 408 409 /* Read in the quota file header. */ 410 if (fread((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qfi) > 0) { 411 /* Check for reverse endian file. */ 412 if (OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type] && 413 OSSwapInt32(dqhdr.dqh_magic) == quotamagic[type]) { 414 fprintf(stderr, 415 "quotacheck: %s: not in big endian byte order\n", 416 quotafile); 417 (void) fclose(qfo); 418 (void) fclose(qfi); 419 return (1); 420 } 421 /* Sanity check the quota file header. */ 422 if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) || 423 (OSSwapBigToHostInt32(dqhdr.dqh_version) > QF_VERSION) || 424 (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) { 425 fprintf(stderr, 426 "quotacheck: %s: not a valid quota file\n", 427 quotafile); 428 (void) fclose(qfo); 429 (void) fclose(qfi); 430 return (1); 431 } 432 m = OSSwapBigToHostInt32(dqhdr.dqh_maxentries); 433 } else /* empty file */ { 434 if (maxentries) 435 m = maxentries; 436 else 437 m = qfmaxentries(fsname, type); 438 439 ftruncate(fileno(qfo), (off_t)((m + 1) * sizeof(struct dqblk))); 440 441 /* Initialize file header in big endian. */ 442 dqhdr.dqh_magic = OSSwapHostToBigInt32(quotamagic[type]); 443 dqhdr.dqh_version = OSSwapHostToBigConstInt32(QF_VERSION); 444 dqhdr.dqh_maxentries = OSSwapHostToBigInt32(m); 445 dqhdr.dqh_btime = OSSwapHostToBigConstInt32(MAX_DQ_TIME); 446 dqhdr.dqh_itime = OSSwapHostToBigConstInt32(MAX_IQ_TIME); 447 memmove(dqhdr.dqh_string, QF_STRING_TAG, strlen(QF_STRING_TAG)); 448 goto orphans; /* just insert all new records */ 449 } 450 451 /* Examine all the entries in the quota file. */ 452 for (i = 0; i < m; i++) { 453 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) { 454 fprintf(stderr, 455 "quotacheck: problem reading at index %ld\n", i); 456 continue; 457 } 458 if (dqbuf.dqb_id == 0) 459 continue; 460 461 ++idcnt; 462 if ((fup = lookup(OSSwapBigToHostInt32(dqbuf.dqb_id), type)) == 0) 463 fup = &zerofileusage; 464 else 465 fup->fu_checked = 1; 466 467 if (OSSwapBigToHostInt32(dqbuf.dqb_curinodes) == fup->fu_curinodes && 468 OSSwapBigToHostInt64(dqbuf.dqb_curbytes) == fup->fu_curbytes) { 469 fup->fu_curinodes = 0; 470 fup->fu_curbytes = 0; 471 continue; 472 } 473 if (vflag) { 474 if (aflag) 475 fprintf(stdout, "%s: ", fsname); 476 fprintf(stdout, "%-12s fixed:", fup->fu_name); 477 if (OSSwapBigToHostInt32(dqbuf.dqb_curinodes) != fup->fu_curinodes) 478 fprintf(stdout, "\tinodes %u -> %u", 479 OSSwapBigToHostInt32(dqbuf.dqb_curinodes), fup->fu_curinodes); 480 if (OSSwapBigToHostInt64(dqbuf.dqb_curbytes) != fup->fu_curbytes) 481 fprintf(stdout, "\t1K blocks %qd -> %qd", 482 (OSSwapBigToHostInt64(dqbuf.dqb_curbytes)/1024), (fup->fu_curbytes/1024)); 483 fprintf(stdout, "\n"); 484 } 485 /* 486 * Reset time limit if have a soft limit and were 487 * previously under it, but are now over it. 488 */ 489 if (dqbuf.dqb_bsoftlimit != 0 && 490 OSSwapBigToHostInt64(dqbuf.dqb_curbytes) < OSSwapBigToHostInt64(dqbuf.dqb_bsoftlimit) && 491 fup->fu_curbytes >= OSSwapBigToHostInt64(dqbuf.dqb_bsoftlimit)) 492 dqbuf.dqb_btime = 0; 493 if (dqbuf.dqb_isoftlimit != 0 && 494 OSSwapBigToHostInt32(dqbuf.dqb_curinodes) < OSSwapBigToHostInt32(dqbuf.dqb_isoftlimit) && 495 fup->fu_curinodes >= OSSwapBigToHostInt32(dqbuf.dqb_isoftlimit)) 496 dqbuf.dqb_itime = 0; 497 dqbuf.dqb_curinodes = OSSwapHostToBigInt32(fup->fu_curinodes); 498 dqbuf.dqb_curbytes = OSSwapHostToBigInt64(fup->fu_curbytes); 499 500 /* Write dqblk in big endian. */ 501 fseek(qfo, dqoffset(i), SEEK_SET); 502 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 503 504 dqbuftohost(&dqbuf); 505 (void) quotactl(fsname, QCMD(Q_SETUSE, type), dqbuf.dqb_id, 506 (caddr_t)&dqbuf); 507 fup->fu_curinodes = 0; 508 fup->fu_curbytes = 0; 509 } 510orphans: 511 /* Look for any fileusage orphans */ 512 513 shift = dqhashshift(m); 514 for (i = 0; i < FUHASH; ++i) { 515 for (fup = fuhead[type][i]; fup != 0; fup = fup->fu_next) { 516 if (fup->fu_checked || fup->fu_id == 0) 517 continue; 518 if (vflag) { 519 if (aflag) 520 fprintf(stdout, "%s: ", fsname); 521 fprintf(stdout, 522 "%-12s added:\tinodes %u\t1K blocks %qd\n", 523 fup->fu_name, fup->fu_curinodes, 524 (fup->fu_curbytes/1024)); 525 } 526 /* Initialize dqbuf in big endian. */ 527 dqbuf = zerodqbuf; 528 dqbuf.dqb_id = OSSwapHostToBigInt32(fup->fu_id); 529 dqbuf.dqb_curinodes = OSSwapHostToBigInt32(fup->fu_curinodes); 530 dqbuf.dqb_curbytes = OSSwapHostToBigInt64(fup->fu_curbytes); 531 /* insert this dqb */ 532 if (qfinsert(qfo, &dqbuf, m, shift)) { 533 i = FUHASH; 534 break; 535 } 536 537 dqbuftohost(&dqbuf); 538 (void) quotactl(fsname, QCMD(Q_SETUSE, type), 539 dqbuf.dqb_id, (caddr_t)&dqbuf); 540 ++idcnt; 541 } 542 } 543 544 /* Write the quota file header */ 545 dqhdr.dqh_entrycnt = OSSwapHostToBigInt32(idcnt); 546 fseek(qfo, (long)0, SEEK_SET); 547 fwrite((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qfo); 548 549 fclose(qfi); 550 fflush(qfo); 551 fclose(qfo); 552 return (0); 553} 554 555/* Convert a dqblk to host native byte order. */ 556void 557dqbuftohost(struct dqblk *dqbp) 558{ 559 dqbp->dqb_bhardlimit = OSSwapBigToHostInt64(dqbp->dqb_bhardlimit); 560 dqbp->dqb_bsoftlimit = OSSwapBigToHostInt64(dqbp->dqb_bsoftlimit); 561 dqbp->dqb_curbytes = OSSwapBigToHostInt64(dqbp->dqb_curbytes); 562 dqbp->dqb_ihardlimit = OSSwapBigToHostInt32(dqbp->dqb_ihardlimit); 563 dqbp->dqb_isoftlimit = OSSwapBigToHostInt32(dqbp->dqb_isoftlimit); 564 dqbp->dqb_curinodes = OSSwapBigToHostInt32(dqbp->dqb_curinodes); 565 dqbp->dqb_btime = OSSwapBigToHostInt32(dqbp->dqb_btime); 566 dqbp->dqb_itime = OSSwapBigToHostInt32(dqbp->dqb_itime); 567 dqbp->dqb_id = OSSwapBigToHostInt32(dqbp->dqb_id); 568} 569 570#else 571int 572update(fsname, quotafile, type) 573 char *fsname, *quotafile; 574 register int type; 575{ 576 register struct fileusage *fup; 577 register FILE *qfi, *qfo; 578 register u_long id, lastid; 579 struct dqblk dqbuf; 580 static int warned = 0; 581 static struct dqblk zerodqbuf; 582 static struct fileusage zerofileusage; 583 584 if ((qfo = fopen(quotafile, "r+")) == NULL) { 585 if (errno == ENOENT) 586 qfo = fopen(quotafile, "w+"); 587 if (qfo) { 588 (void) fprintf(stderr, 589 "quotacheck: creating quota file %s\n", quotafile); 590#define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 591 (void) fchown(fileno(qfo), getuid(), getquotagid()); 592 (void) fchmod(fileno(qfo), MODE); 593 } else { 594 (void) fprintf(stderr, 595 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 596 return (1); 597 } 598 } 599 if ((qfi = fopen(quotafile, "r")) == NULL) { 600 (void) fprintf(stderr, 601 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 602 (void) fclose(qfo); 603 return (1); 604 } 605 if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 && 606 errno == ENOTSUP && !warned && vflag) { 607 warned++; 608 (void)printf("*** Warning: %s\n", 609 "Quotas are not compiled into this kernel"); 610 } 611 for (lastid = highid[type], id = 0; id <= lastid; id++) { 612 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 613 dqbuf = zerodqbuf; 614 if ((fup = lookup(id, type)) == 0) 615 fup = &zerofileusage; 616 if (dqbuf.dqb_curinodes == fup->fu_curinodes && 617 dqbuf.dqb_curblocks == fup->fu_curblocks) { 618 fup->fu_curinodes = 0; 619 fup->fu_curblocks = 0; 620 fseek(qfo, (long)sizeof(struct dqblk), 1); 621 continue; 622 } 623 if (vflag) { 624 if (aflag) 625 printf("%s: ", fsname); 626 printf("%-8s fixed:", fup->fu_name); 627 if (dqbuf.dqb_curinodes != fup->fu_curinodes) 628 (void)printf("\tinodes %d -> %d", 629 dqbuf.dqb_curinodes, fup->fu_curinodes); 630 if (dqbuf.dqb_curblocks != fup->fu_curblocks) 631 (void)printf("\tblocks %d -> %d", 632 dqbuf.dqb_curblocks, fup->fu_curblocks); 633 (void)printf("\n"); 634 } 635 /* 636 * Reset time limit if have a soft limit and were 637 * previously under it, but are now over it. 638 */ 639 if (dqbuf.dqb_bsoftlimit && 640 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 641 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 642 dqbuf.dqb_btime = 0; 643 if (dqbuf.dqb_isoftlimit && 644 dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 645 fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 646 dqbuf.dqb_itime = 0; 647 dqbuf.dqb_curinodes = fup->fu_curinodes; 648 dqbuf.dqb_curblocks = fup->fu_curblocks; 649 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 650 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 651 (caddr_t)&dqbuf); 652 fup->fu_curinodes = 0; 653 fup->fu_curblocks = 0; 654 } 655 fclose(qfi); 656 fflush(qfo); 657 ftruncate(fileno(qfo), 658 (off_t)((highid[type] + 1) * sizeof(struct dqblk))); 659 fclose(qfo); 660 return (0); 661} 662#endif /* __APPLE__ */ 663 664#ifdef __APPLE__ 665/* 666 * Insert an entry into a quota file. 667 * 668 * The dqblk pointed to by dqbp is in big endian. 669 */ 670int 671qfinsert(file, dqbp, maxentries, shift) 672 FILE *file; 673 struct dqblk *dqbp; 674 int maxentries, shift; 675{ 676 struct dqblk dqbuf; 677 int i, skip, last; 678 u_int32_t mask; 679 u_int32_t id; 680 off_t offset; 681 682 id = OSSwapBigToHostInt32(dqbp->dqb_id); 683 if (id == 0) 684 return (0); 685 mask = maxentries - 1; 686 i = dqhash1(id, dqhashshift(maxentries), mask); 687 skip = dqhash2(id, mask); 688 689 for (last = (i + (maxentries-1) * skip) & mask; 690 i != last; 691 i = (i + skip) & mask) { 692 offset = dqoffset(i); 693 fseek(file, (long)offset, SEEK_SET); 694 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, file) == 0) { 695 fprintf(stderr, "quotacheck: read error at index %d\n", i); 696 return (EIO); 697 } 698 /* 699 * Stop when an empty entry is found 700 * or we encounter a matching id. 701 */ 702 if (dqbuf.dqb_id == 0 || dqbuf.dqb_id == dqbp->dqb_id) { 703 dqbuf = *dqbp; 704 fseek(file, (long)offset, SEEK_SET); 705 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, file); 706 return (0); 707 } 708 } 709 fprintf(stderr, "quotacheck: exceeded maximum entries (%d)\n", maxentries); 710 return (ENOSPC); 711} 712 713#define ONEGIGABYTE (1024*1024*1024) 714 715/* 716 * Calculate the size of the hash table from the size of 717 * the file system. The open addressing hashing used on 718 * the quota file assumes that this table will never be 719 * more than 90% full. 720 */ 721int 722qfmaxentries(mntpt, type) 723 char *mntpt; 724 int type; 725{ 726 struct statfs fs_stat; 727 u_int64_t fs_size; 728 int max = 0; 729 730 if (statfs(mntpt, &fs_stat)) { 731 fprintf(stderr, "quotacheck: %s: %s\n", 732 mntpt, strerror(errno)); 733 return (0); 734 } 735 fs_size = (u_int64_t)fs_stat.f_blocks * (u_int64_t)fs_stat.f_bsize; 736 737 if (type == USRQUOTA) { 738 max = QF_USERS_PER_GB * (fs_size / ONEGIGABYTE); 739 740 if (max < QF_MIN_USERS) 741 max = QF_MIN_USERS; 742 else if (max > QF_MAX_USERS) 743 max = QF_MAX_USERS; 744 } else if (type == GRPQUOTA) { 745 max = QF_GROUPS_PER_GB * (fs_size / ONEGIGABYTE); 746 747 if (max < QF_MIN_GROUPS) 748 max = QF_MIN_GROUPS; 749 else if (max > QF_MAX_GROUPS) 750 max = QF_MAX_GROUPS; 751 } 752 /* Round up to a power of 2 */ 753 if (max && !powerof2(max)) { 754 int x = max; 755 max = 4; 756 while (x>>1 != 1) { 757 x = x >> 1; 758 max = max << 1; 759 } 760 } 761 return (max); 762} 763#endif /* __APPLE__ */ 764 765/* 766 * Check to see if target appears in list of size cnt. 767 */ 768int 769oneof(target, list, cnt) 770 register char *target, *list[]; 771 int cnt; 772{ 773 register int i; 774 775 for (i = 0; i < cnt; i++) 776 if (strcmp(target, list[i]) == 0) 777 return (i); 778 return (-1); 779} 780 781/* 782 * Determine the group identifier for quota files. 783 */ 784int 785getquotagid() 786{ 787 struct group *gr; 788 789 if ((gr = getgrnam(quotagroup))) 790 return (gr->gr_gid); 791 return (-1); 792} 793 794/* 795 * Check to see if a particular quota is to be enabled. 796 */ 797#ifdef __APPLE__ 798int 799hasquota(fst, type, qfnamep) 800 struct statfs *fst; 801 int type; 802 char **qfnamep; 803{ 804 struct stat sb; 805 static char initname, usrname[100], grpname[100]; 806 static char buf[BUFSIZ]; 807 808 if (!initname) { 809 (void)snprintf(usrname, sizeof(usrname), 810 "%s%s", qfextension[USRQUOTA], qfname); 811 (void)snprintf(grpname, sizeof(grpname), 812 "%s%s", qfextension[GRPQUOTA], qfname); 813 initname = 1; 814 } 815 816 /* 817 We only support the default path to the 818 on disk quota files. 819 */ 820 821 (void)snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, 822 QUOTAOPSNAME, qfextension[type] ); 823 if (stat(buf, &sb) != 0) { 824 /* There appears to be no mount option file */ 825 return(0); 826 } 827 828 (void)snprintf(buf, sizeof(buf), 829 "%s/%s.%s", fst->f_mntonname, qfname, qfextension[type]); 830 *qfnamep = buf; 831 832 return (1); 833} 834#else 835int 836hasquota(fs, type, qfnamep) 837 register struct fstab *fs; 838 int type; 839 char **qfnamep; 840{ 841 register char *opt; 842 char *cp; 843 static char initname, usrname[100], grpname[100]; 844 static char buf[BUFSIZ]; 845 846 if (!initname) { 847 (void)snprintf(usrname, sizeof(usrname), 848 "%s%s", qfextension[USRQUOTA], qfname); 849 (void)snprintf(grpname, sizeof(grpname), 850 "%s%s", qfextension[GRPQUOTA], qfname); 851 initname = 1; 852 } 853 strlcpy(buf, fs->fs_mntops, sizeof(buf)); 854 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 855 if (cp = strchr(opt, '=')) 856 *cp++ = '\0'; 857 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 858 break; 859 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 860 break; 861 } 862 if (!opt) 863 return (0); 864 if (cp) 865 *qfnamep = cp; 866 else { 867 (void)snprintf(buf, sizeof(buf), 868 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 869 *qfnamep = buf; 870 } 871 return (1); 872} 873#endif /* __APPLE__ */ 874 875/* 876 * Routines to manage the file usage table. 877 * 878 * Lookup an id of a specific type. 879 */ 880struct fileusage * 881lookup(id, type) 882 u_long id; 883 int type; 884{ 885 register struct fileusage *fup; 886 887 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 888 if (fup->fu_id == id) 889 return (fup); 890 return (NULL); 891} 892 893/* 894 * Add a new file usage id if it does not already exist. 895 */ 896#ifdef __APPLE__ 897struct fileusage * 898addid(id, type) 899 uid_t id; 900 int type; 901{ 902 struct fileusage *fup, **fhp; 903 struct passwd *pw; 904 struct group *gr; 905 char *name; 906 size_t len; 907 908 if ((fup = lookup(id, type))) 909 return (fup); 910 911 name = NULL; 912 len = 10; 913 switch (type) { 914 case USRQUOTA: 915 if ((pw = getpwuid(id)) != 0) { 916 name = pw->pw_name; 917 len = strlen(name); 918 } 919 break; 920 case GRPQUOTA: 921 if ((gr = getgrgid(id)) != 0) { 922 name = gr->gr_name; 923 len = strlen(name); 924 } 925 break; 926 } 927 928 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 929 err(1, "%s", strerror(errno)); 930 fhp = &fuhead[type][id & (FUHASH - 1)]; 931 fup->fu_next = *fhp; 932 *fhp = fup; 933 fup->fu_id = id; 934 if (name) 935 memmove(fup->fu_name, name, len + 1); 936 else 937 (void)sprintf(fup->fu_name, "%u", (unsigned int)id); 938 939 return (fup); 940} 941#else 942struct fileusage * 943addid(id, type, name) 944 u_long id; 945 int type; 946 char *name; 947{ 948 struct fileusage *fup, **fhp; 949 int len; 950 951 if (fup = lookup(id, type)) 952 return (fup); 953 if (name) 954 len = strlen(name); 955 else 956 len = 10; 957 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 958 err(1, "%s", strerror(errno)); 959 fhp = &fuhead[type][id & (FUHASH - 1)]; 960 fup->fu_next = *fhp; 961 *fhp = fup; 962 fup->fu_id = id; 963 if (id > highid[type]) 964 highid[type] = id; 965 if (name) 966 memmove(fup->fu_name, name, len + 1); 967 else 968 (void)sprintf(fup->fu_name, "%u", id); 969 return (fup); 970} 971#endif /* __APPLE__ */ 972 973