122055Sdist /* 239976Smckusick * Copyright (c) 1980, 1986 The Regents of the University of California. 339976Smckusick * All rights reserved. 439976Smckusick * 542702Sbostic * %sccs.include.redist.c% 622055Sdist */ 722055Sdist 816269Smckusick #ifndef lint 9*56809Smckusick static char sccsid[] = "@(#)utilities.c 5.34 (Berkeley) 11/15/92"; 1039976Smckusick #endif /* not lint */ 1116269Smckusick 1216269Smckusick #include <sys/param.h> 1353703Smckusick #include <sys/time.h> 1451532Sbostic #include <ufs/ufs/dinode.h> 1551532Sbostic #include <ufs/ufs/dir.h> 1651532Sbostic #include <ufs/ffs/fs.h> 1739165Sbostic #include <stdio.h> 1844934Smckusick #include <stdlib.h> 1944934Smckusick #include <string.h> 2039165Sbostic #include <ctype.h> 2116269Smckusick #include "fsck.h" 2216269Smckusick 2334225Smckusick long diskreads, totalreads; /* Disk cache statistics */ 2416269Smckusick long lseek(); 2516269Smckusick 2616269Smckusick ftypeok(dp) 2739973Smckusick struct dinode *dp; 2816269Smckusick { 2916269Smckusick switch (dp->di_mode & IFMT) { 3016269Smckusick 3116269Smckusick case IFDIR: 3216269Smckusick case IFREG: 3316269Smckusick case IFBLK: 3416269Smckusick case IFCHR: 3516269Smckusick case IFLNK: 3616269Smckusick case IFSOCK: 3742943Smckusick case IFIFO: 3816269Smckusick return (1); 3916269Smckusick 4016269Smckusick default: 4116269Smckusick if (debug) 4216269Smckusick printf("bad file type 0%o\n", dp->di_mode); 4316269Smckusick return (0); 4416269Smckusick } 4516269Smckusick } 4616269Smckusick 4739975Smckusick reply(question) 4839975Smckusick char *question; 4916269Smckusick { 5039975Smckusick int persevere; 5139975Smckusick char c; 5216269Smckusick 5316269Smckusick if (preen) 5416269Smckusick pfatal("INTERNAL ERROR: GOT TO reply()"); 5539975Smckusick persevere = !strcmp(question, "CONTINUE"); 5639975Smckusick printf("\n"); 5739975Smckusick if (!persevere && (nflag || fswritefd < 0)) { 5839975Smckusick printf("%s? no\n\n", question); 5916269Smckusick return (0); 6016269Smckusick } 6139975Smckusick if (yflag || (persevere && nflag)) { 6239975Smckusick printf("%s? yes\n\n", question); 6316269Smckusick return (1); 6416269Smckusick } 6539975Smckusick do { 6639975Smckusick printf("%s? [yn] ", question); 6739975Smckusick (void) fflush(stdout); 6839975Smckusick c = getc(stdin); 6939975Smckusick while (c != '\n' && getc(stdin) != '\n') 7039975Smckusick if (feof(stdin)) 7139975Smckusick return (0); 7239975Smckusick } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 7316269Smckusick printf("\n"); 7439975Smckusick if (c == 'y' || c == 'Y') 7516269Smckusick return (1); 7639975Smckusick return (0); 7716269Smckusick } 7816269Smckusick 7934225Smckusick /* 8034225Smckusick * Malloc buffers and set up cache. 8134225Smckusick */ 8234225Smckusick bufinit() 8334225Smckusick { 8439973Smckusick register struct bufarea *bp; 8534225Smckusick long bufcnt, i; 8634225Smckusick char *bufp; 8734225Smckusick 8840649Smckusick pbp = pdirbp = (struct bufarea *)0; 8939973Smckusick bufp = malloc((unsigned int)sblock.fs_bsize); 9034225Smckusick if (bufp == 0) 9134225Smckusick errexit("cannot allocate buffer pool\n"); 9234225Smckusick cgblk.b_un.b_buf = bufp; 9334225Smckusick initbarea(&cgblk); 9434225Smckusick bufhead.b_next = bufhead.b_prev = &bufhead; 9534225Smckusick bufcnt = MAXBUFSPACE / sblock.fs_bsize; 9634225Smckusick if (bufcnt < MINBUFS) 9734225Smckusick bufcnt = MINBUFS; 9834225Smckusick for (i = 0; i < bufcnt; i++) { 9939973Smckusick bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 10039973Smckusick bufp = malloc((unsigned int)sblock.fs_bsize); 10138377Smckusick if (bp == NULL || bufp == NULL) { 10234225Smckusick if (i >= MINBUFS) 10334225Smckusick break; 10434225Smckusick errexit("cannot allocate buffer pool\n"); 10534225Smckusick } 10634225Smckusick bp->b_un.b_buf = bufp; 10734225Smckusick bp->b_prev = &bufhead; 10834225Smckusick bp->b_next = bufhead.b_next; 10934225Smckusick bufhead.b_next->b_prev = bp; 11034225Smckusick bufhead.b_next = bp; 11134225Smckusick initbarea(bp); 11234225Smckusick } 11334482Smckusick bufhead.b_size = i; /* save number of buffers */ 11434225Smckusick } 11534225Smckusick 11634225Smckusick /* 11734225Smckusick * Manage a cache of directory blocks. 11834225Smckusick */ 11939973Smckusick struct bufarea * 12034225Smckusick getdatablk(blkno, size) 12134225Smckusick daddr_t blkno; 12234225Smckusick long size; 12334225Smckusick { 12439973Smckusick register struct bufarea *bp; 12534225Smckusick 12634225Smckusick for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 12734671Smckusick if (bp->b_bno == fsbtodb(&sblock, blkno)) 12834225Smckusick goto foundit; 12934225Smckusick for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 13034225Smckusick if ((bp->b_flags & B_INUSE) == 0) 13134225Smckusick break; 13234225Smckusick if (bp == &bufhead) 13334225Smckusick errexit("deadlocked buffer pool\n"); 13434225Smckusick getblk(bp, blkno, size); 13534225Smckusick /* fall through */ 13634225Smckusick foundit: 13734225Smckusick totalreads++; 13834225Smckusick bp->b_prev->b_next = bp->b_next; 13934225Smckusick bp->b_next->b_prev = bp->b_prev; 14034225Smckusick bp->b_prev = &bufhead; 14134225Smckusick bp->b_next = bufhead.b_next; 14234225Smckusick bufhead.b_next->b_prev = bp; 14334225Smckusick bufhead.b_next = bp; 14434225Smckusick bp->b_flags |= B_INUSE; 14534225Smckusick return (bp); 14634225Smckusick } 14734225Smckusick 14844934Smckusick void 14916269Smckusick getblk(bp, blk, size) 15039973Smckusick register struct bufarea *bp; 15116269Smckusick daddr_t blk; 15216269Smckusick long size; 15316269Smckusick { 15416269Smckusick daddr_t dblk; 15516269Smckusick 15634671Smckusick dblk = fsbtodb(&sblock, blk); 15744934Smckusick if (bp->b_bno != dblk) { 15844934Smckusick flush(fswritefd, bp); 15944934Smckusick diskreads++; 16044934Smckusick bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 16144934Smckusick bp->b_bno = dblk; 16244934Smckusick bp->b_size = size; 16344934Smckusick } 16416269Smckusick } 16516269Smckusick 16639973Smckusick flush(fd, bp) 16739973Smckusick int fd; 16839973Smckusick register struct bufarea *bp; 16916269Smckusick { 17017931Smckusick register int i, j; 17116269Smckusick 17217931Smckusick if (!bp->b_dirty) 17317931Smckusick return; 17421540Smckusick if (bp->b_errs != 0) 17530609Skarels pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 17630609Skarels (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 17730609Skarels bp->b_bno); 17816269Smckusick bp->b_dirty = 0; 17921540Smckusick bp->b_errs = 0; 18039973Smckusick bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 18117931Smckusick if (bp != &sblk) 18217931Smckusick return; 18317931Smckusick for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 18439973Smckusick bwrite(fswritefd, (char *)sblock.fs_csp[j], 18517931Smckusick fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 18617931Smckusick sblock.fs_cssize - i < sblock.fs_bsize ? 18717931Smckusick sblock.fs_cssize - i : sblock.fs_bsize); 18817931Smckusick } 18916269Smckusick } 19016269Smckusick 19139973Smckusick rwerror(mesg, blk) 19239973Smckusick char *mesg; 19316269Smckusick daddr_t blk; 19416269Smckusick { 19516269Smckusick 19616269Smckusick if (preen == 0) 19716269Smckusick printf("\n"); 19839973Smckusick pfatal("CANNOT %s: BLK %ld", mesg, blk); 19916269Smckusick if (reply("CONTINUE") == 0) 20016269Smckusick errexit("Program terminated\n"); 20116269Smckusick } 20216269Smckusick 20316269Smckusick ckfini() 20416269Smckusick { 20539973Smckusick register struct bufarea *bp, *nbp; 20634482Smckusick int cnt = 0; 20716269Smckusick 208*56809Smckusick if (fswritefd < 0) { 209*56809Smckusick (void)close(fsreadfd); 210*56809Smckusick return; 211*56809Smckusick } 21239973Smckusick flush(fswritefd, &sblk); 21330859Skarels if (havesb && sblk.b_bno != SBOFF / dev_bsize && 21430518Smckusick !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 21530556Smckusick sblk.b_bno = SBOFF / dev_bsize; 21616269Smckusick sbdirty(); 21739973Smckusick flush(fswritefd, &sblk); 21816269Smckusick } 21939973Smckusick flush(fswritefd, &cgblk); 22038342Smckusick free(cgblk.b_un.b_buf); 221*56809Smckusick for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 22234482Smckusick cnt++; 22339973Smckusick flush(fswritefd, bp); 22438342Smckusick nbp = bp->b_prev; 22538342Smckusick free(bp->b_un.b_buf); 22638342Smckusick free((char *)bp); 22734482Smckusick } 22834482Smckusick if (bufhead.b_size != cnt) 22934482Smckusick errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 23040649Smckusick pbp = pdirbp = (struct bufarea *)0; 23134225Smckusick if (debug) 23244934Smckusick printf("cache missed %ld of %ld (%d%%)\n", diskreads, 23344934Smckusick totalreads, (int)(diskreads * 100 / totalreads)); 23439973Smckusick (void)close(fsreadfd); 23539973Smckusick (void)close(fswritefd); 23616269Smckusick } 23716269Smckusick 23839973Smckusick bread(fd, buf, blk, size) 23939973Smckusick int fd; 24016269Smckusick char *buf; 24116269Smckusick daddr_t blk; 24216269Smckusick long size; 24316269Smckusick { 24421540Smckusick char *cp; 24521540Smckusick int i, errs; 24621540Smckusick 24739973Smckusick if (lseek(fd, blk * dev_bsize, 0) < 0) 24839973Smckusick rwerror("SEEK", blk); 24939973Smckusick else if (read(fd, buf, (int)size) == size) 25021540Smckusick return (0); 25139973Smckusick rwerror("READ", blk); 25239973Smckusick if (lseek(fd, blk * dev_bsize, 0) < 0) 25339973Smckusick rwerror("SEEK", blk); 25421540Smckusick errs = 0; 25544934Smckusick bzero(buf, (size_t)size); 25630609Skarels printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 25730609Skarels for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 25844999Smckusick if (read(fd, cp, (int)secsize) != secsize) { 25944934Smckusick (void)lseek(fd, blk * dev_bsize + i + secsize, 0); 26030859Skarels if (secsize != dev_bsize && dev_bsize != 1) 26144934Smckusick printf(" %ld (%ld),", 26230609Skarels (blk * dev_bsize + i) / secsize, 26330609Skarels blk + i / dev_bsize); 26430609Skarels else 26544934Smckusick printf(" %ld,", blk + i / dev_bsize); 26621540Smckusick errs++; 26721540Smckusick } 26821540Smckusick } 26921758Smckusick printf("\n"); 27021540Smckusick return (errs); 27116269Smckusick } 27216269Smckusick 27339973Smckusick bwrite(fd, buf, blk, size) 27439973Smckusick int fd; 27516269Smckusick char *buf; 27616269Smckusick daddr_t blk; 27716269Smckusick long size; 27816269Smckusick { 27921758Smckusick int i; 28021758Smckusick char *cp; 28116269Smckusick 28239973Smckusick if (fd < 0) 28321758Smckusick return; 28439973Smckusick if (lseek(fd, blk * dev_bsize, 0) < 0) 28539973Smckusick rwerror("SEEK", blk); 28639973Smckusick else if (write(fd, buf, (int)size) == size) { 28739973Smckusick fsmodified = 1; 28821758Smckusick return; 28916269Smckusick } 29039973Smckusick rwerror("WRITE", blk); 29139973Smckusick if (lseek(fd, blk * dev_bsize, 0) < 0) 29239973Smckusick rwerror("SEEK", blk); 29330609Skarels printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 29430518Smckusick for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 29544999Smckusick if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 29644934Smckusick (void)lseek(fd, blk * dev_bsize + i + dev_bsize, 0); 29744934Smckusick printf(" %ld,", blk + i / dev_bsize); 29830395Smckusick } 29921758Smckusick printf("\n"); 30021758Smckusick return; 30116269Smckusick } 30216269Smckusick 30317944Smckusick /* 30417944Smckusick * allocate a data block with the specified number of fragments 30517944Smckusick */ 30617944Smckusick allocblk(frags) 30739973Smckusick long frags; 30817944Smckusick { 30917944Smckusick register int i, j, k; 31017944Smckusick 31117944Smckusick if (frags <= 0 || frags > sblock.fs_frag) 31217944Smckusick return (0); 31339973Smckusick for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 31417944Smckusick for (j = 0; j <= sblock.fs_frag - frags; j++) { 31539973Smckusick if (testbmap(i + j)) 31617944Smckusick continue; 31717944Smckusick for (k = 1; k < frags; k++) 31839973Smckusick if (testbmap(i + j + k)) 31917944Smckusick break; 32017944Smckusick if (k < frags) { 32117944Smckusick j += k; 32217944Smckusick continue; 32317944Smckusick } 32417944Smckusick for (k = 0; k < frags; k++) 32517944Smckusick setbmap(i + j + k); 32617944Smckusick n_blks += frags; 32717944Smckusick return (i + j); 32817944Smckusick } 32917944Smckusick } 33017944Smckusick return (0); 33117944Smckusick } 33217944Smckusick 33317944Smckusick /* 33417944Smckusick * Free a previously allocated block 33517944Smckusick */ 33617944Smckusick freeblk(blkno, frags) 33717944Smckusick daddr_t blkno; 33839973Smckusick long frags; 33917944Smckusick { 34017944Smckusick struct inodesc idesc; 34117944Smckusick 34217944Smckusick idesc.id_blkno = blkno; 34317944Smckusick idesc.id_numfrags = frags; 34444934Smckusick (void)pass4check(&idesc); 34517944Smckusick } 34617944Smckusick 34717991Smckusick /* 34817991Smckusick * Find a pathname 34917991Smckusick */ 35017991Smckusick getpathname(namebuf, curdir, ino) 35117991Smckusick char *namebuf; 35217991Smckusick ino_t curdir, ino; 35317991Smckusick { 35417991Smckusick int len; 35517991Smckusick register char *cp; 35617991Smckusick struct inodesc idesc; 35744999Smckusick static int busy = 0; 35817991Smckusick extern int findname(); 35917991Smckusick 36054600Smckusick if (curdir == ino && ino == ROOTINO) { 36154600Smckusick (void)strcpy(namebuf, "/"); 36254600Smckusick return; 36354600Smckusick } 36444999Smckusick if (busy || 36544999Smckusick (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { 36644934Smckusick (void)strcpy(namebuf, "?"); 36717991Smckusick return; 36817991Smckusick } 36944999Smckusick busy = 1; 37039973Smckusick bzero((char *)&idesc, sizeof(struct inodesc)); 37117991Smckusick idesc.id_type = DATA; 37244999Smckusick idesc.id_fix = IGNORE; 37340646Smckusick cp = &namebuf[MAXPATHLEN - 1]; 37430354Smckusick *cp = '\0'; 37517991Smckusick if (curdir != ino) { 37617991Smckusick idesc.id_parent = curdir; 37717991Smckusick goto namelookup; 37817991Smckusick } 37917991Smckusick while (ino != ROOTINO) { 38017991Smckusick idesc.id_number = ino; 38117991Smckusick idesc.id_func = findino; 38217991Smckusick idesc.id_name = ".."; 38340019Smckusick if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 38417991Smckusick break; 38517991Smckusick namelookup: 38617991Smckusick idesc.id_number = idesc.id_parent; 38717991Smckusick idesc.id_parent = ino; 38817991Smckusick idesc.id_func = findname; 38917991Smckusick idesc.id_name = namebuf; 39040019Smckusick if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 39117991Smckusick break; 39217991Smckusick len = strlen(namebuf); 39317991Smckusick cp -= len; 39450572Smckusick bcopy(namebuf, cp, (size_t)len); 39550572Smckusick *--cp = '/'; 39617991Smckusick if (cp < &namebuf[MAXNAMLEN]) 39717991Smckusick break; 39817991Smckusick ino = idesc.id_number; 39917991Smckusick } 40044999Smckusick busy = 0; 40150572Smckusick if (ino != ROOTINO) 40250572Smckusick *--cp = '?'; 40344934Smckusick bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp)); 40417991Smckusick } 40517991Smckusick 40639165Sbostic void 40716269Smckusick catch() 40816269Smckusick { 40954600Smckusick if (!doinglevel2) 41054600Smckusick ckfini(); 41116269Smckusick exit(12); 41216269Smckusick } 41316269Smckusick 41416269Smckusick /* 41524680Skarels * When preening, allow a single quit to signal 41624680Skarels * a special exit after filesystem checks complete 41724680Skarels * so that reboot sequence may be interrupted. 41824680Skarels */ 41939165Sbostic void 42024680Skarels catchquit() 42124680Skarels { 42224680Skarels extern returntosingle; 42324680Skarels 42424680Skarels printf("returning to single-user after filesystem check\n"); 42524680Skarels returntosingle = 1; 42624680Skarels (void)signal(SIGQUIT, SIG_DFL); 42724680Skarels } 42824680Skarels 42924680Skarels /* 43024680Skarels * Ignore a single quit signal; wait and flush just in case. 43124680Skarels * Used by child processes in preen. 43224680Skarels */ 43339165Sbostic void 43424680Skarels voidquit() 43524680Skarels { 43624680Skarels 43724680Skarels sleep(1); 43824680Skarels (void)signal(SIGQUIT, SIG_IGN); 43924680Skarels (void)signal(SIGQUIT, SIG_DFL); 44024680Skarels } 44124680Skarels 44224680Skarels /* 44316269Smckusick * determine whether an inode should be fixed. 44416269Smckusick */ 44517931Smckusick dofix(idesc, msg) 44616269Smckusick register struct inodesc *idesc; 44717931Smckusick char *msg; 44816269Smckusick { 44916269Smckusick 45016269Smckusick switch (idesc->id_fix) { 45116269Smckusick 45216269Smckusick case DONTKNOW: 45317931Smckusick if (idesc->id_type == DATA) 45439973Smckusick direrror(idesc->id_number, msg); 45517931Smckusick else 45617931Smckusick pwarn(msg); 45717931Smckusick if (preen) { 45817931Smckusick printf(" (SALVAGED)\n"); 45917931Smckusick idesc->id_fix = FIX; 46017931Smckusick return (ALTERED); 46117931Smckusick } 46216269Smckusick if (reply("SALVAGE") == 0) { 46316269Smckusick idesc->id_fix = NOFIX; 46416269Smckusick return (0); 46516269Smckusick } 46616269Smckusick idesc->id_fix = FIX; 46716269Smckusick return (ALTERED); 46816269Smckusick 46916269Smckusick case FIX: 47016269Smckusick return (ALTERED); 47116269Smckusick 47216269Smckusick case NOFIX: 47344999Smckusick case IGNORE: 47416269Smckusick return (0); 47516269Smckusick 47616269Smckusick default: 47716269Smckusick errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 47816269Smckusick } 47916269Smckusick /* NOTREACHED */ 48016269Smckusick } 48116269Smckusick 48216269Smckusick /* VARARGS1 */ 48317931Smckusick errexit(s1, s2, s3, s4) 48416269Smckusick char *s1; 48516269Smckusick { 48616269Smckusick printf(s1, s2, s3, s4); 48716269Smckusick exit(8); 48816269Smckusick } 48916269Smckusick 49016269Smckusick /* 49139973Smckusick * An unexpected inconsistency occured. 49239973Smckusick * Die if preening, otherwise just print message and continue. 49316269Smckusick */ 49416269Smckusick /* VARARGS1 */ 49516269Smckusick pfatal(s, a1, a2, a3) 49616269Smckusick char *s; 49716269Smckusick { 49816269Smckusick 49916269Smckusick if (preen) { 50016269Smckusick printf("%s: ", devname); 50116269Smckusick printf(s, a1, a2, a3); 50216269Smckusick printf("\n"); 50317931Smckusick printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", 50417931Smckusick devname); 50517931Smckusick exit(8); 50616269Smckusick } 50716269Smckusick printf(s, a1, a2, a3); 50816269Smckusick } 50916269Smckusick 51016269Smckusick /* 51139973Smckusick * Pwarn just prints a message when not preening, 51216269Smckusick * or a warning (preceded by filename) when preening. 51316269Smckusick */ 51416269Smckusick /* VARARGS1 */ 51516269Smckusick pwarn(s, a1, a2, a3, a4, a5, a6) 51616269Smckusick char *s; 51716269Smckusick { 51816269Smckusick 51916269Smckusick if (preen) 52016269Smckusick printf("%s: ", devname); 52116269Smckusick printf(s, a1, a2, a3, a4, a5, a6); 52216269Smckusick } 52316269Smckusick 52416269Smckusick #ifndef lint 52516269Smckusick /* 52616269Smckusick * Stub for routines from kernel. 52716269Smckusick */ 52816269Smckusick panic(s) 52916269Smckusick char *s; 53016269Smckusick { 53116269Smckusick 53217931Smckusick pfatal("INTERNAL INCONSISTENCY:"); 53317931Smckusick errexit(s); 53416269Smckusick } 53516269Smckusick #endif 536