quotacheck.c revision 166180
1/* 2 * Copyright (c) 1980, 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Robert Elz at The University of Melbourne. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#if 0 34#ifndef lint 35static const char copyright[] = 36"@(#) Copyright (c) 1980, 1990, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94"; 42#endif /* not lint */ 43#endif 44#include <sys/cdefs.h> 45__FBSDID("$FreeBSD: head/sbin/quotacheck/quotacheck.c 166180 2007-01-23 02:13:00Z mpp $"); 46 47/* 48 * Fix up / report on disk quotas & usage 49 */ 50#include <sys/param.h> 51#include <sys/disklabel.h> 52#include <sys/stat.h> 53 54#include <ufs/ufs/dinode.h> 55#include <ufs/ufs/quota.h> 56#include <ufs/ffs/fs.h> 57 58#include <err.h> 59#include <errno.h> 60#include <fcntl.h> 61#include <fstab.h> 62#include <grp.h> 63#include <pwd.h> 64#include <stdio.h> 65#include <stdlib.h> 66#include <string.h> 67#include <unistd.h> 68 69char *qfname = QUOTAFILENAME; 70char *qfextension[] = INITQFNAMES; 71char *quotagroup = QUOTAGROUP; 72 73union { 74 struct fs sblk; 75 char dummy[MAXBSIZE]; 76} sb_un; 77#define sblock sb_un.sblk 78union { 79 struct cg cgblk; 80 char dummy[MAXBSIZE]; 81} cg_un; 82#define cgblk cg_un.cgblk 83long dev_bsize = 1; 84ino_t maxino; 85 86union dinode { 87 struct ufs1_dinode dp1; 88 struct ufs2_dinode dp2; 89}; 90#define DIP(dp, field) \ 91 ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ 92 (dp)->dp1.field : (dp)->dp2.field) 93 94struct quotaname { 95 long flags; 96 char grpqfname[PATH_MAX]; 97 char usrqfname[PATH_MAX]; 98}; 99#define HASUSR 1 100#define HASGRP 2 101 102struct fileusage { 103 struct fileusage *fu_next; 104 u_long fu_curinodes; 105 u_long fu_curblocks; 106 u_long fu_id; 107 char fu_name[1]; 108 /* actually bigger */ 109}; 110#define FUHASH 1024 /* must be power of two */ 111struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 112 113int aflag; /* all file systems */ 114int gflag; /* check group quotas */ 115int uflag; /* check user quotas */ 116int vflag; /* verbose */ 117int fi; /* open disk file descriptor */ 118 119struct fileusage * 120 addid(u_long, int, char *, char *); 121char *blockcheck(char *); 122void bread(ufs2_daddr_t, char *, long); 123extern int checkfstab(int, int, void * (*)(struct fstab *), 124 int (*)(char *, char *, struct quotaname *)); 125int chkquota(char *, char *, struct quotaname *); 126void freeinodebuf(void); 127union dinode * 128 getnextinode(ino_t); 129int getquotagid(void); 130int hasquota(struct fstab *, int, char **); 131struct fileusage * 132 lookup(u_long, int); 133void *needchk(struct fstab *); 134int oneof(char *, char*[], int); 135void printchanges(char *, int, struct dqblk *, struct fileusage *, u_long); 136void setinodebuf(ino_t); 137int update(char *, char *, int); 138void usage(void); 139 140int 141main(argc, argv) 142 int argc; 143 char *argv[]; 144{ 145 struct fstab *fs; 146 struct passwd *pw; 147 struct group *gr; 148 struct quotaname *auxdata; 149 int i, argnum, maxrun, errs, ch; 150 long done = 0; 151 char *name; 152 153 errs = maxrun = 0; 154 while ((ch = getopt(argc, argv, "aguvl:")) != -1) { 155 switch(ch) { 156 case 'a': 157 aflag++; 158 break; 159 case 'g': 160 gflag++; 161 break; 162 case 'u': 163 uflag++; 164 break; 165 case 'v': 166 vflag++; 167 break; 168 case 'l': 169 maxrun = atoi(optarg); 170 break; 171 default: 172 usage(); 173 } 174 } 175 argc -= optind; 176 argv += optind; 177 if ((argc == 0 && !aflag) || (argc > 0 && aflag)) 178 usage(); 179 if (!gflag && !uflag) { 180 gflag++; 181 uflag++; 182 } 183 if (gflag) { 184 setgrent(); 185 while ((gr = getgrent()) != NULL) 186 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name, 187 NULL); 188 endgrent(); 189 } 190 if (uflag) { 191 setpwent(); 192 while ((pw = getpwent()) != NULL) 193 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name, 194 NULL); 195 endpwent(); 196 } 197 /* 198 * Setting maxrun (-l) makes no sense without the -a flag. 199 * Historically this was never an error, so we just warn. 200 */ 201 if (maxrun > 0 && !aflag) 202 warnx("ignoring -l without -a"); 203 if (aflag) 204 exit(checkfstab(1, maxrun, needchk, chkquota)); 205 if (setfsent() == 0) 206 errx(1, "%s: can't open", FSTAB); 207 while ((fs = getfsent()) != NULL) { 208 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 209 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && 210 (auxdata = needchk(fs)) && 211 (name = blockcheck(fs->fs_spec))) { 212 done |= 1 << argnum; 213 errs += chkquota(name, fs->fs_file, auxdata); 214 } 215 } 216 endfsent(); 217 for (i = 0; i < argc; i++) 218 if ((done & (1 << i)) == 0) 219 fprintf(stderr, "%s not found in %s\n", 220 argv[i], FSTAB); 221 exit(errs); 222} 223 224void 225usage() 226{ 227 (void)fprintf(stderr, "%s\n%s\n", 228 "usage: quotacheck [-guv] [-l maxrun] -a", 229 " quotacheck [-guv] filesystem ..."); 230 exit(1); 231} 232 233void * 234needchk(fs) 235 struct fstab *fs; 236{ 237 struct quotaname *qnp; 238 char *qfnp; 239 240 if (strcmp(fs->fs_vfstype, "ufs") || 241 strcmp(fs->fs_type, FSTAB_RW)) 242 return (NULL); 243 if ((qnp = malloc(sizeof(*qnp))) == NULL) 244 errx(1, "malloc failed"); 245 qnp->flags = 0; 246 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 247 strcpy(qnp->grpqfname, qfnp); 248 qnp->flags |= HASGRP; 249 } 250 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 251 strcpy(qnp->usrqfname, qfnp); 252 qnp->flags |= HASUSR; 253 } 254 if (qnp->flags) 255 return (qnp); 256 free(qnp); 257 return (NULL); 258} 259 260/* 261 * Possible superblock locations ordered from most to least likely. 262 */ 263static int sblock_try[] = SBLOCKSEARCH; 264 265/* 266 * Scan the specified file system to check quota(s) present on it. 267 */ 268int 269chkquota(fsname, mntpt, qnp) 270 char *fsname, *mntpt; 271 struct quotaname *qnp; 272{ 273 struct fileusage *fup; 274 union dinode *dp; 275 int cg, i, mode, errs = 0; 276 ino_t ino, inosused, userino = 0, groupino = 0; 277 char *cp; 278 struct stat sb; 279 280 if ((fi = open(fsname, O_RDONLY, 0)) < 0) { 281 warn("%s", fsname); 282 return (1); 283 } 284 if (vflag) { 285 (void)printf("*** Checking "); 286 if (qnp->flags & HASUSR) 287 (void)printf("%s%s", qfextension[USRQUOTA], 288 (qnp->flags & HASGRP) ? " and " : ""); 289 if (qnp->flags & HASGRP) 290 (void)printf("%s", qfextension[GRPQUOTA]); 291 (void)printf(" quotas for %s (%s)\n", fsname, mntpt); 292 } 293 if (qnp->flags & HASUSR) { 294 if (stat(qnp->usrqfname, &sb) == 0) 295 userino = sb.st_ino; 296 } 297 if (qnp->flags & HASGRP) { 298 if (stat(qnp->grpqfname, &sb) == 0) 299 groupino = sb.st_ino; 300 } 301 sync(); 302 dev_bsize = 1; 303 for (i = 0; sblock_try[i] != -1; i++) { 304 bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE); 305 if ((sblock.fs_magic == FS_UFS1_MAGIC || 306 (sblock.fs_magic == FS_UFS2_MAGIC && 307 sblock.fs_sblockloc == sblock_try[i])) && 308 sblock.fs_bsize <= MAXBSIZE && 309 sblock.fs_bsize >= sizeof(struct fs)) 310 break; 311 } 312 if (sblock_try[i] == -1) { 313 warn("Cannot find file system superblock"); 314 return (1); 315 } 316 dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 317 maxino = sblock.fs_ncg * sblock.fs_ipg; 318 for (cg = 0; cg < sblock.fs_ncg; cg++) { 319 ino = cg * sblock.fs_ipg; 320 setinodebuf(ino); 321 bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk), 322 sblock.fs_cgsize); 323 if (sblock.fs_magic == FS_UFS2_MAGIC) 324 inosused = cgblk.cg_initediblk; 325 else 326 inosused = sblock.fs_ipg; 327 /* 328 * If we are using soft updates, then we can trust the 329 * cylinder group inode allocation maps to tell us which 330 * inodes are allocated. We will scan the used inode map 331 * to find the inodes that are really in use, and then 332 * read only those inodes in from disk. 333 */ 334 if (sblock.fs_flags & FS_DOSOFTDEP) { 335 if (!cg_chkmagic(&cgblk)) 336 errx(1, "CG %d: BAD MAGIC NUMBER\n", cg); 337 cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT]; 338 for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { 339 if (*cp == 0) 340 continue; 341 for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { 342 if (*cp & i) 343 break; 344 inosused--; 345 } 346 break; 347 } 348 if (inosused <= 0) 349 continue; 350 } 351 for (i = 0; i < inosused; i++, ino++) { 352 if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO || 353 (mode = DIP(dp, di_mode) & IFMT) == 0) 354 continue; 355 /* 356 * XXX: Do not account for UIDs or GIDs that appear 357 * to be negative to prevent generating 100GB+ 358 * quota files. 359 */ 360 if ((int)DIP(dp, di_uid) < 0 || 361 (int)DIP(dp, di_gid) < 0) { 362 if (vflag) { 363 if (aflag) 364 (void)printf("%s: ", mntpt); 365 (void)printf("out of range UID/GID (%u/%u) ino=%u\n", 366 DIP(dp, di_uid), DIP(dp,di_gid), 367 ino); 368 } 369 continue; 370 } 371 372 /* 373 * Do not account for file system snapshot files 374 * or the actual quota data files to be consistent 375 * with how they are handled inside the kernel. 376 */ 377#ifdef SF_SNAPSHOT 378 if (DIP(dp, di_flags) & SF_SNAPSHOT) 379 continue; 380#endif 381 if (ino == userino || ino == groupino) 382 continue; 383 if (qnp->flags & HASGRP) { 384 fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA, 385 (char *)0, mntpt); 386 fup->fu_curinodes++; 387 if (mode == IFREG || mode == IFDIR || 388 mode == IFLNK) 389 fup->fu_curblocks += DIP(dp, di_blocks); 390 } 391 if (qnp->flags & HASUSR) { 392 fup = addid((u_long)DIP(dp, di_uid), USRQUOTA, 393 (char *)0, mntpt); 394 fup->fu_curinodes++; 395 if (mode == IFREG || mode == IFDIR || 396 mode == IFLNK) 397 fup->fu_curblocks += DIP(dp, di_blocks); 398 } 399 } 400 } 401 freeinodebuf(); 402 if (qnp->flags & HASUSR) 403 errs += update(mntpt, qnp->usrqfname, USRQUOTA); 404 if (qnp->flags & HASGRP) 405 errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 406 close(fi); 407 return (errs); 408} 409 410/* 411 * Update a specified quota file. 412 */ 413int 414update(fsname, quotafile, type) 415 char *fsname, *quotafile; 416 int type; 417{ 418 struct fileusage *fup; 419 FILE *qfi, *qfo; 420 u_long id, lastid, highid = 0; 421 off_t offset; 422 int i; 423 struct dqblk dqbuf; 424 struct stat sb; 425 static int warned = 0; 426 static struct dqblk zerodqbuf; 427 static struct fileusage zerofileusage; 428 429 if ((qfo = fopen(quotafile, "r+")) == NULL) { 430 if (errno == ENOENT) 431 qfo = fopen(quotafile, "w+"); 432 if (qfo) { 433 warnx("creating quota file %s", quotafile); 434#define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 435 (void) fchown(fileno(qfo), getuid(), getquotagid()); 436 (void) fchmod(fileno(qfo), MODE); 437 } else { 438 warn("%s", quotafile); 439 return (1); 440 } 441 } 442 if ((qfi = fopen(quotafile, "r")) == NULL) { 443 warn("%s", quotafile); 444 (void) fclose(qfo); 445 return (1); 446 } 447 if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 && 448 errno == EOPNOTSUPP && !warned && vflag) { 449 warned++; 450 (void)printf("*** Warning: %s\n", 451 "Quotas are not compiled into this kernel"); 452 } 453 if (fstat(fileno(qfi), &sb) < 0) { 454 warn("Cannot fstat quota file %s\n", quotafile); 455 (void) fclose(qfo); 456 (void) fclose(qfi); 457 return (1); 458 } 459 if ((sb.st_size % sizeof(struct dqblk)) != 0) 460 warn("%s size is not a multiple of dqblk\n", quotafile); 461 462 /* 463 * Scan the on-disk quota file and record any usage changes. 464 */ 465 466 if (sb.st_size != 0) 467 lastid = (sb.st_size / sizeof(struct dqblk)) - 1; 468 else 469 lastid = 0; 470 for (id = 0, offset = 0; id <= lastid; 471 id++, offset += sizeof(struct dqblk)) { 472 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 473 dqbuf = zerodqbuf; 474 if ((fup = lookup(id, type)) == NULL) 475 fup = &zerofileusage; 476 if (fup->fu_curinodes || fup->fu_curblocks || 477 dqbuf.dqb_bsoftlimit || dqbuf.dqb_bhardlimit || 478 dqbuf.dqb_isoftlimit || dqbuf.dqb_ihardlimit) 479 highid = id; 480 if (dqbuf.dqb_curinodes == fup->fu_curinodes && 481 dqbuf.dqb_curblocks == fup->fu_curblocks) { 482 fup->fu_curinodes = 0; 483 fup->fu_curblocks = 0; 484 continue; 485 } 486 printchanges(fsname, type, &dqbuf, fup, id); 487 /* 488 * Reset time limit if have a soft limit and were 489 * previously under it, but are now over it. 490 */ 491 if (dqbuf.dqb_bsoftlimit && 492 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 493 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 494 dqbuf.dqb_btime = 0; 495 if (dqbuf.dqb_isoftlimit && 496 dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 497 fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 498 dqbuf.dqb_itime = 0; 499 dqbuf.dqb_curinodes = fup->fu_curinodes; 500 dqbuf.dqb_curblocks = fup->fu_curblocks; 501 if (fseeko(qfo, offset, SEEK_SET) < 0) { 502 warn("%s: seek failed", quotafile); 503 return(1); 504 } 505 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 506 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 507 (caddr_t)&dqbuf); 508 fup->fu_curinodes = 0; 509 fup->fu_curblocks = 0; 510 } 511 512 /* 513 * Walk the hash table looking for ids with non-zero usage 514 * that are not currently recorded in the quota file. E.g. 515 * ids that are past the end of the current file. 516 */ 517 518 for (i = 0; i < FUHASH; i++) { 519 for (fup = fuhead[type][i]; fup != NULL; fup = fup->fu_next) { 520 if (fup->fu_id <= lastid) 521 continue; 522 if (fup->fu_curinodes == 0 && fup->fu_curblocks == 0) 523 continue; 524 bzero(&dqbuf, sizeof(struct dqblk)); 525 if (fup->fu_id > highid) 526 highid = fup->fu_id; 527 printchanges(fsname, type, &dqbuf, fup, id); 528 dqbuf.dqb_curinodes = fup->fu_curinodes; 529 dqbuf.dqb_curblocks = fup->fu_curblocks; 530 offset = (off_t)fup->fu_id * sizeof(struct dqblk); 531 if (fseeko(qfo, offset, SEEK_SET) < 0) { 532 warn("%s: seek failed", quotafile); 533 return(1); 534 } 535 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 536 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 537 (caddr_t)&dqbuf); 538 fup->fu_curinodes = 0; 539 fup->fu_curblocks = 0; 540 } 541 } 542 fclose(qfi); 543 fflush(qfo); 544 ftruncate(fileno(qfo), 545 (((off_t)highid + 1) * sizeof(struct dqblk))); 546 fclose(qfo); 547 return (0); 548} 549 550/* 551 * Check to see if target appears in list of size cnt. 552 */ 553int 554oneof(target, list, cnt) 555 char *target, *list[]; 556 int cnt; 557{ 558 int i; 559 560 for (i = 0; i < cnt; i++) 561 if (strcmp(target, list[i]) == 0) 562 return (i); 563 return (-1); 564} 565 566/* 567 * Determine the group identifier for quota files. 568 */ 569int 570getquotagid() 571{ 572 struct group *gr; 573 574 if ((gr = getgrnam(quotagroup)) != NULL) 575 return (gr->gr_gid); 576 return (-1); 577} 578 579/* 580 * Check to see if a particular quota is to be enabled. 581 */ 582int 583hasquota(fs, type, qfnamep) 584 struct fstab *fs; 585 int type; 586 char **qfnamep; 587{ 588 char *opt; 589 char *cp; 590 static char initname, usrname[100], grpname[100]; 591 static char buf[BUFSIZ]; 592 593 if (!initname) { 594 (void)snprintf(usrname, sizeof(usrname), 595 "%s%s", qfextension[USRQUOTA], qfname); 596 (void)snprintf(grpname, sizeof(grpname), 597 "%s%s", qfextension[GRPQUOTA], qfname); 598 initname = 1; 599 } 600 strcpy(buf, fs->fs_mntops); 601 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 602 if ((cp = index(opt, '=')) != NULL) 603 *cp++ = '\0'; 604 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 605 break; 606 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 607 break; 608 } 609 if (!opt) 610 return (0); 611 if (cp) 612 *qfnamep = cp; 613 else { 614 (void)snprintf(buf, sizeof(buf), 615 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 616 *qfnamep = buf; 617 } 618 return (1); 619} 620 621/* 622 * Routines to manage the file usage table. 623 * 624 * Lookup an id of a specific type. 625 */ 626struct fileusage * 627lookup(id, type) 628 u_long id; 629 int type; 630{ 631 struct fileusage *fup; 632 633 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 634 if (fup->fu_id == id) 635 return (fup); 636 return (NULL); 637} 638 639/* 640 * Add a new file usage id if it does not already exist. 641 */ 642struct fileusage * 643addid(id, type, name, fsname) 644 u_long id; 645 int type; 646 char *name; 647 char *fsname; 648{ 649 struct fileusage *fup, **fhp; 650 int len; 651 652 if ((fup = lookup(id, type)) != NULL) 653 return (fup); 654 if (name) 655 len = strlen(name); 656 else 657 len = 0; 658 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 659 errx(1, "calloc failed"); 660 fhp = &fuhead[type][id & (FUHASH - 1)]; 661 fup->fu_next = *fhp; 662 *fhp = fup; 663 fup->fu_id = id; 664 if (name) 665 bcopy(name, fup->fu_name, len + 1); 666 else { 667 (void)sprintf(fup->fu_name, "%lu", id); 668 if (vflag) { 669 if (aflag && fsname != NULL) 670 (void)printf("%s: ", fsname); 671 printf("unknown %cid: %lu\n", 672 type == USRQUOTA ? 'u' : 'g', id); 673 } 674 } 675 return (fup); 676} 677 678/* 679 * Special purpose version of ginode used to optimize pass 680 * over all the inodes in numerical order. 681 */ 682static ino_t nextino, lastinum, lastvalidinum; 683static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 684static caddr_t inodebuf; 685#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 686 687union dinode * 688getnextinode(ino_t inumber) 689{ 690 long size; 691 ufs2_daddr_t dblk; 692 union dinode *dp; 693 static caddr_t nextinop; 694 695 if (inumber != nextino++ || inumber > lastvalidinum) 696 errx(1, "bad inode number %d to nextinode", inumber); 697 if (inumber >= lastinum) { 698 readcnt++; 699 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 700 if (readcnt % readpercg == 0) { 701 size = partialsize; 702 lastinum += partialcnt; 703 } else { 704 size = inobufsize; 705 lastinum += fullcnt; 706 } 707 /* 708 * If bread returns an error, it will already have zeroed 709 * out the buffer, so we do not need to do so here. 710 */ 711 bread(dblk, inodebuf, size); 712 nextinop = inodebuf; 713 } 714 dp = (union dinode *)nextinop; 715 if (sblock.fs_magic == FS_UFS1_MAGIC) 716 nextinop += sizeof(struct ufs1_dinode); 717 else 718 nextinop += sizeof(struct ufs2_dinode); 719 return (dp); 720} 721 722/* 723 * Prepare to scan a set of inodes. 724 */ 725void 726setinodebuf(ino_t inum) 727{ 728 729 if (inum % sblock.fs_ipg != 0) 730 errx(1, "bad inode number %d to setinodebuf", inum); 731 lastvalidinum = inum + sblock.fs_ipg - 1; 732 nextino = inum; 733 lastinum = inum; 734 readcnt = 0; 735 if (inodebuf != NULL) 736 return; 737 inobufsize = blkroundup(&sblock, INOBUFSIZE); 738 fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? 739 sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 740 readpercg = sblock.fs_ipg / fullcnt; 741 partialcnt = sblock.fs_ipg % fullcnt; 742 partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ? 743 sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 744 if (partialcnt != 0) { 745 readpercg++; 746 } else { 747 partialcnt = fullcnt; 748 partialsize = inobufsize; 749 } 750 if ((inodebuf = malloc((unsigned)inobufsize)) == NULL) 751 errx(1, "cannot allocate space for inode buffer"); 752} 753 754/* 755 * Free up data structures used to scan inodes. 756 */ 757void 758freeinodebuf() 759{ 760 761 if (inodebuf != NULL) 762 free(inodebuf); 763 inodebuf = NULL; 764} 765 766/* 767 * Read specified disk blocks. 768 */ 769void 770bread(bno, buf, cnt) 771 ufs2_daddr_t bno; 772 char *buf; 773 long cnt; 774{ 775 776 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || 777 read(fi, buf, cnt) != cnt) 778 errx(1, "bread failed on block %ld", (long)bno); 779} 780 781/* 782 * Display updated block and i-node counts. 783 */ 784void 785printchanges(fsname, type, dp, fup, id) 786 char *fsname; 787 int type; 788 struct dqblk *dp; 789 struct fileusage *fup; 790 u_long id; 791{ 792 if (!vflag) 793 return; 794 if (aflag) 795 (void)printf("%s: ", fsname); 796 if (fup->fu_name[0] == '\0') 797 (void)printf("%-8lu fixed ", id); 798 else 799 (void)printf("%-8s fixed ", fup->fu_name); 800 switch (type) { 801 802 case GRPQUOTA: 803 (void)printf("(group):"); 804 break; 805 806 case USRQUOTA: 807 (void)printf("(user): "); 808 break; 809 810 default: 811 (void)printf("(unknown quota type %d)", type); 812 break; 813 } 814 if (dp->dqb_curinodes != fup->fu_curinodes) 815 (void)printf("\tinodes %lu -> %lu", (u_long)dp->dqb_curinodes, 816 (u_long)fup->fu_curinodes); 817 if (dp->dqb_curblocks != fup->fu_curblocks) 818 (void)printf("\tblocks %lu -> %lu", 819 (u_long)dp->dqb_curblocks, 820 (u_long)fup->fu_curblocks); 821 (void)printf("\n"); 822} 823