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