122050Sdist /* 261492Sbostic * Copyright (c) 1980, 1986, 1993 361492Sbostic * The Regents of the University of California. All rights reserved. 439976Smckusick * 542702Sbostic * %sccs.include.redist.c% 622050Sdist */ 722050Sdist 816264Smckusick #ifndef lint 9*68139Smckusick static char sccsid[] = "@(#)pass2.c 8.7 (Berkeley) 01/06/95"; 1039976Smckusick #endif /* not lint */ 1116264Smckusick 1216264Smckusick #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> 1744934Smckusick #include <stdlib.h> 1842041Sbostic #include <string.h> 1916264Smckusick #include "fsck.h" 2016264Smckusick 2140026Smckusick #define MINDIRSIZE (sizeof (struct dirtemplate)) 2216264Smckusick 2340026Smckusick int pass2check(), blksort(); 2440026Smckusick 2516264Smckusick pass2() 2616264Smckusick { 2739973Smckusick register struct dinode *dp; 2840026Smckusick register struct inoinfo **inpp, *inp; 2940026Smckusick struct inoinfo **inpend; 3040026Smckusick struct inodesc curino; 3140026Smckusick struct dinode dino; 3240026Smckusick char pathbuf[MAXPATHLEN + 1]; 3316264Smckusick 3416264Smckusick switch (statemap[ROOTINO]) { 3516264Smckusick 3616264Smckusick case USTATE: 3717966Smckusick pfatal("ROOT INODE UNALLOCATED"); 3817966Smckusick if (reply("ALLOCATE") == 0) 3917966Smckusick errexit(""); 4036827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 4117966Smckusick errexit("CANNOT ALLOCATE ROOT INODE\n"); 4217966Smckusick break; 4316264Smckusick 4417966Smckusick case DCLEAR: 4517966Smckusick pfatal("DUPS/BAD IN ROOT INODE"); 4617966Smckusick if (reply("REALLOCATE")) { 4717966Smckusick freeino(ROOTINO); 4836827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 4917966Smckusick errexit("CANNOT ALLOCATE ROOT INODE\n"); 5017966Smckusick break; 5117966Smckusick } 5217966Smckusick if (reply("CONTINUE") == 0) 5317966Smckusick errexit(""); 5417966Smckusick break; 5517966Smckusick 5616264Smckusick case FSTATE: 5717937Smckusick case FCLEAR: 5816264Smckusick pfatal("ROOT INODE NOT DIRECTORY"); 5917966Smckusick if (reply("REALLOCATE")) { 6017966Smckusick freeino(ROOTINO); 6136827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 6217966Smckusick errexit("CANNOT ALLOCATE ROOT INODE\n"); 6317966Smckusick break; 6417966Smckusick } 6517943Smckusick if (reply("FIX") == 0) 6616264Smckusick errexit(""); 6717943Smckusick dp = ginode(ROOTINO); 68*68139Smckusick dp->di_mode &= ~IFMT; 69*68139Smckusick dp->di_mode |= IFDIR; 7016264Smckusick inodirty(); 7140026Smckusick break; 7216264Smckusick 7316264Smckusick case DSTATE: 7416264Smckusick break; 7516264Smckusick 7617937Smckusick default: 7717937Smckusick errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 7816264Smckusick } 7940026Smckusick statemap[ROOTINO] = DFOUND; 8067772Smckusick if (newinofmt) { 8167772Smckusick statemap[WINO] = FSTATE; 82*68139Smckusick typemap[WINO] = DT_WHT; 8367772Smckusick } 8440026Smckusick /* 8540026Smckusick * Sort the directory list into disk block order. 8640026Smckusick */ 8744934Smckusick qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 8840026Smckusick /* 8940026Smckusick * Check the integrity of each directory. 9040026Smckusick */ 9140026Smckusick bzero((char *)&curino, sizeof(struct inodesc)); 9240026Smckusick curino.id_type = DATA; 9340026Smckusick curino.id_func = pass2check; 9440026Smckusick dp = &dino; 9540026Smckusick inpend = &inpsort[inplast]; 9640026Smckusick for (inpp = inpsort; inpp < inpend; inpp++) { 9740026Smckusick inp = *inpp; 9841136Smckusick if (inp->i_isize == 0) 9941136Smckusick continue; 10040026Smckusick if (inp->i_isize < MINDIRSIZE) { 10140026Smckusick direrror(inp->i_number, "DIRECTORY TOO SHORT"); 10245002Smckusick inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 10340026Smckusick if (reply("FIX") == 1) { 10440026Smckusick dp = ginode(inp->i_number); 10545002Smckusick dp->di_size = inp->i_isize; 10640026Smckusick inodirty(); 10740026Smckusick dp = &dino; 10840026Smckusick } 10945002Smckusick } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 11040026Smckusick getpathname(pathbuf, inp->i_number, inp->i_number); 11140026Smckusick pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", 11240026Smckusick pathbuf, inp->i_isize, DIRBLKSIZ); 11340026Smckusick if (preen) 11440026Smckusick printf(" (ADJUSTED)\n"); 11540026Smckusick inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 11640026Smckusick if (preen || reply("ADJUST") == 1) { 11740026Smckusick dp = ginode(inp->i_number); 11840026Smckusick dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); 11940026Smckusick inodirty(); 12040026Smckusick dp = &dino; 12140026Smckusick } 12240026Smckusick } 12366276Smkm bzero((char *)&dino, sizeof(struct dinode)); 124*68139Smckusick dino.di_mode = IFDIR; 12540026Smckusick dp->di_size = inp->i_isize; 12640026Smckusick bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 12744934Smckusick (size_t)inp->i_numblks); 12840026Smckusick curino.id_number = inp->i_number; 12940026Smckusick curino.id_parent = inp->i_parent; 13040026Smckusick (void)ckinode(dp, &curino); 13140026Smckusick } 13240026Smckusick /* 13340026Smckusick * Now that the parents of all directories have been found, 13440026Smckusick * make another pass to verify the value of `..' 13540026Smckusick */ 13640026Smckusick for (inpp = inpsort; inpp < inpend; inpp++) { 13740026Smckusick inp = *inpp; 13841136Smckusick if (inp->i_parent == 0 || inp->i_isize == 0) 13940026Smckusick continue; 14040026Smckusick if (statemap[inp->i_parent] == DFOUND && 14140026Smckusick statemap[inp->i_number] == DSTATE) 14240026Smckusick statemap[inp->i_number] = DFOUND; 14340026Smckusick if (inp->i_dotdot == inp->i_parent || 14440026Smckusick inp->i_dotdot == (ino_t)-1) 14540026Smckusick continue; 14640026Smckusick if (inp->i_dotdot == 0) { 14740026Smckusick inp->i_dotdot = inp->i_parent; 14840026Smckusick fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 14940026Smckusick if (reply("FIX") == 0) 15040026Smckusick continue; 15144934Smckusick (void)makeentry(inp->i_number, inp->i_parent, ".."); 15240026Smckusick lncntp[inp->i_parent]--; 15340026Smckusick continue; 15440026Smckusick } 15540026Smckusick fileerror(inp->i_parent, inp->i_number, 15654603Smckusick "BAD INODE NUMBER FOR '..'"); 15740026Smckusick if (reply("FIX") == 0) 15840026Smckusick continue; 15940026Smckusick lncntp[inp->i_dotdot]++; 16040026Smckusick lncntp[inp->i_parent]--; 16140026Smckusick inp->i_dotdot = inp->i_parent; 16240026Smckusick (void)changeino(inp->i_number, "..", inp->i_parent); 16340026Smckusick } 16440026Smckusick /* 16540026Smckusick * Mark all the directories that can be found from the root. 16640026Smckusick */ 16740026Smckusick propagate(); 16816264Smckusick } 16916264Smckusick 17016264Smckusick pass2check(idesc) 17116264Smckusick struct inodesc *idesc; 17216264Smckusick { 17339973Smckusick register struct direct *dirp = idesc->id_dirp; 17440026Smckusick register struct inoinfo *inp; 17516264Smckusick int n, entrysize, ret = 0; 17639973Smckusick struct dinode *dp; 17741136Smckusick char *errmsg; 17839973Smckusick struct direct proto; 17940026Smckusick char namebuf[MAXPATHLEN + 1]; 18040026Smckusick char pathbuf[MAXPATHLEN + 1]; 18116264Smckusick 18254603Smckusick /* 18354603Smckusick * If converting, set directory entry type. 18454603Smckusick */ 18554603Smckusick if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { 18654603Smckusick dirp->d_type = typemap[dirp->d_ino]; 18754603Smckusick ret |= ALTERED; 18854603Smckusick } 18916264Smckusick /* 19016264Smckusick * check for "." 19116264Smckusick */ 19216264Smckusick if (idesc->id_entryno != 0) 19316264Smckusick goto chk1; 19416264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 19516264Smckusick if (dirp->d_ino != idesc->id_number) { 19639973Smckusick direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 19716264Smckusick dirp->d_ino = idesc->id_number; 19816264Smckusick if (reply("FIX") == 1) 19916264Smckusick ret |= ALTERED; 20016264Smckusick } 20154603Smckusick if (newinofmt && dirp->d_type != DT_DIR) { 20254603Smckusick direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 20354603Smckusick dirp->d_type = DT_DIR; 20454603Smckusick if (reply("FIX") == 1) 20554603Smckusick ret |= ALTERED; 20654603Smckusick } 20716264Smckusick goto chk1; 20816264Smckusick } 20939973Smckusick direrror(idesc->id_number, "MISSING '.'"); 21016264Smckusick proto.d_ino = idesc->id_number; 21154603Smckusick if (newinofmt) 21254603Smckusick proto.d_type = DT_DIR; 21355074Smckusick else 21455074Smckusick proto.d_type = 0; 21516264Smckusick proto.d_namlen = 1; 21616264Smckusick (void)strcpy(proto.d_name, "."); 21767864Smckusick # if BYTE_ORDER == LITTLE_ENDIAN 21867864Smckusick if (!newinofmt) { 21967864Smckusick u_char tmp; 22067864Smckusick 22167864Smckusick tmp = proto.d_type; 22267864Smckusick proto.d_type = proto.d_namlen; 22367864Smckusick proto.d_namlen = tmp; 22467864Smckusick } 22567864Smckusick # endif 22654603Smckusick entrysize = DIRSIZ(0, &proto); 22716264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 22816264Smckusick pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 22916264Smckusick dirp->d_name); 23016264Smckusick } else if (dirp->d_reclen < entrysize) { 23116264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 23216264Smckusick } else if (dirp->d_reclen < 2 * entrysize) { 23316264Smckusick proto.d_reclen = dirp->d_reclen; 23444934Smckusick bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 23516264Smckusick if (reply("FIX") == 1) 23616264Smckusick ret |= ALTERED; 23716264Smckusick } else { 23816264Smckusick n = dirp->d_reclen - entrysize; 23916264Smckusick proto.d_reclen = entrysize; 24044934Smckusick bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 24116264Smckusick idesc->id_entryno++; 24216264Smckusick lncntp[dirp->d_ino]--; 24339973Smckusick dirp = (struct direct *)((char *)(dirp) + entrysize); 24444934Smckusick bzero((char *)dirp, (size_t)n); 24516264Smckusick dirp->d_reclen = n; 24616264Smckusick if (reply("FIX") == 1) 24716264Smckusick ret |= ALTERED; 24816264Smckusick } 24916264Smckusick chk1: 25016264Smckusick if (idesc->id_entryno > 1) 25116264Smckusick goto chk2; 25240026Smckusick inp = getinoinfo(idesc->id_number); 25340026Smckusick proto.d_ino = inp->i_parent; 25454603Smckusick if (newinofmt) 25554603Smckusick proto.d_type = DT_DIR; 25655074Smckusick else 25755074Smckusick proto.d_type = 0; 25816264Smckusick proto.d_namlen = 2; 25916264Smckusick (void)strcpy(proto.d_name, ".."); 26067864Smckusick # if BYTE_ORDER == LITTLE_ENDIAN 26167864Smckusick if (!newinofmt) { 26267864Smckusick u_char tmp; 26367864Smckusick 26467864Smckusick tmp = proto.d_type; 26567864Smckusick proto.d_type = proto.d_namlen; 26667864Smckusick proto.d_namlen = tmp; 26767864Smckusick } 26867864Smckusick # endif 26954603Smckusick entrysize = DIRSIZ(0, &proto); 27016264Smckusick if (idesc->id_entryno == 0) { 27154603Smckusick n = DIRSIZ(0, dirp); 27216264Smckusick if (dirp->d_reclen < n + entrysize) 27316264Smckusick goto chk2; 27416264Smckusick proto.d_reclen = dirp->d_reclen - n; 27516264Smckusick dirp->d_reclen = n; 27616264Smckusick idesc->id_entryno++; 27716264Smckusick lncntp[dirp->d_ino]--; 27839973Smckusick dirp = (struct direct *)((char *)(dirp) + n); 27944934Smckusick bzero((char *)dirp, (size_t)proto.d_reclen); 28040026Smckusick dirp->d_reclen = proto.d_reclen; 28116264Smckusick } 28216264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 28340026Smckusick inp->i_dotdot = dirp->d_ino; 28454603Smckusick if (newinofmt && dirp->d_type != DT_DIR) { 28554603Smckusick direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 28654603Smckusick dirp->d_type = DT_DIR; 28754603Smckusick if (reply("FIX") == 1) 28854603Smckusick ret |= ALTERED; 28954603Smckusick } 29016264Smckusick goto chk2; 29116264Smckusick } 29216264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 29340026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 29416264Smckusick pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 29516264Smckusick dirp->d_name); 29640026Smckusick inp->i_dotdot = (ino_t)-1; 29716264Smckusick } else if (dirp->d_reclen < entrysize) { 29840026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 29916264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 30040026Smckusick inp->i_dotdot = (ino_t)-1; 30140026Smckusick } else if (inp->i_parent != 0) { 30240026Smckusick /* 30340026Smckusick * We know the parent, so fix now. 30440026Smckusick */ 30540026Smckusick inp->i_dotdot = inp->i_parent; 30640026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 30716264Smckusick proto.d_reclen = dirp->d_reclen; 30844934Smckusick bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 30916264Smckusick if (reply("FIX") == 1) 31016264Smckusick ret |= ALTERED; 31116264Smckusick } 31240026Smckusick idesc->id_entryno++; 31340026Smckusick if (dirp->d_ino != 0) 31440026Smckusick lncntp[dirp->d_ino]--; 31540026Smckusick return (ret|KEEPON); 31616264Smckusick chk2: 31716264Smckusick if (dirp->d_ino == 0) 31816264Smckusick return (ret|KEEPON); 31916264Smckusick if (dirp->d_namlen <= 2 && 32016264Smckusick dirp->d_name[0] == '.' && 32116264Smckusick idesc->id_entryno >= 2) { 32216264Smckusick if (dirp->d_namlen == 1) { 32339973Smckusick direrror(idesc->id_number, "EXTRA '.' ENTRY"); 32416264Smckusick dirp->d_ino = 0; 32516264Smckusick if (reply("FIX") == 1) 32616264Smckusick ret |= ALTERED; 32716264Smckusick return (KEEPON | ret); 32816264Smckusick } 32916264Smckusick if (dirp->d_name[1] == '.') { 33039973Smckusick direrror(idesc->id_number, "EXTRA '..' ENTRY"); 33116264Smckusick dirp->d_ino = 0; 33216264Smckusick if (reply("FIX") == 1) 33316264Smckusick ret |= ALTERED; 33416264Smckusick return (KEEPON | ret); 33516264Smckusick } 33616264Smckusick } 33716264Smckusick idesc->id_entryno++; 33816264Smckusick n = 0; 33945847Smckusick if (dirp->d_ino > maxino) { 34040026Smckusick fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 34116264Smckusick n = reply("REMOVE"); 34267575Spendry } else if (newinofmt && 34367670Smckusick ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 34467670Smckusick (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 34567575Spendry fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 34667575Spendry dirp->d_ino = WINO; 34767575Spendry dirp->d_type = DT_WHT; 34867575Spendry if (reply("FIX") == 1) 34967575Spendry ret |= ALTERED; 35016264Smckusick } else { 35116264Smckusick again: 35216264Smckusick switch (statemap[dirp->d_ino]) { 35316264Smckusick case USTATE: 35440569Smckusick if (idesc->id_entryno <= 2) 35540569Smckusick break; 35640026Smckusick fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 35716264Smckusick n = reply("REMOVE"); 35816264Smckusick break; 35916264Smckusick 36017937Smckusick case DCLEAR: 36117937Smckusick case FCLEAR: 36240569Smckusick if (idesc->id_entryno <= 2) 36340569Smckusick break; 36457717Smckusick if (statemap[dirp->d_ino] == FCLEAR) 36557717Smckusick errmsg = "DUP/BAD"; 36657717Smckusick else if (!preen) 36741136Smckusick errmsg = "ZERO LENGTH DIRECTORY"; 36857717Smckusick else { 36957717Smckusick n = 1; 37057717Smckusick break; 37157717Smckusick } 37241136Smckusick fileerror(idesc->id_number, dirp->d_ino, errmsg); 37316264Smckusick if ((n = reply("REMOVE")) == 1) 37416264Smckusick break; 37517943Smckusick dp = ginode(dirp->d_ino); 37639973Smckusick statemap[dirp->d_ino] = 377*68139Smckusick (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 37830599Smckusick lncntp[dirp->d_ino] = dp->di_nlink; 37916264Smckusick goto again; 38016264Smckusick 38140026Smckusick case DSTATE: 38240026Smckusick if (statemap[idesc->id_number] == DFOUND) 38340026Smckusick statemap[dirp->d_ino] = DFOUND; 38440026Smckusick /* fall through */ 38540026Smckusick 38617937Smckusick case DFOUND: 38740026Smckusick inp = getinoinfo(dirp->d_ino); 38840026Smckusick if (inp->i_parent != 0 && idesc->id_entryno > 2) { 38940026Smckusick getpathname(pathbuf, idesc->id_number, 39040026Smckusick idesc->id_number); 39117991Smckusick getpathname(namebuf, dirp->d_ino, dirp->d_ino); 39240026Smckusick pwarn("%s %s %s\n", pathbuf, 39317991Smckusick "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 39417991Smckusick namebuf); 39517991Smckusick if (preen) 39617991Smckusick printf(" (IGNORED)\n"); 39717991Smckusick else if ((n = reply("REMOVE")) == 1) 39817991Smckusick break; 39917991Smckusick } 40040026Smckusick if (idesc->id_entryno > 2) 40140026Smckusick inp->i_parent = idesc->id_number; 40217937Smckusick /* fall through */ 40317937Smckusick 40416264Smckusick case FSTATE: 40554603Smckusick if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { 40654603Smckusick fileerror(idesc->id_number, dirp->d_ino, 40754603Smckusick "BAD TYPE VALUE"); 40854603Smckusick dirp->d_type = typemap[dirp->d_ino]; 40954603Smckusick if (reply("FIX") == 1) 41054603Smckusick ret |= ALTERED; 41154603Smckusick } 41216264Smckusick lncntp[dirp->d_ino]--; 41316264Smckusick break; 41416264Smckusick 41526480Smckusick default: 41626480Smckusick errexit("BAD STATE %d FOR INODE I=%d", 41726480Smckusick statemap[dirp->d_ino], dirp->d_ino); 41816264Smckusick } 41916264Smckusick } 42016264Smckusick if (n == 0) 42116264Smckusick return (ret|KEEPON); 42216264Smckusick dirp->d_ino = 0; 42316264Smckusick return (ret|KEEPON|ALTERED); 42416264Smckusick } 42540026Smckusick 42640026Smckusick /* 42740026Smckusick * Routine to sort disk blocks. 42840026Smckusick */ 42940026Smckusick blksort(inpp1, inpp2) 43040026Smckusick struct inoinfo **inpp1, **inpp2; 43140026Smckusick { 43240026Smckusick 43340026Smckusick return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); 43440026Smckusick } 435