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