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[] = "@(#)repquota.c 8.2 (Berkeley) 11/22/94"; 69#endif /* not lint */ 70 71/* 72 * Quota report 73 */ 74#include <sys/param.h> 75#include <sys/stat.h> 76#include <sys/queue.h> 77#include <sys/quota.h> 78 79#ifdef __APPLE__ 80#include <sys/mount.h> 81#endif /* __APPLE__ */ 82#include <errno.h> 83#include <fstab.h> 84#include <grp.h> 85#include <pwd.h> 86#include <stdio.h> 87#include <stdlib.h> 88#include <string.h> 89#include <unistd.h> 90#ifdef __APPLE__ 91#include <libkern/OSByteOrder.h> 92#endif /* __APPLE__ */ 93 94char *qfname = QUOTAFILENAME; 95char *qfextension[] = INITQFNAMES; 96#ifdef __APPLE__ 97u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS; 98#endif /* __APPLE__ */ 99 100#ifndef __APPLE__ 101struct fileusage { 102 struct fileusage *fu_next; 103 struct dqblk fu_dqblk; 104 u_long fu_id; 105 char fu_name[1]; 106 /* actually bigger */ 107}; 108struct fileusage * addid(u_long, int); 109struct fileusage * lookup(u_long, int); 110 111#define FUHASH 1024 /* must be power of two */ 112struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 113u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 114#endif /* NOT __APPLE__ */ 115 116int vflag; /* verbose */ 117int aflag; /* all file systems */ 118 119int hasquota(struct statfs *, int, char **); 120int repquota(struct statfs *, int, char *); 121int oneof(char *, char **, int); 122void usage(void); 123 124int 125main(argc, argv) 126 int argc; 127 char **argv; 128{ 129#ifndef __APPLE__ 130 register struct fstab *fs; 131 register struct passwd *pw; 132 register struct group *gr; 133#endif /* __APPLE__ */ 134 int gflag = 0, uflag = 0, errs = 0; 135 long i, argnum, done = 0; 136#ifdef __APPLE__ 137 int nfst; 138 struct statfs *fst; 139#endif /* __APPLE__ */ 140 char ch, *qfnp; 141 142 while ((ch = getopt(argc, argv, "aguv")) != EOF) { 143 switch(ch) { 144 case 'a': 145 aflag++; 146 break; 147 case 'g': 148 gflag++; 149 break; 150 case 'u': 151 uflag++; 152 break; 153 case 'v': 154 vflag++; 155 break; 156 default: 157 usage(); 158 } 159 } 160 argc -= optind; 161 argv += optind; 162 if (argc == 0 && !aflag) 163 usage(); 164 if (!gflag && !uflag) { 165 if (aflag) 166 gflag++; 167 uflag++; 168 } 169 170#ifdef __APPLE__ 171 nfst = getmntinfo(&fst, MNT_WAIT); 172 if (nfst==0) { 173 fprintf(stderr, "repquota: no filesystems mounted"); 174 return(1); 175 } 176 177 for (i=0; i<nfst; i++) { 178 if(strcmp(fst[i].f_fstypename, "hfs")) { 179 continue; 180 } 181 if (aflag) { 182 if (gflag && hasquota(&fst[i], GRPQUOTA, &qfnp)) 183 errs += repquota(&fst[i], GRPQUOTA, qfnp); 184 if (uflag && hasquota(&fst[i], USRQUOTA, &qfnp)) 185 errs += repquota(&fst[i], USRQUOTA, qfnp); 186 continue; 187 } 188 if ((argnum = oneof(fst[i].f_mntonname, argv, argc)) >= 0 || 189 (argnum = oneof(fst[i].f_mntfromname, argv, argc)) >= 0) { 190 done |= 1 << argnum; 191 if (gflag && hasquota(&fst[i], GRPQUOTA, &qfnp)) 192 errs += repquota(&fst[i], GRPQUOTA, qfnp); 193 if (uflag && hasquota(&fst[i], USRQUOTA, &qfnp)) 194 errs += repquota(&fst[i], USRQUOTA, qfnp); 195 } 196 } 197#else 198 if (gflag) { 199 setgrent(); 200 while ((gr = getgrent()) != 0) 201 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 202 endgrent(); 203 } 204 if (uflag) { 205 setpwent(); 206 while ((pw = getpwent()) != 0) 207 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); 208 endpwent(); 209 } 210 211 setfsent(); 212 while ((fs = getfsent()) != NULL) { 213 if (strcmp(fs->fs_vfstype, "ufs")) 214 continue; 215 if (aflag) { 216 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) 217 errs += repquota(fs, GRPQUOTA, qfnp); 218 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) 219 errs += repquota(fs, USRQUOTA, qfnp); 220 continue; 221 } 222 if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 223 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) { 224 done |= 1 << argnum; 225 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) 226 errs += repquota(fs, GRPQUOTA, qfnp); 227 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) 228 errs += repquota(fs, USRQUOTA, qfnp); 229 } 230 } 231 endfsent(); 232#endif /* __APPLE */ 233 for (i = 0; i < argc; i++) 234 if ((done & (1 << i)) == 0) 235 fprintf(stderr, "%s not found in fstab\n", argv[i]); 236 exit(errs); 237} 238 239void 240usage() 241{ 242 fprintf(stderr, "Usage:\n\t%s\n\t%s\n", 243 "repquota [-v] [-g] [-u] -a", 244 "repquota [-v] [-g] [-u] filesys ..."); 245 exit(1); 246} 247 248#ifdef __APPLE__ 249int 250repquota(fst, type, qfpathname) 251 struct statfs *fst; 252 int type; 253 char *qfpathname; 254{ 255 FILE *qf; 256 uid_t id; 257 struct dqblk dqbuf; 258 char *timeprt(); 259 char *name; 260 struct dqfilehdr dqhdr; 261 static int warned = 0; 262 static int multiple = 0; 263 extern int errno; 264 int i; 265 struct passwd *pw; 266 struct group *gr; 267 int maxentries; 268 u_int64_t bsoftlimit; 269 u_int32_t isoftlimit; 270 u_int64_t curbytes; 271 u_int32_t curinodes; 272 273 274 if (quotactl(fst->f_mntonname, QCMD(Q_SYNC, type), 0, 0) < 0 && 275 errno == ENOTSUP && !warned && vflag) { 276 warned++; 277 fprintf(stdout, 278 "*** Warning: Quotas are not compiled into this kernel\n"); 279 } 280 if (multiple++) 281 printf("\n"); 282 if (vflag) 283 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n", 284 qfextension[type], fst->f_mntonname, fst->f_mntfromname); 285 if ((qf = fopen(qfpathname, "r")) == NULL) { 286 perror(qfpathname); 287 return (1); 288 } 289 290 /* Read in the quota file header. */ 291 if (fread((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qf) > 0) { 292 /* Check for non big endian file. */ 293 if (OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type] && 294 OSSwapInt32(dqhdr.dqh_magic) == quotamagic[type]) { 295 (void) fprintf(stderr, 296 "*** Error: %s: not in big endian byte order\n", qfpathname); 297 (void) fclose(qf); 298 return (1); 299 } 300 /* Sanity check the quota file header. */ 301 if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) || 302 (OSSwapBigToHostInt32(dqhdr.dqh_version) > QF_VERSION) || 303 (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) { 304 (void) fprintf(stderr, 305 "repquota: %s: not a valid quota file\n", qfpathname); 306 (void) fclose(qf); 307 return (1); 308 } 309 } 310 311 printf(" 1K Block limits File limits\n"); 312 printf("User used soft hard grace used soft hard grace\n"); 313 314 maxentries = OSSwapBigToHostInt32(dqhdr.dqh_maxentries); 315 316 /* Read the entries in the quota file. */ 317 for (i = 0; i < maxentries; i++) { 318 if (fread(&dqbuf, sizeof(struct dqblk), 1, qf) == 0 && feof(qf)) 319 break; 320 id = OSSwapBigToHostInt32(dqbuf.dqb_id); 321 if (id == 0) 322 continue; 323 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curbytes == 0LL) 324 continue; 325 name = NULL; 326 switch (type) { 327 case USRQUOTA: 328 if ((pw = getpwuid(id)) != 0) 329 name = pw->pw_name; 330 break; 331 case GRPQUOTA: 332 if ((gr = getgrgid(id)) != 0) 333 name = gr->gr_name; 334 break; 335 } 336 if (name) 337 printf("%-10s", name); 338 else 339 printf("%-10u", (unsigned int)id); 340 341 bsoftlimit = OSSwapBigToHostInt64( dqbuf.dqb_bsoftlimit ); 342 isoftlimit = OSSwapBigToHostInt32( dqbuf.dqb_isoftlimit ); 343 curbytes = OSSwapBigToHostInt64( dqbuf.dqb_curbytes ); 344 curinodes = OSSwapBigToHostInt32( dqbuf.dqb_curinodes ); 345 346 printf("%c%c%12qd%12qd%12qd%7s", 347 bsoftlimit && 348 curbytes >= 349 bsoftlimit ? '+' : '-', 350 isoftlimit && 351 curinodes >= 352 isoftlimit ? '+' : '-', 353 curbytes / 1024, 354 bsoftlimit / 1024, 355 OSSwapBigToHostInt64( dqbuf.dqb_bhardlimit ) / 1024, 356 bsoftlimit && 357 curbytes >= 358 bsoftlimit ? 359 timeprt(OSSwapBigToHostInt32(dqbuf.dqb_btime)) : ""); 360 printf(" %6d%6d%6d%7s\n", 361 curinodes, 362 isoftlimit, 363 OSSwapBigToHostInt32( dqbuf.dqb_ihardlimit ), 364 isoftlimit && 365 curinodes >= 366 isoftlimit ? 367 timeprt(OSSwapBigToHostInt32(dqbuf.dqb_itime)) : ""); 368 } 369 fclose(qf); 370 371 return (0); 372} 373#else 374repquota(fs, type, qfpathname) 375 register struct fstab *fs; 376 int type; 377 char *qfpathname; 378{ 379 register struct fileusage *fup; 380 FILE *qf; 381 u_long id; 382 struct dqblk dqbuf; 383 char *timeprt(); 384 static struct dqblk zerodqblk; 385 static int warned = 0; 386 static int multiple = 0; 387 extern int errno; 388 389 if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 && 390 errno == ENOTSUP && !warned && vflag) { 391 warned++; 392 fprintf(stdout, 393 "*** Warning: Quotas are not compiled into this kernel\n"); 394 } 395 if (multiple++) 396 printf("\n"); 397 if (vflag) 398 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n", 399 qfextension[type], fs->fs_file, fs->fs_spec); 400 if ((qf = fopen(qfpathname, "r")) == NULL) { 401 perror(qfpathname); 402 return (1); 403 } 404 for (id = 0; ; id++) { 405 fread(&dqbuf, sizeof(struct dqblk), 1, qf); 406 if (feof(qf)) 407 break; 408 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0) 409 continue; 410 if ((fup = lookup(id, type)) == 0) 411 fup = addid(id, type, (char *)0); 412 fup->fu_dqblk = dqbuf; 413 } 414 fclose(qf); 415 printf(" Block limits File limits\n"); 416 printf("User used soft hard grace used soft hard grace\n"); 417 418 for (id = 0; id <= highid[type]; id++) { 419 fup = lookup(id, type); 420 if (fup == 0) 421 continue; 422 if (fup->fu_dqblk.dqb_curinodes == 0 && 423 fup->fu_dqblk.dqb_curblocks == 0) 424 continue; 425 printf("%-10s", fup->fu_name); 426 printf("%c%c%8d%8d%8d%7s", 427 fup->fu_dqblk.dqb_bsoftlimit && 428 fup->fu_dqblk.dqb_curblocks >= 429 fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-', 430 fup->fu_dqblk.dqb_isoftlimit && 431 fup->fu_dqblk.dqb_curinodes >= 432 fup->fu_dqblk.dqb_isoftlimit ? '+' : '-', 433 dbtob(fup->fu_dqblk.dqb_curblocks) / 1024, 434 dbtob(fup->fu_dqblk.dqb_bsoftlimit) / 1024, 435 dbtob(fup->fu_dqblk.dqb_bhardlimit) / 1024, 436 fup->fu_dqblk.dqb_bsoftlimit && 437 fup->fu_dqblk.dqb_curblocks >= 438 fup->fu_dqblk.dqb_bsoftlimit ? 439 timeprt(fup->fu_dqblk.dqb_btime) : ""); 440 printf(" %6d%6d%6d%7s\n", 441 fup->fu_dqblk.dqb_curinodes, 442 fup->fu_dqblk.dqb_isoftlimit, 443 fup->fu_dqblk.dqb_ihardlimit, 444 fup->fu_dqblk.dqb_isoftlimit && 445 fup->fu_dqblk.dqb_curinodes >= 446 fup->fu_dqblk.dqb_isoftlimit ? 447 timeprt(fup->fu_dqblk.dqb_itime) : ""); 448 fup->fu_dqblk = zerodqblk; 449 } 450 return (0); 451} 452#endif /* __APPLE */ 453 454/* 455 * Check to see if target appears in list of size cnt. 456 */ 457int 458oneof(target, list, cnt) 459 register char *target, **list; 460 int cnt; 461{ 462 register int i; 463 464 for (i = 0; i < cnt; i++) 465 if (strcmp(target, list[i]) == 0) 466 return (i); 467 return (-1); 468} 469 470/* 471 * Check to see if a particular quota is to be enabled. 472 */ 473#ifdef __APPLE__ 474int 475hasquota(fst, type, qfnamep) 476 register struct statfs *fst; 477 int type; 478 char **qfnamep; 479{ 480 struct stat sb; 481 static char initname, usrname[100], grpname[100]; 482 static char buf[BUFSIZ]; 483 484 if (!initname) { 485 snprintf(usrname, sizeof(usrname), "%s%s", qfextension[USRQUOTA], qfname); 486 snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname); 487 initname = 1; 488 } 489 490 /* 491 We only support the default path to the 492 on disk quota files. 493 */ 494 495 (void)snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, 496 QUOTAOPSNAME, qfextension[type] ); 497 if (stat(buf, &sb) != 0) { 498 /* There appears to be no mount option file */ 499 return(0); 500 } 501 502 (void) snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, qfname, qfextension[type]); 503 *qfnamep = buf; 504 return (1); 505} 506#else 507hasquota(fs, type, qfnamep) 508 register struct fstab *fs; 509 int type; 510 char **qfnamep; 511{ 512 register char *opt; 513 char *cp, *index(), *strtok(); 514 static char initname, usrname[100], grpname[100]; 515 static char buf[BUFSIZ]; 516 517 if (!initname) { 518 snprintf(usrname, sizoef(usrname), "%s%s", qfextension[USRQUOTA], qfname); 519 snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname); 520 initname = 1; 521 } 522 strlcpy(buf, fs->fs_mntops, sizeof(buf)); 523 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 524 if (cp = index(opt, '=')) 525 *cp++ = '\0'; 526 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 527 break; 528 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 529 break; 530 } 531 if (!opt) 532 return (0); 533 if (cp) { 534 *qfnamep = cp; 535 return (1); 536 } 537 (void) snprintf(buf, sizeof(buf), "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 538 *qfnamep = buf; 539 return (1); 540} 541#endif /* __APPLE__ */ 542 543 544#ifndef __APPLE__ 545 546/* 547 * Routines to manage the file usage table. 548 * 549 * Lookup an id of a specific type. 550 */ 551struct fileusage * 552lookup(id, type) 553 u_long id; 554 int type; 555{ 556 register struct fileusage *fup; 557 558 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 559 if (fup->fu_id == id) 560 return (fup); 561 return ((struct fileusage *)0); 562} 563 564/* 565 * Add a new file usage id if it does not already exist. 566 */ 567struct fileusage * 568addid(id, type, name) 569 u_long id; 570 int type; 571 char *name; 572{ 573 struct fileusage *fup, **fhp; 574 int len; 575 extern char *calloc(); 576 577 if (fup = lookup(id, type)) 578 return (fup); 579 if (name) 580 len = strlen(name); 581 else 582 len = 10; 583 if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) { 584 fprintf(stderr, "out of memory for fileusage structures\n"); 585 exit(1); 586 } 587 fhp = &fuhead[type][id & (FUHASH - 1)]; 588 fup->fu_next = *fhp; 589 *fhp = fup; 590 fup->fu_id = id; 591 if (id > highid[type]) 592 highid[type] = id; 593 if (name) { 594 bcopy(name, fup->fu_name, len + 1); 595 } else { 596 sprintf(fup->fu_name, "%u", id); 597 } 598 return (fup); 599} 600#endif /* !__APPLE__ */ 601 602/* 603 * Calculate the grace period and return a printable string for it. 604 */ 605char * 606timeprt(seconds) 607 time_t seconds; 608{ 609 time_t hours, minutes; 610 static char buf[20]; 611 static time_t now; 612 613 if (now == 0) 614 time(&now); 615 if (now > seconds) 616 return ("none"); 617 seconds -= now; 618 minutes = (seconds + 30) / 60; 619 hours = (minutes + 30) / 60; 620 if (hours >= 36) { 621 snprintf(buf, sizeof(buf), "%lddays", (hours + 12) / 24); 622 return (buf); 623 } 624 if (minutes >= 60) { 625 sprintf(buf, "%2ld:%ld", minutes / 60, minutes % 60); 626 return (buf); 627 } 628 sprintf(buf, "%2ld", minutes); 629 return (buf); 630} 631