1 /* $OpenBSD: utilities.c,v 1.49 2015/01/20 18:22:21 deraadt Exp $ */ 2 /* $NetBSD: utilities.c,v 1.18 1996/09/27 22:45:20 christos Exp $ */ 3 4 /* 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. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> /* DEV_BSIZE isset setbit clrbit */ 34 #include <sys/time.h> 35 #include <sys/uio.h> 36 #include <ufs/ufs/dinode.h> 37 #include <ufs/ufs/dir.h> 38 #include <ufs/ffs/fs.h> 39 #include <signal.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <ctype.h> 44 #include <unistd.h> 45 #include <limits.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <paths.h> 49 50 #include "fsutil.h" 51 #include "fsck.h" 52 #include "extern.h" 53 54 long diskreads, totalreads; /* Disk cache statistics */ 55 56 static void rwerror(char *, daddr_t); 57 58 int 59 ftypeok(union dinode *dp) 60 { 61 switch (DIP(dp, di_mode) & IFMT) { 62 case IFDIR: 63 case IFREG: 64 case IFBLK: 65 case IFCHR: 66 case IFLNK: 67 case IFSOCK: 68 case IFIFO: 69 return (1); 70 default: 71 if (debug) 72 printf("bad file type 0%o\n", DIP(dp, di_mode)); 73 return (0); 74 } 75 } 76 77 int 78 reply(char *question) 79 { 80 int persevere, 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 resolved = 0; 89 return (0); 90 } 91 if (yflag || (persevere && nflag)) { 92 printf("%s? yes\n\n", question); 93 return (1); 94 } 95 96 do { 97 printf("%s? [Fyn?] ", question); 98 (void) fflush(stdout); 99 c = getc(stdin); 100 if (c == 'F') { 101 yflag = 1; 102 return (1); 103 } 104 while (c != '\n' && getc(stdin) != '\n') { 105 if (feof(stdin)) { 106 resolved = 0; 107 return (0); 108 } 109 } 110 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 111 printf("\n"); 112 if (c == 'y' || c == 'Y') 113 return (1); 114 resolved = 0; 115 return (0); 116 } 117 118 /* 119 * Look up state information for an inode. 120 */ 121 struct inostat * 122 inoinfo(ino_t inum) 123 { 124 static struct inostat unallocated = { USTATE, 0, 0 }; 125 struct inostatlist *ilp; 126 int iloff; 127 128 if (inum > maxino) 129 errexit("inoinfo: inumber %llu out of range", 130 (unsigned long long)inum); 131 ilp = &inostathead[inum / sblock.fs_ipg]; 132 iloff = inum % sblock.fs_ipg; 133 if (iloff >= ilp->il_numalloced) 134 return (&unallocated); 135 return (&ilp->il_stat[iloff]); 136 } 137 138 /* 139 * Malloc buffers and set up cache. 140 */ 141 void 142 bufinit(void) 143 { 144 struct bufarea *bp; 145 long bufcnt, i; 146 char *bufp; 147 148 pbp = pdirbp = NULL; 149 bufp = malloc((unsigned int)sblock.fs_bsize); 150 if (bufp == 0) 151 errexit("cannot allocate buffer pool\n"); 152 cgblk.b_un.b_buf = bufp; 153 initbarea(&cgblk); 154 bufhead.b_next = bufhead.b_prev = &bufhead; 155 bufcnt = MAXBUFSPACE / sblock.fs_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.fs_bsize); 161 if (bp == NULL || bufp == NULL) { 162 free(bp); 163 free(bufp); 164 if (i >= MINBUFS) 165 break; 166 errexit("cannot allocate buffer pool\n"); 167 } 168 bp->b_un.b_buf = bufp; 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 initbarea(bp); 174 } 175 bufhead.b_size = i; /* save number of buffers */ 176 } 177 178 /* 179 * Manage a cache of directory blocks. 180 */ 181 struct bufarea * 182 getdatablk(daddr_t blkno, long size) 183 { 184 struct bufarea *bp; 185 186 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 187 if (bp->b_bno == fsbtodb(&sblock, blkno)) 188 goto foundit; 189 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 190 if ((bp->b_flags & B_INUSE) == 0) 191 break; 192 if (bp == &bufhead) 193 errexit("deadlocked buffer pool\n"); 194 getblk(bp, blkno, size); 195 /* FALLTHROUGH */ 196 foundit: 197 totalreads++; 198 bp->b_prev->b_next = bp->b_next; 199 bp->b_next->b_prev = bp->b_prev; 200 bp->b_prev = &bufhead; 201 bp->b_next = bufhead.b_next; 202 bufhead.b_next->b_prev = bp; 203 bufhead.b_next = bp; 204 bp->b_flags |= B_INUSE; 205 return (bp); 206 } 207 208 void 209 getblk(struct bufarea *bp, daddr_t blk, long size) 210 { 211 daddr_t dblk; 212 213 dblk = fsbtodb(&sblock, blk); 214 if (bp->b_bno != dblk) { 215 flush(fswritefd, bp); 216 diskreads++; 217 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 218 bp->b_bno = dblk; 219 bp->b_size = size; 220 } 221 } 222 223 void 224 flush(int fd, struct bufarea *bp) 225 { 226 int i, j; 227 228 if (!bp->b_dirty) 229 return; 230 if (bp->b_errs != 0) 231 pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n", 232 (bp->b_errs == bp->b_size / DEV_BSIZE) ? "" : "PARTIALLY ", 233 (long long)bp->b_bno); 234 bp->b_dirty = 0; 235 bp->b_errs = 0; 236 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 237 if (bp != &sblk) 238 return; 239 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 240 bwrite(fswritefd, (char *)sblock.fs_csp + i, 241 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 242 sblock.fs_cssize - i < sblock.fs_bsize ? 243 sblock.fs_cssize - i : sblock.fs_bsize); 244 } 245 } 246 247 static void 248 rwerror(char *mesg, daddr_t blk) 249 { 250 251 if (preen == 0) 252 printf("\n"); 253 pfatal("CANNOT %s: BLK %lld", mesg, (long long)blk); 254 if (reply("CONTINUE") == 0) 255 errexit("Program terminated\n"); 256 } 257 258 void 259 ckfini(int markclean) 260 { 261 struct bufarea *bp, *nbp; 262 int cnt = 0; 263 sigset_t oset, nset; 264 int64_t sblockloc; 265 266 sigemptyset(&nset); 267 sigaddset(&nset, SIGINT); 268 sigprocmask(SIG_BLOCK, &nset, &oset); 269 270 if (fswritefd < 0) { 271 (void)close(fsreadfd); 272 fsreadfd = -1; 273 sigprocmask(SIG_SETMASK, &oset, NULL); 274 return; 275 } 276 if (sblock.fs_magic == FS_UFS1_MAGIC) { 277 sblockloc = SBLOCK_UFS1; 278 sblock.fs_ffs1_time = sblock.fs_time; 279 sblock.fs_ffs1_size = sblock.fs_size; 280 sblock.fs_ffs1_dsize = sblock.fs_dsize; 281 sblock.fs_ffs1_csaddr = sblock.fs_csaddr; 282 sblock.fs_ffs1_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; 283 sblock.fs_ffs1_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; 284 sblock.fs_ffs1_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; 285 sblock.fs_ffs1_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; 286 /* Force update on next mount */ 287 sblock.fs_ffs1_flags &= ~FS_FLAGS_UPDATED; 288 } else 289 sblockloc = SBLOCK_UFS2; 290 flush(fswritefd, &sblk); 291 if (havesb && sblk.b_bno != sblockloc / DEV_BSIZE && !preen && 292 reply("UPDATE STANDARD SUPERBLOCK")) { 293 sblk.b_bno = sblockloc / DEV_BSIZE; 294 sbdirty(); 295 flush(fswritefd, &sblk); 296 } 297 flush(fswritefd, &cgblk); 298 free(cgblk.b_un.b_buf); 299 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 300 cnt++; 301 flush(fswritefd, bp); 302 nbp = bp->b_prev; 303 free(bp->b_un.b_buf); 304 free(bp); 305 } 306 if (bufhead.b_size != cnt) 307 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 308 pbp = pdirbp = NULL; 309 if (markclean && (sblock.fs_clean & FS_ISCLEAN) == 0) { 310 /* 311 * Mark the file system as clean, and sync the superblock. 312 */ 313 if (preen) 314 pwarn("MARKING FILE SYSTEM CLEAN\n"); 315 else if (!reply("MARK FILE SYSTEM CLEAN")) 316 markclean = 0; 317 if (markclean) { 318 sblock.fs_clean = FS_ISCLEAN; 319 sbdirty(); 320 flush(fswritefd, &sblk); 321 } 322 } 323 if (debug) 324 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 325 totalreads, (int)(diskreads * 100 / totalreads)); 326 (void)close(fsreadfd); 327 fsreadfd = -1; 328 (void)close(fswritefd); 329 fswritefd = -1; 330 sigprocmask(SIG_SETMASK, &oset, NULL); 331 } 332 333 int 334 bread(int fd, char *buf, daddr_t blk, long size) 335 { 336 char *cp; 337 int i, errs; 338 off_t offset; 339 340 offset = blk; 341 offset *= DEV_BSIZE; 342 if (pread(fd, buf, size, offset) == size) 343 return (0); 344 rwerror("READ", blk); 345 errs = 0; 346 memset(buf, 0, (size_t)size); 347 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 348 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 349 if (pread(fd, cp, secsize, offset + i) != secsize) { 350 if (secsize != DEV_BSIZE) 351 printf(" %lld (%lld),", 352 (long long)(offset + i) / secsize, 353 (long long)blk + i / DEV_BSIZE); 354 else 355 printf(" %lld,", (long long)blk + 356 i / DEV_BSIZE); 357 errs++; 358 } 359 } 360 printf("\n"); 361 return (errs); 362 } 363 364 void 365 bwrite(int fd, char *buf, daddr_t blk, long size) 366 { 367 int i; 368 char *cp; 369 off_t offset; 370 371 if (fd < 0) 372 return; 373 offset = blk; 374 offset *= DEV_BSIZE; 375 if (pwrite(fd, buf, size, offset) == size) { 376 fsmodified = 1; 377 return; 378 } 379 rwerror("WRITE", blk); 380 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 381 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) 382 if (pwrite(fd, cp, secsize, offset + i) != secsize) { 383 if (secsize != DEV_BSIZE) 384 printf(" %lld (%lld),", 385 (long long)(offset + i) / secsize, 386 (long long)blk + i / DEV_BSIZE); 387 else 388 printf(" %lld,", (long long)blk + 389 i / DEV_BSIZE); 390 } 391 printf("\n"); 392 return; 393 } 394 395 /* 396 * allocate a data block with the specified number of fragments 397 */ 398 daddr_t 399 allocblk(int frags) 400 { 401 daddr_t i, baseblk; 402 int j, k, cg; 403 struct cg *cgp = &cgrp; 404 405 if (frags <= 0 || frags > sblock.fs_frag) 406 return (0); 407 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 408 for (j = 0; j <= sblock.fs_frag - frags; j++) { 409 if (testbmap(i + j)) 410 continue; 411 for (k = 1; k < frags; k++) 412 if (testbmap(i + j + k)) 413 break; 414 if (k < frags) { 415 j += k; 416 continue; 417 } 418 cg = dtog(&sblock, i + j); 419 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 420 if (!cg_chkmagic(cgp)) 421 pfatal("CG %d: BAD MAGIC NUMBER\n", cg); 422 baseblk = dtogd(&sblock, i + j); 423 424 for (k = 0; k < frags; k++) { 425 setbmap(i + j + k); 426 clrbit(cg_blksfree(cgp), baseblk + k); 427 } 428 n_blks += frags; 429 if (frags == sblock.fs_frag) 430 cgp->cg_cs.cs_nbfree--; 431 else 432 cgp->cg_cs.cs_nffree -= frags; 433 return (i + j); 434 } 435 } 436 return (0); 437 } 438 439 /* 440 * Free a previously allocated block 441 */ 442 void 443 freeblk(daddr_t blkno, int frags) 444 { 445 struct inodesc idesc; 446 447 idesc.id_blkno = blkno; 448 idesc.id_numfrags = frags; 449 (void)pass4check(&idesc); 450 } 451 452 /* 453 * Find a pathname 454 */ 455 void 456 getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino) 457 { 458 int len; 459 char *cp; 460 struct inodesc idesc; 461 static int busy = 0; 462 463 if (curdir == ino && ino == ROOTINO) { 464 (void)strlcpy(namebuf, "/", namebuflen); 465 return; 466 } 467 if (busy || 468 (GET_ISTATE(curdir) != DSTATE && GET_ISTATE(curdir) != DFOUND)) { 469 (void)strlcpy(namebuf, "?", namebuflen); 470 return; 471 } 472 busy = 1; 473 memset(&idesc, 0, sizeof(struct inodesc)); 474 idesc.id_type = DATA; 475 idesc.id_fix = IGNORE; 476 cp = &namebuf[PATH_MAX - 1]; 477 *cp = '\0'; 478 if (curdir != ino) { 479 idesc.id_parent = curdir; 480 goto namelookup; 481 } 482 while (ino != ROOTINO) { 483 idesc.id_number = ino; 484 idesc.id_func = findino; 485 idesc.id_name = ".."; 486 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 487 break; 488 namelookup: 489 idesc.id_number = idesc.id_parent; 490 idesc.id_parent = ino; 491 idesc.id_func = findname; 492 idesc.id_name = namebuf; 493 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 494 break; 495 len = strlen(namebuf); 496 cp -= len; 497 memcpy(cp, namebuf, (size_t)len); 498 *--cp = '/'; 499 if (cp < &namebuf[MAXNAMLEN]) 500 break; 501 ino = idesc.id_number; 502 } 503 busy = 0; 504 if (ino != ROOTINO) 505 *--cp = '?'; 506 memcpy(namebuf, cp, (size_t)(&namebuf[PATH_MAX] - cp)); 507 } 508 509 /*ARGSUSED*/ 510 void 511 catch(int signo) 512 { 513 ckfini(0); /* XXX signal race */ 514 _exit(12); 515 } 516 517 /* 518 * When preening, allow a single quit to signal 519 * a special exit after filesystem checks complete 520 * so that reboot sequence may be interrupted. 521 */ 522 /*ARGSUSED*/ 523 void 524 catchquit(int signo) 525 { 526 extern volatile sig_atomic_t returntosingle; 527 char buf[1024]; 528 529 snprintf(buf, sizeof buf, 530 "returning to single-user after filesystem check\n"); 531 write(STDOUT_FILENO, buf, strlen(buf)); 532 returntosingle = 1; 533 (void)signal(SIGQUIT, SIG_DFL); 534 } 535 536 /* 537 * Ignore a single quit signal; wait and flush just in case. 538 * Used by child processes in preen. 539 */ 540 /*ARGSUSED*/ 541 void 542 voidquit(int signo) 543 { 544 int save_errno = errno; 545 546 sleep(1); 547 (void)signal(SIGQUIT, SIG_IGN); 548 (void)signal(SIGQUIT, SIG_DFL); 549 errno = save_errno; 550 } 551 552 /* 553 * determine whether an inode should be fixed. 554 */ 555 int 556 dofix(struct inodesc *idesc, char *msg) 557 { 558 switch (idesc->id_fix) { 559 560 case DONTKNOW: 561 if (idesc->id_type == DATA) 562 direrror(idesc->id_number, msg); 563 else 564 pwarn("%s", msg); 565 if (preen) { 566 printf(" (SALVAGED)\n"); 567 idesc->id_fix = FIX; 568 return (ALTERED); 569 } 570 if (reply("SALVAGE") == 0) { 571 idesc->id_fix = NOFIX; 572 return (0); 573 } 574 idesc->id_fix = FIX; 575 return (ALTERED); 576 577 case FIX: 578 return (ALTERED); 579 580 case NOFIX: 581 case IGNORE: 582 return (0); 583 584 default: 585 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 586 } 587 /* NOTREACHED */ 588 } 589 590 int (* info_fn)(char *, size_t) = NULL; 591 char *info_filesys = "?"; 592 593 /*ARGSUSED*/ 594 void 595 catchinfo(int signo) 596 { 597 int save_errno = errno, fd; 598 struct iovec iov[4]; 599 char buf[1024]; 600 601 if (info_fn != NULL && info_fn(buf, sizeof buf)) { 602 fd = open(_PATH_TTY, O_WRONLY); 603 if (fd >= 0) { 604 iov[0].iov_base = info_filesys; 605 iov[0].iov_len = strlen(info_filesys); 606 iov[1].iov_base = ": "; 607 iov[1].iov_len = sizeof ": " - 1; 608 iov[2].iov_base = buf; 609 iov[2].iov_len = strlen(buf); 610 iov[3].iov_base = "\n"; 611 iov[3].iov_len = sizeof "\n" - 1; 612 613 writev(fd, iov, 4); 614 close(fd); 615 } 616 } 617 errno = save_errno; 618 } 619