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