1 /* $OpenBSD: utilities.c,v 1.46 2014/05/24 14:54:49 krw 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> 34 #include <sys/time.h> 35 #include <sys/types.h> 36 #include <sys/uio.h> 37 #include <ufs/ufs/dinode.h> 38 #include <ufs/ufs/dir.h> 39 #include <ufs/ffs/fs.h> 40 #include <signal.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <ctype.h> 45 #include <unistd.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 int 399 allocblk(long frags) 400 { 401 int i, j, k, cg, baseblk; 402 struct cg *cgp = &cgrp; 403 404 if (frags <= 0 || frags > sblock.fs_frag) 405 return (0); 406 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 407 for (j = 0; j <= sblock.fs_frag - frags; j++) { 408 if (testbmap(i + j)) 409 continue; 410 for (k = 1; k < frags; k++) 411 if (testbmap(i + j + k)) 412 break; 413 if (k < frags) { 414 j += k; 415 continue; 416 } 417 cg = dtog(&sblock, i + j); 418 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 419 if (!cg_chkmagic(cgp)) 420 pfatal("CG %d: BAD MAGIC NUMBER\n", cg); 421 baseblk = dtogd(&sblock, i + j); 422 423 for (k = 0; k < frags; k++) { 424 setbmap(i + j + k); 425 clrbit(cg_blksfree(cgp), baseblk + k); 426 } 427 n_blks += frags; 428 if (frags == sblock.fs_frag) 429 cgp->cg_cs.cs_nbfree--; 430 else 431 cgp->cg_cs.cs_nffree -= frags; 432 return (i + j); 433 } 434 } 435 return (0); 436 } 437 438 /* 439 * Free a previously allocated block 440 */ 441 void 442 freeblk(daddr_t blkno, long frags) 443 { 444 struct inodesc idesc; 445 446 idesc.id_blkno = blkno; 447 idesc.id_numfrags = frags; 448 (void)pass4check(&idesc); 449 } 450 451 /* 452 * Find a pathname 453 */ 454 void 455 getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino) 456 { 457 int len; 458 char *cp; 459 struct inodesc idesc; 460 static int busy = 0; 461 462 if (curdir == ino && ino == ROOTINO) { 463 (void)strlcpy(namebuf, "/", namebuflen); 464 return; 465 } 466 if (busy || 467 (GET_ISTATE(curdir) != DSTATE && GET_ISTATE(curdir) != DFOUND)) { 468 (void)strlcpy(namebuf, "?", namebuflen); 469 return; 470 } 471 busy = 1; 472 memset(&idesc, 0, sizeof(struct inodesc)); 473 idesc.id_type = DATA; 474 idesc.id_fix = IGNORE; 475 cp = &namebuf[MAXPATHLEN - 1]; 476 *cp = '\0'; 477 if (curdir != ino) { 478 idesc.id_parent = curdir; 479 goto namelookup; 480 } 481 while (ino != ROOTINO) { 482 idesc.id_number = ino; 483 idesc.id_func = findino; 484 idesc.id_name = ".."; 485 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 486 break; 487 namelookup: 488 idesc.id_number = idesc.id_parent; 489 idesc.id_parent = ino; 490 idesc.id_func = findname; 491 idesc.id_name = namebuf; 492 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 493 break; 494 len = strlen(namebuf); 495 cp -= len; 496 memcpy(cp, namebuf, (size_t)len); 497 *--cp = '/'; 498 if (cp < &namebuf[MAXNAMLEN]) 499 break; 500 ino = idesc.id_number; 501 } 502 busy = 0; 503 if (ino != ROOTINO) 504 *--cp = '?'; 505 memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 506 } 507 508 /*ARGSUSED*/ 509 void 510 catch(int signo) 511 { 512 ckfini(0); /* XXX signal race */ 513 _exit(12); 514 } 515 516 /* 517 * When preening, allow a single quit to signal 518 * a special exit after filesystem checks complete 519 * so that reboot sequence may be interrupted. 520 */ 521 /*ARGSUSED*/ 522 void 523 catchquit(int signo) 524 { 525 extern volatile sig_atomic_t returntosingle; 526 char buf[1024]; 527 528 snprintf(buf, sizeof buf, 529 "returning to single-user after filesystem check\n"); 530 write(STDOUT_FILENO, buf, strlen(buf)); 531 returntosingle = 1; 532 (void)signal(SIGQUIT, SIG_DFL); 533 } 534 535 /* 536 * Ignore a single quit signal; wait and flush just in case. 537 * Used by child processes in preen. 538 */ 539 /*ARGSUSED*/ 540 void 541 voidquit(int signo) 542 { 543 int save_errno = errno; 544 545 sleep(1); 546 (void)signal(SIGQUIT, SIG_IGN); 547 (void)signal(SIGQUIT, SIG_DFL); 548 errno = save_errno; 549 } 550 551 /* 552 * determine whether an inode should be fixed. 553 */ 554 int 555 dofix(struct inodesc *idesc, char *msg) 556 { 557 switch (idesc->id_fix) { 558 559 case DONTKNOW: 560 if (idesc->id_type == DATA) 561 direrror(idesc->id_number, msg); 562 else 563 pwarn("%s", msg); 564 if (preen) { 565 printf(" (SALVAGED)\n"); 566 idesc->id_fix = FIX; 567 return (ALTERED); 568 } 569 if (reply("SALVAGE") == 0) { 570 idesc->id_fix = NOFIX; 571 return (0); 572 } 573 idesc->id_fix = FIX; 574 return (ALTERED); 575 576 case FIX: 577 return (ALTERED); 578 579 case NOFIX: 580 case IGNORE: 581 return (0); 582 583 default: 584 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 585 } 586 /* NOTREACHED */ 587 } 588 589 int (* info_fn)(char *, size_t) = NULL; 590 char *info_filesys = "?"; 591 592 /*ARGSUSED*/ 593 void 594 catchinfo(int signo) 595 { 596 int save_errno = errno, fd; 597 struct iovec iov[4]; 598 char buf[1024]; 599 600 if (info_fn != NULL && info_fn(buf, sizeof buf)) { 601 fd = open(_PATH_TTY, O_WRONLY); 602 if (fd >= 0) { 603 iov[0].iov_base = info_filesys; 604 iov[0].iov_len = strlen(info_filesys); 605 iov[1].iov_base = ": "; 606 iov[1].iov_len = sizeof ": " - 1; 607 iov[2].iov_base = buf; 608 iov[2].iov_len = strlen(buf); 609 iov[3].iov_base = "\n"; 610 iov[3].iov_len = sizeof "\n" - 1; 611 612 writev(fd, iov, 4); 613 close(fd); 614 } 615 } 616 errno = save_errno; 617 } 618