122055Sdist /* 222055Sdist * Copyright (c) 1980 Regents of the University of California. 322055Sdist * All rights reserved. The Berkeley software License Agreement 422055Sdist * specifies the terms and conditions for redistribution. 522055Sdist */ 622055Sdist 716269Smckusick #ifndef lint 8*34482Smckusick static char sccsid[] = "@(#)utilities.c 5.12 (Berkeley) 05/25/88"; 922055Sdist #endif not lint 1016269Smckusick 1116269Smckusick #include <stdio.h> 1216269Smckusick #include <ctype.h> 1316269Smckusick #include <sys/param.h> 1416269Smckusick #include <sys/inode.h> 1516269Smckusick #include <sys/fs.h> 1617991Smckusick #include <sys/dir.h> 1716269Smckusick #include "fsck.h" 1816269Smckusick 1934225Smckusick long diskreads, totalreads; /* Disk cache statistics */ 2016269Smckusick long lseek(); 2116269Smckusick 2216269Smckusick ftypeok(dp) 2316269Smckusick DINODE *dp; 2416269Smckusick { 2516269Smckusick switch (dp->di_mode & IFMT) { 2616269Smckusick 2716269Smckusick case IFDIR: 2816269Smckusick case IFREG: 2916269Smckusick case IFBLK: 3016269Smckusick case IFCHR: 3116269Smckusick case IFLNK: 3216269Smckusick case IFSOCK: 3316269Smckusick return (1); 3416269Smckusick 3516269Smckusick default: 3616269Smckusick if (debug) 3716269Smckusick printf("bad file type 0%o\n", dp->di_mode); 3816269Smckusick return (0); 3916269Smckusick } 4016269Smckusick } 4116269Smckusick 4216269Smckusick reply(s) 4316269Smckusick char *s; 4416269Smckusick { 4516269Smckusick char line[80]; 4630609Skarels int cont = (strcmp(s, "CONTINUE") == 0); 4716269Smckusick 4816269Smckusick if (preen) 4916269Smckusick pfatal("INTERNAL ERROR: GOT TO reply()"); 5016269Smckusick printf("\n%s? ", s); 5130609Skarels if (!cont && (nflag || dfile.wfdes < 0)) { 5216269Smckusick printf(" no\n\n"); 5316269Smckusick return (0); 5416269Smckusick } 5530609Skarels if (yflag || (cont && nflag)) { 5616269Smckusick printf(" yes\n\n"); 5716269Smckusick return (1); 5816269Smckusick } 5916269Smckusick if (getline(stdin, line, sizeof(line)) == EOF) 6016269Smckusick errexit("\n"); 6116269Smckusick printf("\n"); 6216269Smckusick if (line[0] == 'y' || line[0] == 'Y') 6316269Smckusick return (1); 6416269Smckusick else 6516269Smckusick return (0); 6616269Smckusick } 6716269Smckusick 6816269Smckusick getline(fp, loc, maxlen) 6916269Smckusick FILE *fp; 7016269Smckusick char *loc; 7116269Smckusick { 7216269Smckusick register n; 7316269Smckusick register char *p, *lastloc; 7416269Smckusick 7516269Smckusick p = loc; 7616269Smckusick lastloc = &p[maxlen-1]; 7716269Smckusick while ((n = getc(fp)) != '\n') { 7816269Smckusick if (n == EOF) 7916269Smckusick return (EOF); 8016269Smckusick if (!isspace(n) && p < lastloc) 8116269Smckusick *p++ = n; 8216269Smckusick } 8316269Smckusick *p = 0; 8416269Smckusick return (p - loc); 8516269Smckusick } 8616269Smckusick 8734225Smckusick /* 8834225Smckusick * Malloc buffers and set up cache. 8934225Smckusick */ 9034225Smckusick bufinit() 9134225Smckusick { 9234225Smckusick register BUFAREA *bp; 9334225Smckusick long bufcnt, i; 9434225Smckusick char *bufp; 9534225Smckusick 9634225Smckusick bufp = (char *)malloc(sblock.fs_bsize); 9734225Smckusick if (bufp == 0) 9834225Smckusick errexit("cannot allocate buffer pool\n"); 9934225Smckusick cgblk.b_un.b_buf = bufp; 10034225Smckusick initbarea(&cgblk); 10134225Smckusick bufhead.b_next = bufhead.b_prev = &bufhead; 10234225Smckusick bufcnt = MAXBUFSPACE / sblock.fs_bsize; 10334225Smckusick if (bufcnt < MINBUFS) 10434225Smckusick bufcnt = MINBUFS; 10534225Smckusick for (i = 0; i < bufcnt; i++) { 10634225Smckusick bp = (BUFAREA *)malloc(sizeof(BUFAREA)); 10734225Smckusick bufp = (char *)malloc(sblock.fs_bsize); 10834225Smckusick if (bp == 0 || bufp == 0) { 10934225Smckusick if (i >= MINBUFS) 11034225Smckusick break; 11134225Smckusick errexit("cannot allocate buffer pool\n"); 11234225Smckusick } 11334225Smckusick bp->b_un.b_buf = bufp; 11434225Smckusick bp->b_prev = &bufhead; 11534225Smckusick bp->b_next = bufhead.b_next; 11634225Smckusick bufhead.b_next->b_prev = bp; 11734225Smckusick bufhead.b_next = bp; 11834225Smckusick initbarea(bp); 11934225Smckusick } 120*34482Smckusick bufhead.b_size = i; /* save number of buffers */ 12134225Smckusick } 12234225Smckusick 12334225Smckusick /* 12434225Smckusick * Manage a cache of directory blocks. 12534225Smckusick */ 12616269Smckusick BUFAREA * 12734225Smckusick getdatablk(blkno, size) 12834225Smckusick daddr_t blkno; 12934225Smckusick long size; 13034225Smckusick { 13134225Smckusick register BUFAREA *bp; 13234225Smckusick 13334225Smckusick for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 13434225Smckusick if (bp->b_bno == blkno) 13534225Smckusick goto foundit; 13634225Smckusick for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 13734225Smckusick if ((bp->b_flags & B_INUSE) == 0) 13834225Smckusick break; 13934225Smckusick if (bp == &bufhead) 14034225Smckusick errexit("deadlocked buffer pool\n"); 14134225Smckusick getblk(bp, blkno, size); 14234225Smckusick /* fall through */ 14334225Smckusick foundit: 14434225Smckusick totalreads++; 14534225Smckusick bp->b_prev->b_next = bp->b_next; 14634225Smckusick bp->b_next->b_prev = bp->b_prev; 14734225Smckusick bp->b_prev = &bufhead; 14834225Smckusick bp->b_next = bufhead.b_next; 14934225Smckusick bufhead.b_next->b_prev = bp; 15034225Smckusick bufhead.b_next = bp; 15134225Smckusick bp->b_flags |= B_INUSE; 15234225Smckusick return (bp); 15334225Smckusick } 15434225Smckusick 15534225Smckusick BUFAREA * 15616269Smckusick getblk(bp, blk, size) 15716269Smckusick register BUFAREA *bp; 15816269Smckusick daddr_t blk; 15916269Smckusick long size; 16016269Smckusick { 16116269Smckusick register struct filecntl *fcp; 16216269Smckusick daddr_t dblk; 16316269Smckusick 16416269Smckusick fcp = &dfile; 16534225Smckusick if (bp->b_bno == blk) 16616269Smckusick return (bp); 16716269Smckusick flush(fcp, bp); 16834225Smckusick diskreads++; 16934225Smckusick bp->b_errs = bread(fcp, bp->b_un.b_buf, fsbtodb(&sblock, blk), size); 17034225Smckusick bp->b_bno = blk; 17121540Smckusick bp->b_size = size; 17221540Smckusick return (bp); 17316269Smckusick } 17416269Smckusick 17516269Smckusick flush(fcp, bp) 17616269Smckusick struct filecntl *fcp; 17716269Smckusick register BUFAREA *bp; 17816269Smckusick { 17917931Smckusick register int i, j; 18016269Smckusick 18117931Smckusick if (!bp->b_dirty) 18217931Smckusick return; 18321540Smckusick if (bp->b_errs != 0) 18430609Skarels pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 18530609Skarels (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 18630609Skarels bp->b_bno); 18716269Smckusick bp->b_dirty = 0; 18821540Smckusick bp->b_errs = 0; 18921758Smckusick bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 19017931Smckusick if (bp != &sblk) 19117931Smckusick return; 19217931Smckusick for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 19321758Smckusick bwrite(&dfile, (char *)sblock.fs_csp[j], 19417931Smckusick fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 19517931Smckusick sblock.fs_cssize - i < sblock.fs_bsize ? 19617931Smckusick sblock.fs_cssize - i : sblock.fs_bsize); 19717931Smckusick } 19816269Smckusick } 19916269Smckusick 20016269Smckusick rwerr(s, blk) 20116269Smckusick char *s; 20216269Smckusick daddr_t blk; 20316269Smckusick { 20416269Smckusick 20516269Smckusick if (preen == 0) 20616269Smckusick printf("\n"); 20716269Smckusick pfatal("CANNOT %s: BLK %ld", s, blk); 20816269Smckusick if (reply("CONTINUE") == 0) 20916269Smckusick errexit("Program terminated\n"); 21016269Smckusick } 21116269Smckusick 21216269Smckusick ckfini() 21316269Smckusick { 21434225Smckusick register BUFAREA *bp; 215*34482Smckusick int cnt = 0; 21616269Smckusick 21716269Smckusick flush(&dfile, &sblk); 21830859Skarels if (havesb && sblk.b_bno != SBOFF / dev_bsize && 21930518Smckusick !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 22030556Smckusick sblk.b_bno = SBOFF / dev_bsize; 22116269Smckusick sbdirty(); 22216269Smckusick flush(&dfile, &sblk); 22316269Smckusick } 22418002Smckusick flush(&dfile, &cgblk); 225*34482Smckusick for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) { 226*34482Smckusick cnt++; 22734225Smckusick flush(&dfile, bp); 228*34482Smckusick } 229*34482Smckusick if (bufhead.b_size != cnt) 230*34482Smckusick errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 23134225Smckusick if (debug) 232*34482Smckusick printf("cache missed %d of %d (%d%%)\n", diskreads, 233*34482Smckusick totalreads, diskreads * 100 / totalreads); 23416269Smckusick (void)close(dfile.rfdes); 23516269Smckusick (void)close(dfile.wfdes); 23616269Smckusick } 23716269Smckusick 23816269Smckusick bread(fcp, buf, blk, size) 23916269Smckusick register struct filecntl *fcp; 24016269Smckusick char *buf; 24116269Smckusick daddr_t blk; 24216269Smckusick long size; 24316269Smckusick { 24421540Smckusick char *cp; 24521540Smckusick int i, errs; 24621540Smckusick 24730557Smckusick if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0) 24816269Smckusick rwerr("SEEK", blk); 24916269Smckusick else if (read(fcp->rfdes, buf, (int)size) == size) 25021540Smckusick return (0); 25116269Smckusick rwerr("READ", blk); 25230557Smckusick if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0) 25321540Smckusick rwerr("SEEK", blk); 25421540Smckusick errs = 0; 25530463Smckusick bzero(buf, size); 25630609Skarels printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 25730609Skarels for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 25830609Skarels if (read(fcp->rfdes, cp, secsize) < 0) { 25930609Skarels lseek(fcp->rfdes, blk * dev_bsize + i + secsize, 0); 26030859Skarels if (secsize != dev_bsize && dev_bsize != 1) 26130609Skarels printf(" %d (%d),", 26230609Skarels (blk * dev_bsize + i) / secsize, 26330609Skarels blk + i / dev_bsize); 26430609Skarels else 26530609Skarels printf(" %d,", blk + i / dev_bsize); 26621540Smckusick errs++; 26721540Smckusick } 26821540Smckusick } 26921758Smckusick printf("\n"); 27021540Smckusick return (errs); 27116269Smckusick } 27216269Smckusick 27316269Smckusick bwrite(fcp, buf, blk, size) 27416269Smckusick register struct filecntl *fcp; 27516269Smckusick char *buf; 27616269Smckusick daddr_t blk; 27716269Smckusick long size; 27816269Smckusick { 27921758Smckusick int i; 28021758Smckusick char *cp; 28116269Smckusick 28216269Smckusick if (fcp->wfdes < 0) 28321758Smckusick return; 28430557Smckusick if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0) 28516269Smckusick rwerr("SEEK", blk); 28616269Smckusick else if (write(fcp->wfdes, buf, (int)size) == size) { 28716269Smckusick fcp->mod = 1; 28821758Smckusick return; 28916269Smckusick } 29016269Smckusick rwerr("WRITE", blk); 29130557Smckusick if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0) 29221758Smckusick rwerr("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) 29530518Smckusick if (write(fcp->wfdes, cp, dev_bsize) < 0) { 29630557Smckusick lseek(fcp->rfdes, blk * dev_bsize + i + dev_bsize, 0); 29730518Smckusick printf(" %d,", 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) 30717944Smckusick int frags; 30817944Smckusick { 30917944Smckusick register int i, j, k; 31017944Smckusick 31117944Smckusick if (frags <= 0 || frags > sblock.fs_frag) 31217944Smckusick return (0); 31317944Smckusick for (i = 0; i < fmax - sblock.fs_frag; i += sblock.fs_frag) { 31417944Smckusick for (j = 0; j <= sblock.fs_frag - frags; j++) { 31517944Smckusick if (getbmap(i + j)) 31617944Smckusick continue; 31717944Smckusick for (k = 1; k < frags; k++) 31817944Smckusick if (getbmap(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; 33817944Smckusick int frags; 33917944Smckusick { 34017944Smckusick struct inodesc idesc; 34117944Smckusick 34217944Smckusick idesc.id_blkno = blkno; 34317944Smckusick idesc.id_numfrags = frags; 34417944Smckusick 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; 35717991Smckusick extern int findname(); 35817991Smckusick 35917991Smckusick if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) { 36017991Smckusick strcpy(namebuf, "?"); 36117991Smckusick return; 36217991Smckusick } 36317991Smckusick bzero(&idesc, sizeof(struct inodesc)); 36417991Smckusick idesc.id_type = DATA; 36517991Smckusick cp = &namebuf[BUFSIZ - 1]; 36630354Smckusick *cp = '\0'; 36717991Smckusick if (curdir != ino) { 36817991Smckusick idesc.id_parent = curdir; 36917991Smckusick goto namelookup; 37017991Smckusick } 37117991Smckusick while (ino != ROOTINO) { 37217991Smckusick idesc.id_number = ino; 37317991Smckusick idesc.id_func = findino; 37417991Smckusick idesc.id_name = ".."; 37530354Smckusick if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 37617991Smckusick break; 37717991Smckusick namelookup: 37817991Smckusick idesc.id_number = idesc.id_parent; 37917991Smckusick idesc.id_parent = ino; 38017991Smckusick idesc.id_func = findname; 38117991Smckusick idesc.id_name = namebuf; 38230354Smckusick if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0) 38317991Smckusick break; 38417991Smckusick len = strlen(namebuf); 38517991Smckusick cp -= len; 38617991Smckusick if (cp < &namebuf[MAXNAMLEN]) 38717991Smckusick break; 38817991Smckusick bcopy(namebuf, cp, len); 38917991Smckusick *--cp = '/'; 39017991Smckusick ino = idesc.id_number; 39117991Smckusick } 39217991Smckusick if (ino != ROOTINO) { 39317991Smckusick strcpy(namebuf, "?"); 39417991Smckusick return; 39517991Smckusick } 39617991Smckusick bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp); 39717991Smckusick } 39817991Smckusick 39916269Smckusick catch() 40016269Smckusick { 40116269Smckusick 40216269Smckusick ckfini(); 40316269Smckusick exit(12); 40416269Smckusick } 40516269Smckusick 40616269Smckusick /* 40724680Skarels * When preening, allow a single quit to signal 40824680Skarels * a special exit after filesystem checks complete 40924680Skarels * so that reboot sequence may be interrupted. 41024680Skarels */ 41124680Skarels catchquit() 41224680Skarels { 41324680Skarels extern returntosingle; 41424680Skarels 41524680Skarels printf("returning to single-user after filesystem check\n"); 41624680Skarels returntosingle = 1; 41724680Skarels (void)signal(SIGQUIT, SIG_DFL); 41824680Skarels } 41924680Skarels 42024680Skarels /* 42124680Skarels * Ignore a single quit signal; wait and flush just in case. 42224680Skarels * Used by child processes in preen. 42324680Skarels */ 42424680Skarels voidquit() 42524680Skarels { 42624680Skarels 42724680Skarels sleep(1); 42824680Skarels (void)signal(SIGQUIT, SIG_IGN); 42924680Skarels (void)signal(SIGQUIT, SIG_DFL); 43024680Skarels } 43124680Skarels 43224680Skarels /* 43316269Smckusick * determine whether an inode should be fixed. 43416269Smckusick */ 43517931Smckusick dofix(idesc, msg) 43616269Smckusick register struct inodesc *idesc; 43717931Smckusick char *msg; 43816269Smckusick { 43916269Smckusick 44016269Smckusick switch (idesc->id_fix) { 44116269Smckusick 44216269Smckusick case DONTKNOW: 44317931Smckusick if (idesc->id_type == DATA) 44417931Smckusick direrr(idesc->id_number, msg); 44517931Smckusick else 44617931Smckusick pwarn(msg); 44717931Smckusick if (preen) { 44817931Smckusick printf(" (SALVAGED)\n"); 44917931Smckusick idesc->id_fix = FIX; 45017931Smckusick return (ALTERED); 45117931Smckusick } 45216269Smckusick if (reply("SALVAGE") == 0) { 45316269Smckusick idesc->id_fix = NOFIX; 45416269Smckusick return (0); 45516269Smckusick } 45616269Smckusick idesc->id_fix = FIX; 45716269Smckusick return (ALTERED); 45816269Smckusick 45916269Smckusick case FIX: 46016269Smckusick return (ALTERED); 46116269Smckusick 46216269Smckusick case NOFIX: 46316269Smckusick return (0); 46416269Smckusick 46516269Smckusick default: 46616269Smckusick errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 46716269Smckusick } 46816269Smckusick /* NOTREACHED */ 46916269Smckusick } 47016269Smckusick 47116269Smckusick /* VARARGS1 */ 47217931Smckusick errexit(s1, s2, s3, s4) 47316269Smckusick char *s1; 47416269Smckusick { 47516269Smckusick printf(s1, s2, s3, s4); 47616269Smckusick exit(8); 47716269Smckusick } 47816269Smckusick 47916269Smckusick /* 48016269Smckusick * An inconsistency occured which shouldn't during normal operations. 48116269Smckusick * Die if preening, otherwise just printf. 48216269Smckusick */ 48316269Smckusick /* VARARGS1 */ 48416269Smckusick pfatal(s, a1, a2, a3) 48516269Smckusick char *s; 48616269Smckusick { 48716269Smckusick 48816269Smckusick if (preen) { 48916269Smckusick printf("%s: ", devname); 49016269Smckusick printf(s, a1, a2, a3); 49116269Smckusick printf("\n"); 49217931Smckusick printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", 49317931Smckusick devname); 49417931Smckusick exit(8); 49516269Smckusick } 49616269Smckusick printf(s, a1, a2, a3); 49716269Smckusick } 49816269Smckusick 49916269Smckusick /* 50016269Smckusick * Pwarn is like printf when not preening, 50116269Smckusick * or a warning (preceded by filename) when preening. 50216269Smckusick */ 50316269Smckusick /* VARARGS1 */ 50416269Smckusick pwarn(s, a1, a2, a3, a4, a5, a6) 50516269Smckusick char *s; 50616269Smckusick { 50716269Smckusick 50816269Smckusick if (preen) 50916269Smckusick printf("%s: ", devname); 51016269Smckusick printf(s, a1, a2, a3, a4, a5, a6); 51116269Smckusick } 51216269Smckusick 51316269Smckusick #ifndef lint 51416269Smckusick /* 51516269Smckusick * Stub for routines from kernel. 51616269Smckusick */ 51716269Smckusick panic(s) 51816269Smckusick char *s; 51916269Smckusick { 52016269Smckusick 52117931Smckusick pfatal("INTERNAL INCONSISTENCY:"); 52217931Smckusick errexit(s); 52316269Smckusick } 52416269Smckusick #endif 525