1 /* $NetBSD: quotacheck.c,v 1.40 2008/10/09 14:56:35 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Robert Elz at The University of Melbourne. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\ 38 The Regents of the University of California. All rights reserved."); 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)quotacheck.c 8.6 (Berkeley) 4/28/95"; 44 #else 45 __RCSID("$NetBSD: quotacheck.c,v 1.40 2008/10/09 14:56:35 christos Exp $"); 46 #endif 47 #endif /* not lint */ 48 49 /* 50 * Fix up / report on disk quotas & usage 51 */ 52 #include <sys/param.h> 53 #include <sys/stat.h> 54 #include <sys/queue.h> 55 56 #include <ufs/ufs/dinode.h> 57 #include <ufs/ufs/quota.h> 58 #include <ufs/ufs/ufs_bswap.h> 59 #include <ufs/ffs/fs.h> 60 #include <ufs/ffs/ffs_extern.h> 61 62 #include <err.h> 63 #include <fcntl.h> 64 #include <fstab.h> 65 #include <pwd.h> 66 #include <grp.h> 67 #include <errno.h> 68 #include <unistd.h> 69 #include <stdio.h> 70 #include <stdlib.h> 71 #include <string.h> 72 73 #include "fsutil.h" 74 75 #ifndef FS_UFS1_MAGIC 76 # define FS_UFS1_MAGIC FS_MAGIC /* 0x011954 */ 77 # define FS_UFS1_MAGIC_SWAPPED 0x54190100 /* bswap32(0x011954) */ 78 # define DINODE1_SIZE sizeof(struct dinode) 79 # define DINODE2_SIZE 0 80 #else 81 # define HAVE_UFSv2 1 82 #endif 83 84 #ifndef SBLOCKSIZE 85 # define SBLOCKSIZE SBSIZE 86 #endif 87 #ifndef SBLOCKSEARCH 88 # define SBLOCKSEARCH { SBSIZE, -1 } 89 #endif 90 91 static const char *qfname = QUOTAFILENAME; 92 static const char *qfextension[] = INITQFNAMES; 93 static const char *quotagroup = QUOTAGROUP; 94 95 static union { 96 struct fs sblk; 97 char dummy[MAXBSIZE]; 98 } un; 99 #define sblock un.sblk 100 static long dev_bsize; 101 static long maxino; 102 103 struct quotaname { 104 long flags; 105 char grpqfname[MAXPATHLEN + 1]; 106 char usrqfname[MAXPATHLEN + 1]; 107 }; 108 #define HASUSR 1 109 #define HASGRP 2 110 111 struct fileusage { 112 struct fileusage *fu_next; 113 u_long fu_curinodes; 114 u_long fu_curblocks; 115 u_int32_t fu_id; /* uid_t, gid_t */ 116 char fu_name[1]; 117 /* actually bigger */ 118 }; 119 #define FUHASH 1024 /* must be power of two */ 120 static struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 121 122 123 union comb_dinode { 124 #ifdef HAVE_UFSv2 125 struct ufs1_dinode dp1; 126 struct ufs2_dinode dp2; 127 #else 128 struct dinode dp1; 129 #endif 130 }; 131 #ifdef HAVE_UFSv2 132 #define DIP(dp, field) \ 133 (is_ufs2 ? (dp)->dp2.di_##field : (dp)->dp1.di_##field) 134 #else 135 #define DIP(dp, field) (dp)->dp1.di_##field 136 #endif 137 138 139 static int aflag; /* all file systems */ 140 static int gflag; /* check group quotas */ 141 static int uflag; /* check user quotas */ 142 static int vflag; /* verbose */ 143 static int qflag; /* quick but untidy mode */ 144 static int fi; /* open disk file descriptor */ 145 static u_int32_t highid[MAXQUOTAS];/* highest addid()'ed identifier per type */ 146 static int needswap; /* FS is in swapped order */ 147 static int got_siginfo = 0; /* got a siginfo signal */ 148 static int is_ufs2; 149 150 151 int main __P((int, char *[])); 152 static void usage __P((void)); 153 static void *needchk __P((struct fstab *)); 154 static int chkquota __P((const char *, const char *, const char *, void *, 155 pid_t *)); 156 static int update __P((const char *, const char *, int)); 157 static u_int32_t skipforward __P((u_int32_t, u_int32_t, FILE *)); 158 static int oneof __P((const char *, char *[], int)); 159 static int getquotagid __P((void)); 160 static int hasquota __P((struct fstab *, int, char **)); 161 static struct fileusage *lookup __P((u_int32_t, int)); 162 static struct fileusage *addid __P((u_int32_t, int, const char *)); 163 static u_int32_t subsequent __P((u_int32_t, int)); 164 static union comb_dinode *getnextinode __P((ino_t)); 165 static void setinodebuf __P((ino_t)); 166 static void freeinodebuf __P((void)); 167 static void bread __P((daddr_t, char *, long)); 168 static void infohandler __P((int sig)); 169 static void swap_dinode1(union comb_dinode *, int); 170 #ifdef HAVE_UFSv2 171 static void swap_dinode2(union comb_dinode *, int); 172 #endif 173 174 int 175 main(argc, argv) 176 int argc; 177 char *argv[]; 178 { 179 struct fstab *fs; 180 struct passwd *pw; 181 struct group *gr; 182 struct quotaname *auxdata; 183 int i, argnum, maxrun, errs; 184 long done = 0; 185 int flags = CHECK_PREEN; 186 const char *name; 187 int ch; 188 189 errs = maxrun = 0; 190 while ((ch = getopt(argc, argv, "aguvqdl:")) != -1) { 191 switch(ch) { 192 case 'a': 193 aflag++; 194 break; 195 case 'd': 196 flags |= CHECK_DEBUG; 197 break; 198 case 'g': 199 gflag++; 200 break; 201 case 'u': 202 uflag++; 203 break; 204 case 'q': 205 qflag++; 206 break; 207 case 'v': 208 vflag++; 209 break; 210 case 'l': 211 maxrun = atoi(optarg); 212 break; 213 default: 214 usage(); 215 } 216 } 217 argc -= optind; 218 argv += optind; 219 if ((argc == 0 && !aflag) || (argc > 0 && aflag) || (!aflag && maxrun)) 220 usage(); 221 if (!gflag && !uflag) { 222 gflag++; 223 uflag++; 224 } 225 226 /* If -a, we do not want to pay the cost of processing every 227 * group and password entry if there are no filesystems with quotas 228 */ 229 if (aflag) { 230 i = 0; 231 while ((fs = getfsent()) != NULL) { 232 if (needchk(fs)) 233 i=1; 234 } 235 endfsent(); 236 if (!i) /* No filesystems with quotas */ 237 exit(0); 238 } 239 240 if (gflag) { 241 setgrent(); 242 while ((gr = getgrent()) != 0) 243 (void) addid((u_int32_t)gr->gr_gid, GRPQUOTA, gr->gr_name); 244 endgrent(); 245 } 246 if (uflag) { 247 setpwent(); 248 while ((pw = getpwent()) != 0) 249 (void) addid((u_int32_t)pw->pw_uid, USRQUOTA, pw->pw_name); 250 endpwent(); 251 } 252 if (aflag) 253 exit(checkfstab(flags, maxrun, needchk, chkquota)); 254 if (setfsent() == 0) 255 err(1, "%s: can't open", FSTAB); 256 while ((fs = getfsent()) != NULL) { 257 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 258 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && 259 (auxdata = needchk(fs)) && 260 (name = blockcheck(fs->fs_spec))) { 261 done |= 1 << argnum; 262 errs += chkquota(fs->fs_type, name, fs->fs_file, 263 auxdata, NULL); 264 } 265 } 266 endfsent(); 267 for (i = 0; i < argc; i++) 268 if ((done & (1 << i)) == 0) 269 fprintf(stderr, "%s not found in %s\n", 270 argv[i], FSTAB); 271 exit(errs); 272 } 273 274 static void 275 usage() 276 { 277 278 (void)fprintf(stderr, 279 "usage:\t%s -a [-gquv] [-l maxparallel]\n\t%s [-gquv] filesys ...\n", getprogname(), 280 getprogname()); 281 exit(1); 282 } 283 284 static void * 285 needchk(fs) 286 struct fstab *fs; 287 { 288 struct quotaname *qnp; 289 char *qfnp; 290 291 if (strcmp(fs->fs_vfstype, "ffs") || 292 strcmp(fs->fs_type, FSTAB_RW)) 293 return (NULL); 294 if ((qnp = malloc(sizeof(*qnp))) == NULL) 295 err(1, "%s", strerror(errno)); 296 qnp->flags = 0; 297 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 298 strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname)); 299 qnp->flags |= HASGRP; 300 } 301 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 302 strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname)); 303 qnp->flags |= HASUSR; 304 } 305 if (qnp->flags) 306 return (qnp); 307 free(qnp); 308 return (NULL); 309 } 310 311 off_t sblock_try[] = SBLOCKSEARCH; 312 313 /* 314 * Scan the specified filesystem to check quota(s) present on it. 315 */ 316 static int 317 chkquota(type, fsname, mntpt, v, pid) 318 const char *type, *fsname, *mntpt; 319 void *v; 320 pid_t *pid; 321 { 322 struct quotaname *qnp = v; 323 struct fileusage *fup; 324 union comb_dinode *dp; 325 int cg, i, mode, errs = 0, inosused; 326 ino_t ino; 327 struct cg *cgp; 328 char msgbuf[4096]; 329 330 if (pid != NULL) { 331 fflush(stdout); 332 switch ((*pid = fork())) { 333 default: 334 return 0; 335 case 0: 336 break; 337 case -1: 338 err(1, "Cannot fork"); 339 } 340 setvbuf(stdout, msgbuf, _IOFBF, sizeof msgbuf); 341 } 342 343 if ((fi = open(fsname, O_RDONLY, 0)) < 0) { 344 warn("Cannot open %s", fsname); 345 if (pid != NULL) 346 exit(1); 347 return 1; 348 } 349 if (vflag) { 350 (void)printf("*** Checking "); 351 if (qnp->flags & HASUSR) 352 (void)printf("%s%s", qfextension[USRQUOTA], 353 (qnp->flags & HASGRP) ? " and " : ""); 354 if (qnp->flags & HASGRP) 355 (void)printf("%s", qfextension[GRPQUOTA]); 356 (void)printf(" quotas for %s (%s)\n", fsname, mntpt); 357 fflush(stdout); 358 } 359 signal(SIGINFO, infohandler); 360 sync(); 361 dev_bsize = 1; 362 363 for (i = 0; ; i++) { 364 if (sblock_try[i] == -1) { 365 warnx("%s: superblock not found", fsname); 366 if (pid != NULL) 367 exit(1); 368 return 1; 369 } 370 bread(sblock_try[i], (char *)&sblock, SBLOCKSIZE); 371 switch (sblock.fs_magic) { 372 #ifdef HAVE_UFSv2 373 case FS_UFS2_MAGIC: 374 is_ufs2 = 1; 375 /*FALLTHROUGH*/ 376 #endif 377 case FS_UFS1_MAGIC: 378 break; 379 #ifdef HAVE_UFSv2 380 case FS_UFS2_MAGIC_SWAPPED: 381 is_ufs2 = 1; 382 /*FALLTHROUGH*/ 383 #endif 384 case FS_UFS1_MAGIC_SWAPPED: 385 needswap = 1; 386 ffs_sb_swap(&sblock, &sblock); 387 break; 388 default: 389 continue; 390 } 391 392 #ifdef HAVE_UFSv2 393 if (is_ufs2 || sblock.fs_old_flags & FS_FLAGS_UPDATED) { 394 if (sblock.fs_sblockloc != sblock_try[i]) 395 continue; 396 } else { 397 if (sblock_try[i] == SBLOCK_UFS2) 398 continue; 399 } 400 #endif 401 break; 402 } 403 404 cgp = malloc(sblock.fs_cgsize); 405 if (cgp == NULL) { 406 warn("%s: can't allocate %d bytes of cg space", fsname, 407 sblock.fs_cgsize); 408 if (pid != NULL) 409 exit(1); 410 return 1; 411 } 412 413 dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 414 maxino = sblock.fs_ncg * sblock.fs_ipg; 415 for (cg = 0; cg < sblock.fs_ncg; cg++) { 416 ino = cg * sblock.fs_ipg; 417 setinodebuf(ino); 418 #ifdef HAVE_UFSv2 419 if (sblock.fs_magic == FS_UFS2_MAGIC) { 420 bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)cgp, 421 sblock.fs_cgsize); 422 if (needswap) 423 ffs_cg_swap(cgp, cgp, &sblock); 424 inosused = cgp->cg_initediblk; 425 } else 426 #endif 427 inosused = sblock.fs_ipg; 428 for (i = 0; i < inosused; i++, ino++) { 429 if (got_siginfo) { 430 fprintf(stderr, 431 "%s: cyl group %d of %d (%d%%)\n", 432 fsname, cg, sblock.fs_ncg, 433 cg * 100 / sblock.fs_ncg); 434 got_siginfo = 0; 435 } 436 if (ino < ROOTINO) 437 continue; 438 if ((dp = getnextinode(ino)) == NULL) 439 continue; 440 if ((mode = DIP(dp, mode) & IFMT) == 0) 441 continue; 442 if (qnp->flags & HASGRP) { 443 fup = addid(DIP(dp, gid), GRPQUOTA, 444 (char *)0); 445 fup->fu_curinodes++; 446 if (mode == IFREG || mode == IFDIR || 447 mode == IFLNK) 448 fup->fu_curblocks += DIP(dp, blocks); 449 } 450 if (qnp->flags & HASUSR) { 451 fup = addid(DIP(dp, uid), USRQUOTA, 452 (char *)0); 453 fup->fu_curinodes++; 454 if (mode == IFREG || mode == IFDIR || 455 mode == IFLNK) 456 fup->fu_curblocks += DIP(dp, blocks); 457 } 458 } 459 } 460 freeinodebuf(); 461 free(cgp); 462 if (qnp->flags & HASUSR) 463 errs += update(mntpt, qnp->usrqfname, USRQUOTA); 464 if (qnp->flags & HASGRP) 465 errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 466 close(fi); 467 if (pid != NULL) 468 exit(errs); 469 return errs; 470 } 471 472 /* 473 * Update a specified quota file. 474 */ 475 static int 476 update(fsname, quotafile, type) 477 const char *fsname, *quotafile; 478 int type; 479 { 480 struct fileusage *fup; 481 FILE *qfi, *qfo; 482 u_int32_t id, lastid, nextid; 483 int need_seek; 484 struct dqblk dqbuf; 485 static int warned = 0; 486 static struct dqblk zerodqbuf; 487 static struct fileusage zerofileusage; 488 489 if ((qfo = fopen(quotafile, "r+")) == NULL) { 490 if (errno == ENOENT) 491 qfo = fopen(quotafile, "w+"); 492 if (qfo) { 493 (void) fprintf(stderr, 494 "quotacheck: creating quota file %s\n", quotafile); 495 #define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 496 (void) fchown(fileno(qfo), getuid(), getquotagid()); 497 (void) fchmod(fileno(qfo), MODE); 498 } else { 499 (void) fprintf(stderr, 500 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 501 return (1); 502 } 503 } 504 if ((qfi = fopen(quotafile, "r")) == NULL) { 505 (void) fprintf(stderr, 506 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 507 (void) fclose(qfo); 508 return (1); 509 } 510 if (quotactl(fsname, QCMD(Q_SYNC, type), 0, (void *) NULL) < 0 && 511 errno == EOPNOTSUPP && !warned && vflag) { 512 warned++; 513 (void)printf("*** Warning: %s\n", 514 "Quotas are not compiled into this kernel"); 515 } 516 need_seek = 1; 517 for (lastid = highid[type], id = 0; id <= lastid; id = nextid) { 518 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 519 dqbuf = zerodqbuf; 520 if ((fup = lookup(id, type)) == 0) 521 fup = &zerofileusage; 522 523 nextid = subsequent(id, type); 524 if (nextid > 0 && nextid != id + 1) /* watch out for id == UINT32_MAX */ 525 nextid = skipforward(id, nextid, qfi); 526 527 if (got_siginfo) { 528 /* XXX this could try to show percentage through the ID list */ 529 fprintf(stderr, 530 "%s: updating %s quotas for id=%" PRIu32 " (%s)\n", fsname, 531 qfextension[type < MAXQUOTAS ? type : MAXQUOTAS], 532 id, fup->fu_name); 533 got_siginfo = 0; 534 } 535 if (dqbuf.dqb_curinodes == fup->fu_curinodes && 536 dqbuf.dqb_curblocks == fup->fu_curblocks) { 537 fup->fu_curinodes = 0; /* reset usage */ 538 fup->fu_curblocks = 0; /* for next filesystem */ 539 540 need_seek = 1; 541 if (id == UINT32_MAX || nextid == 0) { /* infinite loop avoidance (OR do as "nextid < id"?) */ 542 break; 543 } 544 continue; 545 } 546 if (vflag) { 547 if (aflag) 548 printf("%s: ", fsname); 549 printf("%-8s fixed:", fup->fu_name); 550 if (dqbuf.dqb_curinodes != fup->fu_curinodes) 551 (void)printf("\tinodes %d -> %ld", 552 dqbuf.dqb_curinodes, fup->fu_curinodes); 553 if (dqbuf.dqb_curblocks != fup->fu_curblocks) 554 (void)printf("\tblocks %d -> %ld", 555 dqbuf.dqb_curblocks, fup->fu_curblocks); 556 (void)printf("\n"); 557 } 558 /* 559 * Reset time limit if have a soft limit and were 560 * previously under it, but are now over it. 561 */ 562 if (dqbuf.dqb_bsoftlimit && 563 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 564 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 565 dqbuf.dqb_btime = 0; 566 if (dqbuf.dqb_isoftlimit && 567 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 568 fup->fu_curinodes >= dqbuf.dqb_isoftlimit) 569 dqbuf.dqb_itime = 0; 570 dqbuf.dqb_curinodes = fup->fu_curinodes; 571 dqbuf.dqb_curblocks = fup->fu_curblocks; 572 573 if (need_seek) { 574 (void) fseeko(qfo, (off_t)id * sizeof(struct dqblk), 575 SEEK_SET); 576 need_seek = nextid != id + 1; 577 } 578 (void) fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 579 580 if (!warned) 581 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 582 (caddr_t)&dqbuf); 583 584 fup->fu_curinodes = 0; 585 fup->fu_curblocks = 0; 586 if (id == UINT32_MAX || nextid == 0) { /* infinite loop avoidance (OR do as "nextid < id"?) */ 587 break; 588 } 589 } 590 (void) fclose(qfi); 591 (void) fflush(qfo); 592 if (highid[type] != UINT32_MAX) 593 (void) ftruncate(fileno(qfo), 594 (off_t)((highid[type] + 1) * sizeof(struct dqblk))); 595 (void) fclose(qfo); 596 return (0); 597 } 598 599 u_int32_t 600 skipforward(cur, to, qfi) 601 u_int32_t cur, to; 602 FILE *qfi; 603 { 604 struct dqblk dqbuf; 605 606 if (qflag) { 607 (void) fseeko(qfi, (off_t)to * sizeof(struct dqblk), SEEK_SET); 608 return (to); 609 } 610 611 while (++cur < to) { 612 /* 613 * if EOF occurs, nothing left to read, we're done 614 */ 615 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 616 return (to); 617 618 /* 619 * If we find an entry that shows usage, before the next 620 * id that has actual usage, we have to stop here, so the 621 * incorrect entry can be corrected in the file 622 */ 623 if (dqbuf.dqb_curinodes != 0 || dqbuf.dqb_curblocks != 0) { 624 (void)fseek(qfi, -(long)sizeof(struct dqblk), SEEK_CUR); 625 return (cur); 626 } 627 } 628 return (to); 629 } 630 631 /* 632 * Check to see if target appears in list of size cnt. 633 */ 634 static int 635 oneof(target, list, cnt) 636 const char *target; 637 char *list[]; 638 int cnt; 639 { 640 int i; 641 642 for (i = 0; i < cnt; i++) 643 if (strcmp(target, list[i]) == 0) 644 return (i); 645 return (-1); 646 } 647 648 /* 649 * Determine the group identifier for quota files. 650 */ 651 static int 652 getquotagid() 653 { 654 struct group *gr; 655 656 if ((gr = getgrnam(quotagroup)) != NULL) 657 return (gr->gr_gid); 658 return (-1); 659 } 660 661 /* 662 * Check to see if a particular quota is to be enabled. 663 */ 664 static int 665 hasquota(fs, type, qfnamep) 666 struct fstab *fs; 667 int type; 668 char **qfnamep; 669 { 670 char *opt; 671 char *cp = NULL; 672 static char initname, usrname[100], grpname[100]; 673 static char buf[BUFSIZ]; 674 675 if (!initname) { 676 (void)snprintf(usrname, sizeof(usrname), 677 "%s%s", qfextension[USRQUOTA], qfname); 678 (void)snprintf(grpname, sizeof(grpname), 679 "%s%s", qfextension[GRPQUOTA], qfname); 680 initname = 1; 681 } 682 (void) strlcpy(buf, fs->fs_mntops, sizeof(buf)); 683 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 684 if ((cp = strchr(opt, '=')) != NULL) 685 *cp++ = '\0'; 686 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 687 break; 688 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 689 break; 690 } 691 if (!opt) 692 return (0); 693 if (cp) 694 *qfnamep = cp; 695 else { 696 (void)snprintf(buf, sizeof(buf), 697 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 698 *qfnamep = buf; 699 } 700 return (1); 701 } 702 703 /* 704 * Routines to manage the file usage table. 705 * 706 * Lookup an id of a specific type. 707 */ 708 static struct fileusage * 709 lookup(id, type) 710 u_int32_t id; 711 int type; 712 { 713 struct fileusage *fup; 714 715 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 716 if (fup->fu_id == id) 717 return (fup); 718 return (NULL); 719 } 720 721 /* 722 * Add a new file usage id if it does not already exist. 723 */ 724 static struct fileusage * 725 addid(id, type, name) 726 u_int32_t id; 727 int type; 728 const char *name; 729 { 730 struct fileusage *fup, **fhp; 731 int len; 732 733 if ((fup = lookup(id, type)) != NULL) 734 return (fup); 735 if (name) 736 len = strlen(name); 737 else 738 len = 10; 739 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 740 err(1, "%s", strerror(errno)); 741 fhp = &fuhead[type][id & (FUHASH - 1)]; 742 fup->fu_next = *fhp; 743 *fhp = fup; 744 fup->fu_id = id; 745 if (id > highid[type]) 746 highid[type] = id; 747 if (name) 748 memmove(fup->fu_name, name, len + 1); 749 else 750 (void) sprintf(fup->fu_name, "%" PRIu32, id); 751 return (fup); 752 } 753 754 static u_int32_t 755 subsequent(id, type) 756 u_int32_t id; 757 int type; 758 { 759 struct fileusage *fup, **iup, **cup; 760 u_int32_t next, offset; 761 762 next = highid[type] + 1; 763 offset = 0; 764 cup = iup = &fuhead[type][id & (FUHASH-1)]; 765 do { 766 ++offset; 767 if (++cup >= &fuhead[type][FUHASH]) 768 cup = &fuhead[type][0]; 769 for (fup = *cup; fup != 0; fup = fup->fu_next) { 770 if (fup->fu_id > id && fup->fu_id <= id + offset) 771 return (fup->fu_id); 772 if (fup->fu_id > id && fup->fu_id < next) 773 next = fup->fu_id; 774 } 775 } while (cup != iup); 776 777 return next; 778 } 779 780 /* 781 * Special purpose version of ginode used to optimize first pass 782 * over all the inodes in numerical order. 783 */ 784 static ino_t nextino, lastinum, lastvalidinum; 785 static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 786 static union comb_dinode *inodebuf; 787 #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 788 789 union comb_dinode * 790 getnextinode(inumber) 791 ino_t inumber; 792 { 793 long size; 794 daddr_t dblk; 795 static union comb_dinode *dp; 796 union comb_dinode *ret; 797 798 if (inumber != nextino++ || inumber > lastvalidinum) { 799 errx(1, "bad inode number %llu to nextinode", 800 (unsigned long long)inumber); 801 } 802 803 if (inumber >= lastinum) { 804 readcnt++; 805 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 806 if (readcnt % readpercg == 0) { 807 size = partialsize; 808 lastinum += partialcnt; 809 } else { 810 size = inobufsize; 811 lastinum += fullcnt; 812 } 813 (void)bread(dblk, (caddr_t)inodebuf, size); 814 if (needswap) { 815 #ifdef HAVE_UFSv2 816 if (is_ufs2) 817 swap_dinode2(inodebuf, lastinum - inumber); 818 else 819 #endif 820 swap_dinode1(inodebuf, lastinum - inumber); 821 } 822 dp = (union comb_dinode *)inodebuf; 823 } 824 ret = dp; 825 dp = (union comb_dinode *) 826 ((char *)dp + (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE)); 827 return ret; 828 } 829 830 void 831 setinodebuf(inum) 832 ino_t inum; 833 { 834 835 if (inum % sblock.fs_ipg != 0) 836 errx(1, "bad inode number %llu to setinodebuf", 837 (unsigned long long)inum); 838 839 lastvalidinum = inum + sblock.fs_ipg - 1; 840 nextino = inum; 841 lastinum = inum; 842 readcnt = 0; 843 if (inodebuf != NULL) 844 return; 845 inobufsize = blkroundup(&sblock, INOBUFSIZE); 846 fullcnt = inobufsize / (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE); 847 readpercg = sblock.fs_ipg / fullcnt; 848 partialcnt = sblock.fs_ipg % fullcnt; 849 partialsize = partialcnt * (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE); 850 if (partialcnt != 0) { 851 readpercg++; 852 } else { 853 partialcnt = fullcnt; 854 partialsize = inobufsize; 855 } 856 if (inodebuf == NULL && 857 (inodebuf = malloc((unsigned)inobufsize)) == NULL) 858 errx(1, "Cannot allocate space for inode buffer"); 859 while (nextino < ROOTINO) 860 getnextinode(nextino); 861 } 862 863 void 864 freeinodebuf() 865 { 866 867 if (inodebuf != NULL) 868 free((char *)inodebuf); 869 inodebuf = NULL; 870 } 871 872 873 #ifdef HAVE_UFSv2 874 static void 875 swap_dinode1(union comb_dinode *dp, int n) 876 { 877 int i; 878 struct ufs1_dinode *dp1; 879 880 dp1 = (struct ufs1_dinode *)&dp->dp1; 881 for (i = 0; i < n; i++, dp1++) 882 ffs_dinode1_swap(dp1, dp1); 883 } 884 885 static void 886 swap_dinode2(union comb_dinode *dp, int n) 887 { 888 int i; 889 struct ufs2_dinode *dp2; 890 891 dp2 = (struct ufs2_dinode *)&dp->dp2; 892 for (i = 0; i < n; i++, dp2++) 893 ffs_dinode2_swap(dp2, dp2); 894 } 895 896 #else 897 898 static void 899 swap_dinode1(union comb_dinode *dp, int n) 900 { 901 int i; 902 struct dinode *dp1; 903 904 dp1 = (struct dinode *) &dp->dp1; 905 for (i = 0; i < n; i++, dp1++) 906 ffs_dinode_swap(dp1, dp1); 907 } 908 909 #endif 910 911 /* 912 * Read specified disk blocks. 913 */ 914 static void 915 bread(bno, buf, cnt) 916 daddr_t bno; 917 char *buf; 918 long cnt; 919 { 920 921 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || 922 read(fi, buf, cnt) != cnt) 923 err(1, "block %lld", (long long)bno); 924 } 925 926 void 927 infohandler(int sig) 928 { 929 got_siginfo = 1; 930 } 931