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*68908Smckusick static char sccsid[] = "@(#)pass2.c 8.8 (Berkeley) 04/27/95"; 1039976Smckusick #endif /* not lint */ 1116264Smckusick 1216264Smckusick #include <sys/param.h> 1353703Smckusick #include <sys/time.h> 14*68908Smckusick 1551532Sbostic #include <ufs/ufs/dinode.h> 1651532Sbostic #include <ufs/ufs/dir.h> 1751532Sbostic #include <ufs/ffs/fs.h> 18*68908Smckusick 19*68908Smckusick #include <err.h> 2042041Sbostic #include <string.h> 21*68908Smckusick 2216264Smckusick #include "fsck.h" 2316264Smckusick 2440026Smckusick #define MINDIRSIZE (sizeof (struct dirtemplate)) 2516264Smckusick 26*68908Smckusick static int blksort __P((const void *, const void *)); 27*68908Smckusick static int pass2check __P((struct inodesc *)); 2840026Smckusick 29*68908Smckusick void 3016264Smckusick pass2() 3116264Smckusick { 3239973Smckusick register struct dinode *dp; 3340026Smckusick register struct inoinfo **inpp, *inp; 3440026Smckusick struct inoinfo **inpend; 3540026Smckusick struct inodesc curino; 3640026Smckusick struct dinode dino; 3740026Smckusick char pathbuf[MAXPATHLEN + 1]; 3816264Smckusick 3916264Smckusick switch (statemap[ROOTINO]) { 4016264Smckusick 4116264Smckusick case USTATE: 4217966Smckusick pfatal("ROOT INODE UNALLOCATED"); 4317966Smckusick if (reply("ALLOCATE") == 0) 44*68908Smckusick exit(EEXIT); 4536827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 46*68908Smckusick errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 4717966Smckusick break; 4816264Smckusick 4917966Smckusick case DCLEAR: 5017966Smckusick pfatal("DUPS/BAD IN ROOT INODE"); 5117966Smckusick if (reply("REALLOCATE")) { 5217966Smckusick freeino(ROOTINO); 5336827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 54*68908Smckusick errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 5517966Smckusick break; 5617966Smckusick } 5717966Smckusick if (reply("CONTINUE") == 0) 58*68908Smckusick exit(EEXIT); 5917966Smckusick break; 6017966Smckusick 6116264Smckusick case FSTATE: 6217937Smckusick case FCLEAR: 6316264Smckusick pfatal("ROOT INODE NOT DIRECTORY"); 6417966Smckusick if (reply("REALLOCATE")) { 6517966Smckusick freeino(ROOTINO); 6636827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 67*68908Smckusick errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 6817966Smckusick break; 6917966Smckusick } 7017943Smckusick if (reply("FIX") == 0) 71*68908Smckusick exit(EEXIT); 7217943Smckusick dp = ginode(ROOTINO); 7368139Smckusick dp->di_mode &= ~IFMT; 7468139Smckusick dp->di_mode |= IFDIR; 7516264Smckusick inodirty(); 7640026Smckusick break; 7716264Smckusick 7816264Smckusick case DSTATE: 7916264Smckusick break; 8016264Smckusick 8117937Smckusick default: 82*68908Smckusick errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 8316264Smckusick } 8440026Smckusick statemap[ROOTINO] = DFOUND; 8567772Smckusick if (newinofmt) { 8667772Smckusick statemap[WINO] = FSTATE; 8768139Smckusick typemap[WINO] = DT_WHT; 8867772Smckusick } 8940026Smckusick /* 9040026Smckusick * Sort the directory list into disk block order. 9140026Smckusick */ 9244934Smckusick qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 9340026Smckusick /* 9440026Smckusick * Check the integrity of each directory. 9540026Smckusick */ 9640026Smckusick bzero((char *)&curino, sizeof(struct inodesc)); 9740026Smckusick curino.id_type = DATA; 9840026Smckusick curino.id_func = pass2check; 9940026Smckusick dp = &dino; 10040026Smckusick inpend = &inpsort[inplast]; 10140026Smckusick for (inpp = inpsort; inpp < inpend; inpp++) { 10240026Smckusick inp = *inpp; 10341136Smckusick if (inp->i_isize == 0) 10441136Smckusick continue; 10540026Smckusick if (inp->i_isize < MINDIRSIZE) { 10640026Smckusick direrror(inp->i_number, "DIRECTORY TOO SHORT"); 10745002Smckusick inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 10840026Smckusick if (reply("FIX") == 1) { 10940026Smckusick dp = ginode(inp->i_number); 11045002Smckusick dp->di_size = inp->i_isize; 11140026Smckusick inodirty(); 11240026Smckusick dp = &dino; 11340026Smckusick } 11445002Smckusick } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 11540026Smckusick getpathname(pathbuf, inp->i_number, inp->i_number); 11640026Smckusick pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", 11740026Smckusick pathbuf, inp->i_isize, DIRBLKSIZ); 11840026Smckusick if (preen) 11940026Smckusick printf(" (ADJUSTED)\n"); 12040026Smckusick inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 12140026Smckusick if (preen || reply("ADJUST") == 1) { 12240026Smckusick dp = ginode(inp->i_number); 12340026Smckusick dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); 12440026Smckusick inodirty(); 12540026Smckusick dp = &dino; 12640026Smckusick } 12740026Smckusick } 12866276Smkm bzero((char *)&dino, sizeof(struct dinode)); 12968139Smckusick dino.di_mode = IFDIR; 13040026Smckusick dp->di_size = inp->i_isize; 13140026Smckusick bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 13244934Smckusick (size_t)inp->i_numblks); 13340026Smckusick curino.id_number = inp->i_number; 13440026Smckusick curino.id_parent = inp->i_parent; 13540026Smckusick (void)ckinode(dp, &curino); 13640026Smckusick } 13740026Smckusick /* 13840026Smckusick * Now that the parents of all directories have been found, 13940026Smckusick * make another pass to verify the value of `..' 14040026Smckusick */ 14140026Smckusick for (inpp = inpsort; inpp < inpend; inpp++) { 14240026Smckusick inp = *inpp; 14341136Smckusick if (inp->i_parent == 0 || inp->i_isize == 0) 14440026Smckusick continue; 14540026Smckusick if (statemap[inp->i_parent] == DFOUND && 14640026Smckusick statemap[inp->i_number] == DSTATE) 14740026Smckusick statemap[inp->i_number] = DFOUND; 14840026Smckusick if (inp->i_dotdot == inp->i_parent || 14940026Smckusick inp->i_dotdot == (ino_t)-1) 15040026Smckusick continue; 15140026Smckusick if (inp->i_dotdot == 0) { 15240026Smckusick inp->i_dotdot = inp->i_parent; 15340026Smckusick fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 15440026Smckusick if (reply("FIX") == 0) 15540026Smckusick continue; 15644934Smckusick (void)makeentry(inp->i_number, inp->i_parent, ".."); 15740026Smckusick lncntp[inp->i_parent]--; 15840026Smckusick continue; 15940026Smckusick } 16040026Smckusick fileerror(inp->i_parent, inp->i_number, 16154603Smckusick "BAD INODE NUMBER FOR '..'"); 16240026Smckusick if (reply("FIX") == 0) 16340026Smckusick continue; 16440026Smckusick lncntp[inp->i_dotdot]++; 16540026Smckusick lncntp[inp->i_parent]--; 16640026Smckusick inp->i_dotdot = inp->i_parent; 16740026Smckusick (void)changeino(inp->i_number, "..", inp->i_parent); 16840026Smckusick } 16940026Smckusick /* 17040026Smckusick * Mark all the directories that can be found from the root. 17140026Smckusick */ 17240026Smckusick propagate(); 17316264Smckusick } 17416264Smckusick 175*68908Smckusick static int 17616264Smckusick pass2check(idesc) 17716264Smckusick struct inodesc *idesc; 17816264Smckusick { 17939973Smckusick register struct direct *dirp = idesc->id_dirp; 18040026Smckusick register struct inoinfo *inp; 18116264Smckusick int n, entrysize, ret = 0; 18239973Smckusick struct dinode *dp; 18341136Smckusick char *errmsg; 18439973Smckusick struct direct proto; 18540026Smckusick char namebuf[MAXPATHLEN + 1]; 18640026Smckusick char pathbuf[MAXPATHLEN + 1]; 18716264Smckusick 18854603Smckusick /* 18954603Smckusick * If converting, set directory entry type. 19054603Smckusick */ 19154603Smckusick if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { 19254603Smckusick dirp->d_type = typemap[dirp->d_ino]; 19354603Smckusick ret |= ALTERED; 19454603Smckusick } 19516264Smckusick /* 19616264Smckusick * check for "." 19716264Smckusick */ 19816264Smckusick if (idesc->id_entryno != 0) 19916264Smckusick goto chk1; 20016264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 20116264Smckusick if (dirp->d_ino != idesc->id_number) { 20239973Smckusick direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 20316264Smckusick dirp->d_ino = idesc->id_number; 20416264Smckusick if (reply("FIX") == 1) 20516264Smckusick ret |= ALTERED; 20616264Smckusick } 20754603Smckusick if (newinofmt && dirp->d_type != DT_DIR) { 20854603Smckusick direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 20954603Smckusick dirp->d_type = DT_DIR; 21054603Smckusick if (reply("FIX") == 1) 21154603Smckusick ret |= ALTERED; 21254603Smckusick } 21316264Smckusick goto chk1; 21416264Smckusick } 21539973Smckusick direrror(idesc->id_number, "MISSING '.'"); 21616264Smckusick proto.d_ino = idesc->id_number; 21754603Smckusick if (newinofmt) 21854603Smckusick proto.d_type = DT_DIR; 21955074Smckusick else 22055074Smckusick proto.d_type = 0; 22116264Smckusick proto.d_namlen = 1; 22216264Smckusick (void)strcpy(proto.d_name, "."); 22367864Smckusick # if BYTE_ORDER == LITTLE_ENDIAN 22467864Smckusick if (!newinofmt) { 22567864Smckusick u_char tmp; 22667864Smckusick 22767864Smckusick tmp = proto.d_type; 22867864Smckusick proto.d_type = proto.d_namlen; 22967864Smckusick proto.d_namlen = tmp; 23067864Smckusick } 23167864Smckusick # endif 23254603Smckusick entrysize = DIRSIZ(0, &proto); 23316264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 23416264Smckusick pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 23516264Smckusick dirp->d_name); 23616264Smckusick } else if (dirp->d_reclen < entrysize) { 23716264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 23816264Smckusick } else if (dirp->d_reclen < 2 * entrysize) { 23916264Smckusick proto.d_reclen = dirp->d_reclen; 24044934Smckusick bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 24116264Smckusick if (reply("FIX") == 1) 24216264Smckusick ret |= ALTERED; 24316264Smckusick } else { 24416264Smckusick n = dirp->d_reclen - entrysize; 24516264Smckusick proto.d_reclen = entrysize; 24644934Smckusick bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 24716264Smckusick idesc->id_entryno++; 24816264Smckusick lncntp[dirp->d_ino]--; 24939973Smckusick dirp = (struct direct *)((char *)(dirp) + entrysize); 25044934Smckusick bzero((char *)dirp, (size_t)n); 25116264Smckusick dirp->d_reclen = n; 25216264Smckusick if (reply("FIX") == 1) 25316264Smckusick ret |= ALTERED; 25416264Smckusick } 25516264Smckusick chk1: 25616264Smckusick if (idesc->id_entryno > 1) 25716264Smckusick goto chk2; 25840026Smckusick inp = getinoinfo(idesc->id_number); 25940026Smckusick proto.d_ino = inp->i_parent; 26054603Smckusick if (newinofmt) 26154603Smckusick proto.d_type = DT_DIR; 26255074Smckusick else 26355074Smckusick proto.d_type = 0; 26416264Smckusick proto.d_namlen = 2; 26516264Smckusick (void)strcpy(proto.d_name, ".."); 26667864Smckusick # if BYTE_ORDER == LITTLE_ENDIAN 26767864Smckusick if (!newinofmt) { 26867864Smckusick u_char tmp; 26967864Smckusick 27067864Smckusick tmp = proto.d_type; 27167864Smckusick proto.d_type = proto.d_namlen; 27267864Smckusick proto.d_namlen = tmp; 27367864Smckusick } 27467864Smckusick # endif 27554603Smckusick entrysize = DIRSIZ(0, &proto); 27616264Smckusick if (idesc->id_entryno == 0) { 27754603Smckusick n = DIRSIZ(0, dirp); 27816264Smckusick if (dirp->d_reclen < n + entrysize) 27916264Smckusick goto chk2; 28016264Smckusick proto.d_reclen = dirp->d_reclen - n; 28116264Smckusick dirp->d_reclen = n; 28216264Smckusick idesc->id_entryno++; 28316264Smckusick lncntp[dirp->d_ino]--; 28439973Smckusick dirp = (struct direct *)((char *)(dirp) + n); 28544934Smckusick bzero((char *)dirp, (size_t)proto.d_reclen); 28640026Smckusick dirp->d_reclen = proto.d_reclen; 28716264Smckusick } 28816264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 28940026Smckusick inp->i_dotdot = dirp->d_ino; 29054603Smckusick if (newinofmt && dirp->d_type != DT_DIR) { 29154603Smckusick direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 29254603Smckusick dirp->d_type = DT_DIR; 29354603Smckusick if (reply("FIX") == 1) 29454603Smckusick ret |= ALTERED; 29554603Smckusick } 29616264Smckusick goto chk2; 29716264Smckusick } 29816264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 29940026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 30016264Smckusick pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 30116264Smckusick dirp->d_name); 30240026Smckusick inp->i_dotdot = (ino_t)-1; 30316264Smckusick } else if (dirp->d_reclen < entrysize) { 30440026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 30516264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 30640026Smckusick inp->i_dotdot = (ino_t)-1; 30740026Smckusick } else if (inp->i_parent != 0) { 30840026Smckusick /* 30940026Smckusick * We know the parent, so fix now. 31040026Smckusick */ 31140026Smckusick inp->i_dotdot = inp->i_parent; 31240026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 31316264Smckusick proto.d_reclen = dirp->d_reclen; 31444934Smckusick bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 31516264Smckusick if (reply("FIX") == 1) 31616264Smckusick ret |= ALTERED; 31716264Smckusick } 31840026Smckusick idesc->id_entryno++; 31940026Smckusick if (dirp->d_ino != 0) 32040026Smckusick lncntp[dirp->d_ino]--; 32140026Smckusick return (ret|KEEPON); 32216264Smckusick chk2: 32316264Smckusick if (dirp->d_ino == 0) 32416264Smckusick return (ret|KEEPON); 32516264Smckusick if (dirp->d_namlen <= 2 && 32616264Smckusick dirp->d_name[0] == '.' && 32716264Smckusick idesc->id_entryno >= 2) { 32816264Smckusick if (dirp->d_namlen == 1) { 32939973Smckusick direrror(idesc->id_number, "EXTRA '.' ENTRY"); 33016264Smckusick dirp->d_ino = 0; 33116264Smckusick if (reply("FIX") == 1) 33216264Smckusick ret |= ALTERED; 33316264Smckusick return (KEEPON | ret); 33416264Smckusick } 33516264Smckusick if (dirp->d_name[1] == '.') { 33639973Smckusick direrror(idesc->id_number, "EXTRA '..' ENTRY"); 33716264Smckusick dirp->d_ino = 0; 33816264Smckusick if (reply("FIX") == 1) 33916264Smckusick ret |= ALTERED; 34016264Smckusick return (KEEPON | ret); 34116264Smckusick } 34216264Smckusick } 34316264Smckusick idesc->id_entryno++; 34416264Smckusick n = 0; 34545847Smckusick if (dirp->d_ino > maxino) { 34640026Smckusick fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 34716264Smckusick n = reply("REMOVE"); 34867575Spendry } else if (newinofmt && 34967670Smckusick ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 35067670Smckusick (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 35167575Spendry fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 35267575Spendry dirp->d_ino = WINO; 35367575Spendry dirp->d_type = DT_WHT; 35467575Spendry if (reply("FIX") == 1) 35567575Spendry ret |= ALTERED; 35616264Smckusick } else { 35716264Smckusick again: 35816264Smckusick switch (statemap[dirp->d_ino]) { 35916264Smckusick case USTATE: 36040569Smckusick if (idesc->id_entryno <= 2) 36140569Smckusick break; 36240026Smckusick fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 36316264Smckusick n = reply("REMOVE"); 36416264Smckusick break; 36516264Smckusick 36617937Smckusick case DCLEAR: 36717937Smckusick case FCLEAR: 36840569Smckusick if (idesc->id_entryno <= 2) 36940569Smckusick break; 37057717Smckusick if (statemap[dirp->d_ino] == FCLEAR) 37157717Smckusick errmsg = "DUP/BAD"; 37257717Smckusick else if (!preen) 37341136Smckusick errmsg = "ZERO LENGTH DIRECTORY"; 37457717Smckusick else { 37557717Smckusick n = 1; 37657717Smckusick break; 37757717Smckusick } 37841136Smckusick fileerror(idesc->id_number, dirp->d_ino, errmsg); 37916264Smckusick if ((n = reply("REMOVE")) == 1) 38016264Smckusick break; 38117943Smckusick dp = ginode(dirp->d_ino); 38239973Smckusick statemap[dirp->d_ino] = 38368139Smckusick (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 38430599Smckusick lncntp[dirp->d_ino] = dp->di_nlink; 38516264Smckusick goto again; 38616264Smckusick 38740026Smckusick case DSTATE: 38840026Smckusick if (statemap[idesc->id_number] == DFOUND) 38940026Smckusick statemap[dirp->d_ino] = DFOUND; 39040026Smckusick /* fall through */ 39140026Smckusick 39217937Smckusick case DFOUND: 39340026Smckusick inp = getinoinfo(dirp->d_ino); 39440026Smckusick if (inp->i_parent != 0 && idesc->id_entryno > 2) { 39540026Smckusick getpathname(pathbuf, idesc->id_number, 39640026Smckusick idesc->id_number); 39717991Smckusick getpathname(namebuf, dirp->d_ino, dirp->d_ino); 39840026Smckusick pwarn("%s %s %s\n", pathbuf, 39917991Smckusick "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 40017991Smckusick namebuf); 40117991Smckusick if (preen) 40217991Smckusick printf(" (IGNORED)\n"); 40317991Smckusick else if ((n = reply("REMOVE")) == 1) 40417991Smckusick break; 40517991Smckusick } 40640026Smckusick if (idesc->id_entryno > 2) 40740026Smckusick inp->i_parent = idesc->id_number; 40817937Smckusick /* fall through */ 40917937Smckusick 41016264Smckusick case FSTATE: 41154603Smckusick if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { 41254603Smckusick fileerror(idesc->id_number, dirp->d_ino, 41354603Smckusick "BAD TYPE VALUE"); 41454603Smckusick dirp->d_type = typemap[dirp->d_ino]; 41554603Smckusick if (reply("FIX") == 1) 41654603Smckusick ret |= ALTERED; 41754603Smckusick } 41816264Smckusick lncntp[dirp->d_ino]--; 41916264Smckusick break; 42016264Smckusick 42126480Smckusick default: 422*68908Smckusick errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 42326480Smckusick statemap[dirp->d_ino], dirp->d_ino); 42416264Smckusick } 42516264Smckusick } 42616264Smckusick if (n == 0) 42716264Smckusick return (ret|KEEPON); 42816264Smckusick dirp->d_ino = 0; 42916264Smckusick return (ret|KEEPON|ALTERED); 43016264Smckusick } 43140026Smckusick 43240026Smckusick /* 43340026Smckusick * Routine to sort disk blocks. 43440026Smckusick */ 435*68908Smckusick static int 436*68908Smckusick blksort(arg1, arg2) 437*68908Smckusick const void *arg1, *arg2; 43840026Smckusick { 43940026Smckusick 440*68908Smckusick return ((*(struct inoinfo **)arg1)->i_blks[0] - 441*68908Smckusick (*(struct inoinfo **)arg2)->i_blks[0]); 44240026Smckusick } 443