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*34225Smckusick static char sccsid[] = "@(#)utilities.c 5.11 (Berkeley) 05/07/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 19*34225Smckusick 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 87*34225Smckusick /* 88*34225Smckusick * Malloc buffers and set up cache. 89*34225Smckusick */ 90*34225Smckusick bufinit() 91*34225Smckusick { 92*34225Smckusick register BUFAREA *bp; 93*34225Smckusick long bufcnt, i; 94*34225Smckusick char *bufp; 95*34225Smckusick 96*34225Smckusick bufp = (char *)malloc(sblock.fs_bsize); 97*34225Smckusick if (bufp == 0) 98*34225Smckusick errexit("cannot allocate buffer pool\n"); 99*34225Smckusick cgblk.b_un.b_buf = bufp; 100*34225Smckusick initbarea(&cgblk); 101*34225Smckusick bufhead.b_next = bufhead.b_prev = &bufhead; 102*34225Smckusick bufcnt = MAXBUFSPACE / sblock.fs_bsize; 103*34225Smckusick if (bufcnt < MINBUFS) 104*34225Smckusick bufcnt = MINBUFS; 105*34225Smckusick for (i = 0; i < bufcnt; i++) { 106*34225Smckusick bp = (BUFAREA *)malloc(sizeof(BUFAREA)); 107*34225Smckusick bufp = (char *)malloc(sblock.fs_bsize); 108*34225Smckusick if (bp == 0 || bufp == 0) { 109*34225Smckusick if (i >= MINBUFS) 110*34225Smckusick break; 111*34225Smckusick errexit("cannot allocate buffer pool\n"); 112*34225Smckusick } 113*34225Smckusick bp->b_un.b_buf = bufp; 114*34225Smckusick bp->b_prev = &bufhead; 115*34225Smckusick bp->b_next = bufhead.b_next; 116*34225Smckusick bufhead.b_next->b_prev = bp; 117*34225Smckusick bufhead.b_next = bp; 118*34225Smckusick initbarea(bp); 119*34225Smckusick } 120*34225Smckusick } 121*34225Smckusick 122*34225Smckusick /* 123*34225Smckusick * Manage a cache of directory blocks. 124*34225Smckusick */ 12516269Smckusick BUFAREA * 126*34225Smckusick getdatablk(blkno, size) 127*34225Smckusick daddr_t blkno; 128*34225Smckusick long size; 129*34225Smckusick { 130*34225Smckusick register BUFAREA *bp; 131*34225Smckusick 132*34225Smckusick for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 133*34225Smckusick if (bp->b_bno == blkno) 134*34225Smckusick goto foundit; 135*34225Smckusick for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 136*34225Smckusick if ((bp->b_flags & B_INUSE) == 0) 137*34225Smckusick break; 138*34225Smckusick if (bp == &bufhead) 139*34225Smckusick errexit("deadlocked buffer pool\n"); 140*34225Smckusick getblk(bp, blkno, size); 141*34225Smckusick /* fall through */ 142*34225Smckusick foundit: 143*34225Smckusick totalreads++; 144*34225Smckusick bp->b_prev->b_next = bp->b_next; 145*34225Smckusick bp->b_next->b_prev = bp->b_prev; 146*34225Smckusick bp->b_prev = &bufhead; 147*34225Smckusick bp->b_next = bufhead.b_next; 148*34225Smckusick bufhead.b_next->b_prev = bp; 149*34225Smckusick bufhead.b_next = bp; 150*34225Smckusick bp->b_flags |= B_INUSE; 151*34225Smckusick return (bp); 152*34225Smckusick } 153*34225Smckusick 154*34225Smckusick BUFAREA * 15516269Smckusick getblk(bp, blk, size) 15616269Smckusick register BUFAREA *bp; 15716269Smckusick daddr_t blk; 15816269Smckusick long size; 15916269Smckusick { 16016269Smckusick register struct filecntl *fcp; 16116269Smckusick daddr_t dblk; 16216269Smckusick 16316269Smckusick fcp = &dfile; 164*34225Smckusick if (bp->b_bno == blk) 16516269Smckusick return (bp); 16616269Smckusick flush(fcp, bp); 167*34225Smckusick diskreads++; 168*34225Smckusick bp->b_errs = bread(fcp, bp->b_un.b_buf, fsbtodb(&sblock, blk), size); 169*34225Smckusick bp->b_bno = blk; 17021540Smckusick bp->b_size = size; 17121540Smckusick return (bp); 17216269Smckusick } 17316269Smckusick 17416269Smckusick flush(fcp, bp) 17516269Smckusick struct filecntl *fcp; 17616269Smckusick register BUFAREA *bp; 17716269Smckusick { 17817931Smckusick register int i, j; 17916269Smckusick 18017931Smckusick if (!bp->b_dirty) 18117931Smckusick return; 18221540Smckusick if (bp->b_errs != 0) 18330609Skarels pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 18430609Skarels (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 18530609Skarels bp->b_bno); 18616269Smckusick bp->b_dirty = 0; 18721540Smckusick bp->b_errs = 0; 18821758Smckusick bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 18917931Smckusick if (bp != &sblk) 19017931Smckusick return; 19117931Smckusick for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 19221758Smckusick bwrite(&dfile, (char *)sblock.fs_csp[j], 19317931Smckusick fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 19417931Smckusick sblock.fs_cssize - i < sblock.fs_bsize ? 19517931Smckusick sblock.fs_cssize - i : sblock.fs_bsize); 19617931Smckusick } 19716269Smckusick } 19816269Smckusick 19916269Smckusick rwerr(s, blk) 20016269Smckusick char *s; 20116269Smckusick daddr_t blk; 20216269Smckusick { 20316269Smckusick 20416269Smckusick if (preen == 0) 20516269Smckusick printf("\n"); 20616269Smckusick pfatal("CANNOT %s: BLK %ld", s, blk); 20716269Smckusick if (reply("CONTINUE") == 0) 20816269Smckusick errexit("Program terminated\n"); 20916269Smckusick } 21016269Smckusick 21116269Smckusick ckfini() 21216269Smckusick { 213*34225Smckusick register BUFAREA *bp; 21416269Smckusick 21516269Smckusick flush(&dfile, &sblk); 21630859Skarels if (havesb && sblk.b_bno != SBOFF / dev_bsize && 21730518Smckusick !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 21830556Smckusick sblk.b_bno = SBOFF / dev_bsize; 21916269Smckusick sbdirty(); 22016269Smckusick flush(&dfile, &sblk); 22116269Smckusick } 22218002Smckusick flush(&dfile, &cgblk); 223*34225Smckusick for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 224*34225Smckusick flush(&dfile, bp); 225*34225Smckusick if (debug) 226*34225Smckusick printf("cache hit %d of %d (%d%%)\n", totalreads - diskreads, 227*34225Smckusick totalreads, (totalreads - diskreads) * 100 / totalreads); 22816269Smckusick (void)close(dfile.rfdes); 22916269Smckusick (void)close(dfile.wfdes); 23016269Smckusick } 23116269Smckusick 23216269Smckusick bread(fcp, buf, blk, size) 23316269Smckusick register struct filecntl *fcp; 23416269Smckusick char *buf; 23516269Smckusick daddr_t blk; 23616269Smckusick long size; 23716269Smckusick { 23821540Smckusick char *cp; 23921540Smckusick int i, errs; 24021540Smckusick 24130557Smckusick if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0) 24216269Smckusick rwerr("SEEK", blk); 24316269Smckusick else if (read(fcp->rfdes, buf, (int)size) == size) 24421540Smckusick return (0); 24516269Smckusick rwerr("READ", blk); 24630557Smckusick if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0) 24721540Smckusick rwerr("SEEK", blk); 24821540Smckusick errs = 0; 24930463Smckusick bzero(buf, size); 25030609Skarels printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 25130609Skarels for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 25230609Skarels if (read(fcp->rfdes, cp, secsize) < 0) { 25330609Skarels lseek(fcp->rfdes, blk * dev_bsize + i + secsize, 0); 25430859Skarels if (secsize != dev_bsize && dev_bsize != 1) 25530609Skarels printf(" %d (%d),", 25630609Skarels (blk * dev_bsize + i) / secsize, 25730609Skarels blk + i / dev_bsize); 25830609Skarels else 25930609Skarels printf(" %d,", blk + i / dev_bsize); 26021540Smckusick errs++; 26121540Smckusick } 26221540Smckusick } 26321758Smckusick printf("\n"); 26421540Smckusick return (errs); 26516269Smckusick } 26616269Smckusick 26716269Smckusick bwrite(fcp, buf, blk, size) 26816269Smckusick register struct filecntl *fcp; 26916269Smckusick char *buf; 27016269Smckusick daddr_t blk; 27116269Smckusick long size; 27216269Smckusick { 27321758Smckusick int i; 27421758Smckusick char *cp; 27516269Smckusick 27616269Smckusick if (fcp->wfdes < 0) 27721758Smckusick return; 27830557Smckusick if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0) 27916269Smckusick rwerr("SEEK", blk); 28016269Smckusick else if (write(fcp->wfdes, buf, (int)size) == size) { 28116269Smckusick fcp->mod = 1; 28221758Smckusick return; 28316269Smckusick } 28416269Smckusick rwerr("WRITE", blk); 28530557Smckusick if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0) 28621758Smckusick rwerr("SEEK", blk); 28730609Skarels printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 28830518Smckusick for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 28930518Smckusick if (write(fcp->wfdes, cp, dev_bsize) < 0) { 29030557Smckusick lseek(fcp->rfdes, blk * dev_bsize + i + dev_bsize, 0); 29130518Smckusick printf(" %d,", blk + i / dev_bsize); 29230395Smckusick } 29321758Smckusick printf("\n"); 29421758Smckusick return; 29516269Smckusick } 29616269Smckusick 29717944Smckusick /* 29817944Smckusick * allocate a data block with the specified number of fragments 29917944Smckusick */ 30017944Smckusick allocblk(frags) 30117944Smckusick int frags; 30217944Smckusick { 30317944Smckusick register int i, j, k; 30417944Smckusick 30517944Smckusick if (frags <= 0 || frags > sblock.fs_frag) 30617944Smckusick return (0); 30717944Smckusick for (i = 0; i < fmax - sblock.fs_frag; i += sblock.fs_frag) { 30817944Smckusick for (j = 0; j <= sblock.fs_frag - frags; j++) { 30917944Smckusick if (getbmap(i + j)) 31017944Smckusick continue; 31117944Smckusick for (k = 1; k < frags; k++) 31217944Smckusick if (getbmap(i + j + k)) 31317944Smckusick break; 31417944Smckusick if (k < frags) { 31517944Smckusick j += k; 31617944Smckusick continue; 31717944Smckusick } 31817944Smckusick for (k = 0; k < frags; k++) 31917944Smckusick setbmap(i + j + k); 32017944Smckusick n_blks += frags; 32117944Smckusick return (i + j); 32217944Smckusick } 32317944Smckusick } 32417944Smckusick return (0); 32517944Smckusick } 32617944Smckusick 32717944Smckusick /* 32817944Smckusick * Free a previously allocated block 32917944Smckusick */ 33017944Smckusick freeblk(blkno, frags) 33117944Smckusick daddr_t blkno; 33217944Smckusick int frags; 33317944Smckusick { 33417944Smckusick struct inodesc idesc; 33517944Smckusick 33617944Smckusick idesc.id_blkno = blkno; 33717944Smckusick idesc.id_numfrags = frags; 33817944Smckusick pass4check(&idesc); 33917944Smckusick } 34017944Smckusick 34117991Smckusick /* 34217991Smckusick * Find a pathname 34317991Smckusick */ 34417991Smckusick getpathname(namebuf, curdir, ino) 34517991Smckusick char *namebuf; 34617991Smckusick ino_t curdir, ino; 34717991Smckusick { 34817991Smckusick int len; 34917991Smckusick register char *cp; 35017991Smckusick struct inodesc idesc; 35117991Smckusick extern int findname(); 35217991Smckusick 35317991Smckusick if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) { 35417991Smckusick strcpy(namebuf, "?"); 35517991Smckusick return; 35617991Smckusick } 35717991Smckusick bzero(&idesc, sizeof(struct inodesc)); 35817991Smckusick idesc.id_type = DATA; 35917991Smckusick cp = &namebuf[BUFSIZ - 1]; 36030354Smckusick *cp = '\0'; 36117991Smckusick if (curdir != ino) { 36217991Smckusick idesc.id_parent = curdir; 36317991Smckusick goto namelookup; 36417991Smckusick } 36517991Smckusick while (ino != ROOTINO) { 36617991Smckusick idesc.id_number = ino; 36717991Smckusick idesc.id_func = findino; 36817991Smckusick idesc.id_name = ".."; 36930354Smckusick if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 37017991Smckusick break; 37117991Smckusick namelookup: 37217991Smckusick idesc.id_number = idesc.id_parent; 37317991Smckusick idesc.id_parent = ino; 37417991Smckusick idesc.id_func = findname; 37517991Smckusick idesc.id_name = namebuf; 37630354Smckusick if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0) 37717991Smckusick break; 37817991Smckusick len = strlen(namebuf); 37917991Smckusick cp -= len; 38017991Smckusick if (cp < &namebuf[MAXNAMLEN]) 38117991Smckusick break; 38217991Smckusick bcopy(namebuf, cp, len); 38317991Smckusick *--cp = '/'; 38417991Smckusick ino = idesc.id_number; 38517991Smckusick } 38617991Smckusick if (ino != ROOTINO) { 38717991Smckusick strcpy(namebuf, "?"); 38817991Smckusick return; 38917991Smckusick } 39017991Smckusick bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp); 39117991Smckusick } 39217991Smckusick 39316269Smckusick catch() 39416269Smckusick { 39516269Smckusick 39616269Smckusick ckfini(); 39716269Smckusick exit(12); 39816269Smckusick } 39916269Smckusick 40016269Smckusick /* 40124680Skarels * When preening, allow a single quit to signal 40224680Skarels * a special exit after filesystem checks complete 40324680Skarels * so that reboot sequence may be interrupted. 40424680Skarels */ 40524680Skarels catchquit() 40624680Skarels { 40724680Skarels extern returntosingle; 40824680Skarels 40924680Skarels printf("returning to single-user after filesystem check\n"); 41024680Skarels returntosingle = 1; 41124680Skarels (void)signal(SIGQUIT, SIG_DFL); 41224680Skarels } 41324680Skarels 41424680Skarels /* 41524680Skarels * Ignore a single quit signal; wait and flush just in case. 41624680Skarels * Used by child processes in preen. 41724680Skarels */ 41824680Skarels voidquit() 41924680Skarels { 42024680Skarels 42124680Skarels sleep(1); 42224680Skarels (void)signal(SIGQUIT, SIG_IGN); 42324680Skarels (void)signal(SIGQUIT, SIG_DFL); 42424680Skarels } 42524680Skarels 42624680Skarels /* 42716269Smckusick * determine whether an inode should be fixed. 42816269Smckusick */ 42917931Smckusick dofix(idesc, msg) 43016269Smckusick register struct inodesc *idesc; 43117931Smckusick char *msg; 43216269Smckusick { 43316269Smckusick 43416269Smckusick switch (idesc->id_fix) { 43516269Smckusick 43616269Smckusick case DONTKNOW: 43717931Smckusick if (idesc->id_type == DATA) 43817931Smckusick direrr(idesc->id_number, msg); 43917931Smckusick else 44017931Smckusick pwarn(msg); 44117931Smckusick if (preen) { 44217931Smckusick printf(" (SALVAGED)\n"); 44317931Smckusick idesc->id_fix = FIX; 44417931Smckusick return (ALTERED); 44517931Smckusick } 44616269Smckusick if (reply("SALVAGE") == 0) { 44716269Smckusick idesc->id_fix = NOFIX; 44816269Smckusick return (0); 44916269Smckusick } 45016269Smckusick idesc->id_fix = FIX; 45116269Smckusick return (ALTERED); 45216269Smckusick 45316269Smckusick case FIX: 45416269Smckusick return (ALTERED); 45516269Smckusick 45616269Smckusick case NOFIX: 45716269Smckusick return (0); 45816269Smckusick 45916269Smckusick default: 46016269Smckusick errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 46116269Smckusick } 46216269Smckusick /* NOTREACHED */ 46316269Smckusick } 46416269Smckusick 46516269Smckusick /* VARARGS1 */ 46617931Smckusick errexit(s1, s2, s3, s4) 46716269Smckusick char *s1; 46816269Smckusick { 46916269Smckusick printf(s1, s2, s3, s4); 47016269Smckusick exit(8); 47116269Smckusick } 47216269Smckusick 47316269Smckusick /* 47416269Smckusick * An inconsistency occured which shouldn't during normal operations. 47516269Smckusick * Die if preening, otherwise just printf. 47616269Smckusick */ 47716269Smckusick /* VARARGS1 */ 47816269Smckusick pfatal(s, a1, a2, a3) 47916269Smckusick char *s; 48016269Smckusick { 48116269Smckusick 48216269Smckusick if (preen) { 48316269Smckusick printf("%s: ", devname); 48416269Smckusick printf(s, a1, a2, a3); 48516269Smckusick printf("\n"); 48617931Smckusick printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", 48717931Smckusick devname); 48817931Smckusick exit(8); 48916269Smckusick } 49016269Smckusick printf(s, a1, a2, a3); 49116269Smckusick } 49216269Smckusick 49316269Smckusick /* 49416269Smckusick * Pwarn is like printf when not preening, 49516269Smckusick * or a warning (preceded by filename) when preening. 49616269Smckusick */ 49716269Smckusick /* VARARGS1 */ 49816269Smckusick pwarn(s, a1, a2, a3, a4, a5, a6) 49916269Smckusick char *s; 50016269Smckusick { 50116269Smckusick 50216269Smckusick if (preen) 50316269Smckusick printf("%s: ", devname); 50416269Smckusick printf(s, a1, a2, a3, a4, a5, a6); 50516269Smckusick } 50616269Smckusick 50716269Smckusick #ifndef lint 50816269Smckusick /* 50916269Smckusick * Stub for routines from kernel. 51016269Smckusick */ 51116269Smckusick panic(s) 51216269Smckusick char *s; 51316269Smckusick { 51416269Smckusick 51517931Smckusick pfatal("INTERNAL INCONSISTENCY:"); 51617931Smckusick errexit(s); 51716269Smckusick } 51816269Smckusick #endif 519