1 /* $NetBSD: utilities.c,v 1.2 1997/09/14 14:27:31 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Manuel Bouyer. 5 * Copyright (c) 1980, 1986, 1993 6 * The Regents of the University of California. All rights reserved. 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 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93"; 41 #else 42 __RCSID("$NetBSD: utilities.c,v 1.2 1997/09/14 14:27:31 lukem Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/time.h> 48 #include <ufs/ext2fs/ext2fs_dinode.h> 49 #include <ufs/ext2fs/ext2fs_dir.h> 50 #include <ufs/ext2fs/ext2fs.h> 51 #include <ufs/ufs/dinode.h> /* for IFMT & friends */ 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <ctype.h> 56 #include <unistd.h> 57 58 #include "fsutil.h" 59 #include "fsck.h" 60 #include "extern.h" 61 62 long diskreads, totalreads; /* Disk cache statistics */ 63 64 static void rwerror __P((char *, daddr_t)); 65 66 int 67 ftypeok(dp) 68 struct ext2fs_dinode *dp; 69 { 70 switch (dp->e2di_mode & IFMT) { 71 72 case IFDIR: 73 case IFREG: 74 case IFBLK: 75 case IFCHR: 76 case IFLNK: 77 case IFSOCK: 78 case IFIFO: 79 return (1); 80 81 default: 82 if (debug) 83 printf("bad file type 0%o\n", dp->e2di_mode); 84 return (0); 85 } 86 } 87 88 int 89 reply(question) 90 char *question; 91 { 92 int persevere; 93 char c; 94 95 if (preen) 96 pfatal("INTERNAL ERROR: GOT TO reply()"); 97 persevere = !strcmp(question, "CONTINUE"); 98 printf("\n"); 99 if (!persevere && (nflag || fswritefd < 0)) { 100 printf("%s? no\n\n", question); 101 return (0); 102 } 103 if (yflag || (persevere && nflag)) { 104 printf("%s? yes\n\n", question); 105 return (1); 106 } 107 do { 108 printf("%s? [yn] ", question); 109 (void) fflush(stdout); 110 c = getc(stdin); 111 while (c != '\n' && getc(stdin) != '\n') 112 if (feof(stdin)) 113 return (0); 114 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 115 printf("\n"); 116 if (c == 'y' || c == 'Y') 117 return (1); 118 return (0); 119 } 120 121 /* 122 * Malloc buffers and set up cache. 123 */ 124 void 125 bufinit() 126 { 127 struct bufarea *bp; 128 long bufcnt, i; 129 char *bufp; 130 131 pbp = pdirbp = (struct bufarea *)0; 132 bufhead.b_next = bufhead.b_prev = &bufhead; 133 bufcnt = MAXBUFSPACE / sblock.e2fs_bsize; 134 if (bufcnt < MINBUFS) 135 bufcnt = MINBUFS; 136 for (i = 0; i < bufcnt; i++) { 137 bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 138 bufp = malloc((unsigned int)sblock.e2fs_bsize); 139 if (bp == NULL || bufp == NULL) { 140 if (i >= MINBUFS) 141 break; 142 errexit("cannot allocate buffer pool\n"); 143 } 144 bp->b_un.b_buf = bufp; 145 bp->b_prev = &bufhead; 146 bp->b_next = bufhead.b_next; 147 bufhead.b_next->b_prev = bp; 148 bufhead.b_next = bp; 149 initbarea(bp); 150 } 151 bufhead.b_size = i; /* save number of buffers */ 152 } 153 154 /* 155 * Manage a cache of directory blocks. 156 */ 157 struct bufarea * 158 getdatablk(blkno, size) 159 daddr_t blkno; 160 long size; 161 { 162 struct bufarea *bp; 163 164 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 165 if (bp->b_bno == fsbtodb(&sblock, blkno)) 166 goto foundit; 167 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 168 if ((bp->b_flags & B_INUSE) == 0) 169 break; 170 if (bp == &bufhead) 171 errexit("deadlocked buffer pool\n"); 172 getblk(bp, blkno, size); 173 /* fall through */ 174 foundit: 175 totalreads++; 176 bp->b_prev->b_next = bp->b_next; 177 bp->b_next->b_prev = bp->b_prev; 178 bp->b_prev = &bufhead; 179 bp->b_next = bufhead.b_next; 180 bufhead.b_next->b_prev = bp; 181 bufhead.b_next = bp; 182 bp->b_flags |= B_INUSE; 183 return (bp); 184 } 185 186 void 187 getblk(bp, blk, size) 188 struct bufarea *bp; 189 daddr_t blk; 190 long size; 191 { 192 daddr_t dblk; 193 194 dblk = fsbtodb(&sblock, blk); 195 if (bp->b_bno != dblk) { 196 flush(fswritefd, bp); 197 diskreads++; 198 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 199 bp->b_bno = dblk; 200 bp->b_size = size; 201 } 202 } 203 204 void 205 flush(fd, bp) 206 int fd; 207 struct bufarea *bp; 208 { 209 int i; 210 211 if (!bp->b_dirty) 212 return; 213 if (bp->b_errs != 0) 214 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 215 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 216 bp->b_bno); 217 bp->b_dirty = 0; 218 bp->b_errs = 0; 219 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 220 if (bp != &sblk) 221 return; 222 for (i = 0; i < sblock.e2fs_ngdb; i++) { 223 bwrite(fswritefd, (char *) 224 &sblock.e2fs_gd[i* sblock.e2fs_bsize / sizeof(struct ext2_gd)], 225 fsbtodb(&sblock, ((sblock.e2fs_bsize>1024)?0:1)+i+1), 226 sblock.e2fs_bsize); 227 } 228 } 229 230 static void 231 rwerror(mesg, blk) 232 char *mesg; 233 daddr_t blk; 234 { 235 236 if (preen == 0) 237 printf("\n"); 238 pfatal("CANNOT %s: BLK %d", mesg, blk); 239 if (reply("CONTINUE") == 0) 240 errexit("Program terminated\n"); 241 } 242 243 void 244 ckfini(markclean) 245 int markclean; 246 { 247 struct bufarea *bp, *nbp; 248 int cnt = 0; 249 250 if (fswritefd < 0) { 251 (void)close(fsreadfd); 252 return; 253 } 254 flush(fswritefd, &sblk); 255 if (havesb && sblk.b_bno != SBOFF / dev_bsize && 256 !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 257 sblk.b_bno = SBOFF / dev_bsize; 258 sbdirty(); 259 flush(fswritefd, &sblk); 260 } 261 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 262 cnt++; 263 flush(fswritefd, bp); 264 nbp = bp->b_prev; 265 free(bp->b_un.b_buf); 266 free((char *)bp); 267 } 268 if (bufhead.b_size != cnt) 269 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 270 pbp = pdirbp = (struct bufarea *)0; 271 if (markclean && (sblock.e2fs.e2fs_state & E2FS_ISCLEAN) == 0) { 272 /* 273 * Mark the file system as clean, and sync the superblock. 274 */ 275 if (preen) 276 pwarn("MARKING FILE SYSTEM CLEAN\n"); 277 else if (!reply("MARK FILE SYSTEM CLEAN")) 278 markclean = 0; 279 if (markclean) { 280 sblock.e2fs.e2fs_state = E2FS_ISCLEAN; 281 sbdirty(); 282 flush(fswritefd, &sblk); 283 } 284 } 285 if (debug) 286 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 287 totalreads, (int)(diskreads * 100 / totalreads)); 288 (void)close(fsreadfd); 289 (void)close(fswritefd); 290 } 291 292 int 293 bread(fd, buf, blk, size) 294 int fd; 295 char *buf; 296 daddr_t blk; 297 long size; 298 { 299 char *cp; 300 int i, errs; 301 off_t offset; 302 303 offset = blk; 304 offset *= dev_bsize; 305 if (lseek(fd, offset, 0) < 0) 306 rwerror("SEEK", blk); 307 else if (read(fd, buf, (int)size) == size) 308 return (0); 309 rwerror("READ", blk); 310 if (lseek(fd, offset, 0) < 0) 311 rwerror("SEEK", blk); 312 errs = 0; 313 memset(buf, 0, (size_t)size); 314 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 315 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 316 if (read(fd, cp, (int)secsize) != secsize) { 317 (void)lseek(fd, offset + i + secsize, 0); 318 if (secsize != dev_bsize && dev_bsize != 1) 319 printf(" %ld (%ld),", 320 (blk * dev_bsize + i) / secsize, 321 blk + i / dev_bsize); 322 else 323 printf(" %ld,", blk + i / dev_bsize); 324 errs++; 325 } 326 } 327 printf("\n"); 328 return (errs); 329 } 330 331 void 332 bwrite(fd, buf, blk, size) 333 int fd; 334 char *buf; 335 daddr_t blk; 336 long size; 337 { 338 int i; 339 char *cp; 340 off_t offset; 341 342 if (fd < 0) 343 return; 344 offset = blk; 345 offset *= dev_bsize; 346 if (lseek(fd, offset, 0) < 0) 347 rwerror("SEEK", blk); 348 else if (write(fd, buf, (int)size) == size) { 349 fsmodified = 1; 350 return; 351 } 352 rwerror("WRITE", blk); 353 if (lseek(fd, offset, 0) < 0) 354 rwerror("SEEK", blk); 355 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 356 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 357 if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 358 (void)lseek(fd, offset + i + dev_bsize, 0); 359 printf(" %ld,", blk + i / dev_bsize); 360 } 361 printf("\n"); 362 return; 363 } 364 365 /* 366 * allocate a data block 367 */ 368 int 369 allocblk() 370 { 371 int i; 372 373 for (i = 0; i < maxfsblock - 1; i++) { 374 if (testbmap(i)) 375 continue; 376 setbmap(i); 377 n_blks ++; 378 return (i); 379 } 380 return (0); 381 } 382 383 /* 384 * Free a previously allocated block 385 */ 386 void 387 freeblk(blkno) 388 daddr_t blkno; 389 { 390 struct inodesc idesc; 391 392 idesc.id_blkno = blkno; 393 idesc.id_numfrags = 1; 394 (void)pass4check(&idesc); 395 } 396 397 /* 398 * Find a pathname 399 */ 400 void 401 getpathname(namebuf, curdir, ino) 402 char *namebuf; 403 ino_t curdir, ino; 404 { 405 int len; 406 char *cp; 407 struct inodesc idesc; 408 static int busy = 0; 409 410 if (curdir == ino && ino == EXT2_ROOTINO) { 411 (void)strcpy(namebuf, "/"); 412 return; 413 } 414 if (busy || 415 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { 416 (void)strcpy(namebuf, "?"); 417 return; 418 } 419 busy = 1; 420 memset(&idesc, 0, sizeof(struct inodesc)); 421 idesc.id_type = DATA; 422 idesc.id_fix = IGNORE; 423 cp = &namebuf[MAXPATHLEN - 1]; 424 *cp = '\0'; 425 if (curdir != ino) { 426 idesc.id_parent = curdir; 427 goto namelookup; 428 } 429 while (ino != EXT2_ROOTINO) { 430 idesc.id_number = ino; 431 idesc.id_func = findino; 432 idesc.id_name = ".."; 433 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 434 break; 435 namelookup: 436 idesc.id_number = idesc.id_parent; 437 idesc.id_parent = ino; 438 idesc.id_func = findname; 439 idesc.id_name = namebuf; 440 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 441 break; 442 len = strlen(namebuf); 443 cp -= len; 444 memcpy(cp, namebuf, (size_t)len); 445 *--cp = '/'; 446 if (cp < &namebuf[EXT2FS_MAXNAMLEN]) 447 break; 448 ino = idesc.id_number; 449 } 450 busy = 0; 451 if (ino != EXT2_ROOTINO) 452 *--cp = '?'; 453 memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 454 } 455 456 void 457 catch(n) 458 int n; 459 { 460 ckfini(0); 461 exit(12); 462 } 463 464 /* 465 * When preening, allow a single quit to signal 466 * a special exit after filesystem checks complete 467 * so that reboot sequence may be interrupted. 468 */ 469 void 470 catchquit(n) 471 int n; 472 { 473 extern returntosingle; 474 475 printf("returning to single-user after filesystem check\n"); 476 returntosingle = 1; 477 (void)signal(SIGQUIT, SIG_DFL); 478 } 479 480 /* 481 * Ignore a single quit signal; wait and flush just in case. 482 * Used by child processes in preen. 483 */ 484 void 485 voidquit(n) 486 int n; 487 { 488 489 sleep(1); 490 (void)signal(SIGQUIT, SIG_IGN); 491 (void)signal(SIGQUIT, SIG_DFL); 492 } 493 494 /* 495 * determine whether an inode should be fixed. 496 */ 497 int 498 dofix(idesc, msg) 499 struct inodesc *idesc; 500 char *msg; 501 { 502 503 switch (idesc->id_fix) { 504 505 case DONTKNOW: 506 if (idesc->id_type == DATA) 507 direrror(idesc->id_number, msg); 508 else 509 pwarn(msg); 510 if (preen) { 511 printf(" (SALVAGED)\n"); 512 idesc->id_fix = FIX; 513 return (ALTERED); 514 } 515 if (reply("SALVAGE") == 0) { 516 idesc->id_fix = NOFIX; 517 return (0); 518 } 519 idesc->id_fix = FIX; 520 return (ALTERED); 521 522 case FIX: 523 return (ALTERED); 524 525 case NOFIX: 526 case IGNORE: 527 return (0); 528 529 default: 530 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 531 } 532 /* NOTREACHED */ 533 } 534