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