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