122050Sdist /* 239976Smckusick * Copyright (c) 1980, 1986 The Regents of the University of California. 339976Smckusick * All rights reserved. 439976Smckusick * 539976Smckusick * Redistribution and use in source and binary forms are permitted 639976Smckusick * provided that the above copyright notice and this paragraph are 739976Smckusick * duplicated in all such forms and that any documentation, 839976Smckusick * advertising materials, and other materials related to such 939976Smckusick * distribution and use acknowledge that the software was developed 1039976Smckusick * by the University of California, Berkeley. The name of the 1139976Smckusick * University may not be used to endorse or promote products derived 1239976Smckusick * from this software without specific prior written permission. 1339976Smckusick * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1439976Smckusick * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1539976Smckusick * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622050Sdist */ 1722050Sdist 1816264Smckusick #ifndef lint 19*41136Smckusick static char sccsid[] = "@(#)pass2.c 5.11 (Berkeley) 04/29/90"; 2039976Smckusick #endif /* not lint */ 2116264Smckusick 2216264Smckusick #include <sys/param.h> 2339383Smckusick #include <ufs/dinode.h> 2438337Smckusick #include <ufs/fs.h> 2540026Smckusick #define KERNEL 2638337Smckusick #include <ufs/dir.h> 2740026Smckusick #undef KERNEL 2816264Smckusick #include <strings.h> 2916264Smckusick #include "fsck.h" 3016264Smckusick 3140026Smckusick #define MINDIRSIZE (sizeof (struct dirtemplate)) 3216264Smckusick 3340026Smckusick int pass2check(), blksort(); 3440026Smckusick 3516264Smckusick pass2() 3616264Smckusick { 3739973Smckusick register struct dinode *dp; 3840026Smckusick register struct inoinfo **inpp, *inp; 3940026Smckusick struct inoinfo **inpend; 4040026Smckusick struct inodesc curino; 4140026Smckusick struct dinode dino; 4240026Smckusick char pathbuf[MAXPATHLEN + 1]; 4316264Smckusick 4416264Smckusick switch (statemap[ROOTINO]) { 4516264Smckusick 4616264Smckusick case USTATE: 4717966Smckusick pfatal("ROOT INODE UNALLOCATED"); 4817966Smckusick if (reply("ALLOCATE") == 0) 4917966Smckusick errexit(""); 5036827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 5117966Smckusick errexit("CANNOT ALLOCATE ROOT INODE\n"); 5217966Smckusick break; 5316264Smckusick 5417966Smckusick case DCLEAR: 5517966Smckusick pfatal("DUPS/BAD IN ROOT INODE"); 5617966Smckusick if (reply("REALLOCATE")) { 5717966Smckusick freeino(ROOTINO); 5836827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 5917966Smckusick errexit("CANNOT ALLOCATE ROOT INODE\n"); 6017966Smckusick break; 6117966Smckusick } 6217966Smckusick if (reply("CONTINUE") == 0) 6317966Smckusick errexit(""); 6417966Smckusick break; 6517966Smckusick 6616264Smckusick case FSTATE: 6717937Smckusick case FCLEAR: 6816264Smckusick pfatal("ROOT INODE NOT DIRECTORY"); 6917966Smckusick if (reply("REALLOCATE")) { 7017966Smckusick freeino(ROOTINO); 7136827Smckusick if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 7217966Smckusick errexit("CANNOT ALLOCATE ROOT INODE\n"); 7317966Smckusick break; 7417966Smckusick } 7517943Smckusick if (reply("FIX") == 0) 7616264Smckusick errexit(""); 7717943Smckusick dp = ginode(ROOTINO); 7816264Smckusick dp->di_mode &= ~IFMT; 7916264Smckusick dp->di_mode |= IFDIR; 8016264Smckusick inodirty(); 8140026Smckusick break; 8216264Smckusick 8316264Smckusick case DSTATE: 8416264Smckusick break; 8516264Smckusick 8617937Smckusick default: 8717937Smckusick errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 8816264Smckusick } 8940026Smckusick statemap[ROOTINO] = DFOUND; 9040026Smckusick /* 9140026Smckusick * Sort the directory list into disk block order. 9240026Smckusick */ 9340026Smckusick qsort((char *)inpsort, (int)inplast, sizeof *inpsort, blksort); 9440026Smckusick /* 9540026Smckusick * Check the integrity of each directory. 9640026Smckusick */ 9740026Smckusick bzero((char *)&curino, sizeof(struct inodesc)); 9840026Smckusick curino.id_type = DATA; 9940026Smckusick curino.id_func = pass2check; 10040026Smckusick dp = &dino; 10140026Smckusick inpend = &inpsort[inplast]; 10240026Smckusick for (inpp = inpsort; inpp < inpend; inpp++) { 10340026Smckusick inp = *inpp; 104*41136Smckusick if (inp->i_isize == 0) 105*41136Smckusick continue; 10640026Smckusick if (inp->i_isize < MINDIRSIZE) { 10740026Smckusick direrror(inp->i_number, "DIRECTORY TOO SHORT"); 10840026Smckusick inp->i_isize = MINDIRSIZE; 10940026Smckusick if (reply("FIX") == 1) { 11040026Smckusick dp = ginode(inp->i_number); 11140026Smckusick dp->di_size = MINDIRSIZE; 11240026Smckusick inodirty(); 11340026Smckusick dp = &dino; 11440026Smckusick } 11540026Smckusick } 11640026Smckusick if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 11740026Smckusick getpathname(pathbuf, inp->i_number, inp->i_number); 11840026Smckusick pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", 11940026Smckusick pathbuf, inp->i_isize, DIRBLKSIZ); 12040026Smckusick if (preen) 12140026Smckusick printf(" (ADJUSTED)\n"); 12240026Smckusick inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 12340026Smckusick if (preen || reply("ADJUST") == 1) { 12440026Smckusick dp = ginode(inp->i_number); 12540026Smckusick dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); 12640026Smckusick inodirty(); 12740026Smckusick dp = &dino; 12840026Smckusick } 12940026Smckusick } 13040026Smckusick dp->di_size = inp->i_isize; 13140026Smckusick bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 13240026Smckusick (int)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; 143*41136Smckusick 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; 15640026Smckusick makeentry(inp->i_number, inp->i_parent, ".."); 15740026Smckusick lncntp[inp->i_parent]--; 15840026Smckusick continue; 15940026Smckusick } 16040026Smckusick fileerror(inp->i_parent, inp->i_number, 16140026Smckusick "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 17516264Smckusick pass2check(idesc) 17616264Smckusick struct inodesc *idesc; 17716264Smckusick { 17839973Smckusick register struct direct *dirp = idesc->id_dirp; 17940026Smckusick register struct inoinfo *inp; 18016264Smckusick int n, entrysize, ret = 0; 18139973Smckusick struct dinode *dp; 182*41136Smckusick char *errmsg; 18339973Smckusick struct direct proto; 18440026Smckusick char namebuf[MAXPATHLEN + 1]; 18540026Smckusick char pathbuf[MAXPATHLEN + 1]; 18616264Smckusick 18716264Smckusick /* 18816264Smckusick * check for "." 18916264Smckusick */ 19016264Smckusick if (idesc->id_entryno != 0) 19116264Smckusick goto chk1; 19216264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 19316264Smckusick if (dirp->d_ino != idesc->id_number) { 19439973Smckusick direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 19516264Smckusick dirp->d_ino = idesc->id_number; 19616264Smckusick if (reply("FIX") == 1) 19716264Smckusick ret |= ALTERED; 19816264Smckusick } 19916264Smckusick goto chk1; 20016264Smckusick } 20139973Smckusick direrror(idesc->id_number, "MISSING '.'"); 20216264Smckusick proto.d_ino = idesc->id_number; 20316264Smckusick proto.d_namlen = 1; 20416264Smckusick (void)strcpy(proto.d_name, "."); 20516264Smckusick entrysize = DIRSIZ(&proto); 20616264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 20716264Smckusick pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 20816264Smckusick dirp->d_name); 20916264Smckusick } else if (dirp->d_reclen < entrysize) { 21016264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 21116264Smckusick } else if (dirp->d_reclen < 2 * entrysize) { 21216264Smckusick proto.d_reclen = dirp->d_reclen; 21316264Smckusick bcopy((char *)&proto, (char *)dirp, entrysize); 21416264Smckusick if (reply("FIX") == 1) 21516264Smckusick ret |= ALTERED; 21616264Smckusick } else { 21716264Smckusick n = dirp->d_reclen - entrysize; 21816264Smckusick proto.d_reclen = entrysize; 21916264Smckusick bcopy((char *)&proto, (char *)dirp, entrysize); 22016264Smckusick idesc->id_entryno++; 22116264Smckusick lncntp[dirp->d_ino]--; 22239973Smckusick dirp = (struct direct *)((char *)(dirp) + entrysize); 22316264Smckusick bzero((char *)dirp, n); 22416264Smckusick dirp->d_reclen = n; 22516264Smckusick if (reply("FIX") == 1) 22616264Smckusick ret |= ALTERED; 22716264Smckusick } 22816264Smckusick chk1: 22916264Smckusick if (idesc->id_entryno > 1) 23016264Smckusick goto chk2; 23140026Smckusick inp = getinoinfo(idesc->id_number); 23240026Smckusick proto.d_ino = inp->i_parent; 23316264Smckusick proto.d_namlen = 2; 23416264Smckusick (void)strcpy(proto.d_name, ".."); 23516264Smckusick entrysize = DIRSIZ(&proto); 23616264Smckusick if (idesc->id_entryno == 0) { 23716264Smckusick n = DIRSIZ(dirp); 23816264Smckusick if (dirp->d_reclen < n + entrysize) 23916264Smckusick goto chk2; 24016264Smckusick proto.d_reclen = dirp->d_reclen - n; 24116264Smckusick dirp->d_reclen = n; 24216264Smckusick idesc->id_entryno++; 24316264Smckusick lncntp[dirp->d_ino]--; 24439973Smckusick dirp = (struct direct *)((char *)(dirp) + n); 24540026Smckusick bzero((char *)dirp, (int)proto.d_reclen); 24640026Smckusick dirp->d_reclen = proto.d_reclen; 24716264Smckusick } 24816264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 24940026Smckusick inp->i_dotdot = dirp->d_ino; 25016264Smckusick goto chk2; 25116264Smckusick } 25216264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 25340026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 25416264Smckusick pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 25516264Smckusick dirp->d_name); 25640026Smckusick inp->i_dotdot = (ino_t)-1; 25716264Smckusick } else if (dirp->d_reclen < entrysize) { 25840026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 25916264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 26040026Smckusick inp->i_dotdot = (ino_t)-1; 26140026Smckusick } else if (inp->i_parent != 0) { 26240026Smckusick /* 26340026Smckusick * We know the parent, so fix now. 26440026Smckusick */ 26540026Smckusick inp->i_dotdot = inp->i_parent; 26640026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 26716264Smckusick proto.d_reclen = dirp->d_reclen; 26816264Smckusick bcopy((char *)&proto, (char *)dirp, entrysize); 26916264Smckusick if (reply("FIX") == 1) 27016264Smckusick ret |= ALTERED; 27116264Smckusick } 27240026Smckusick idesc->id_entryno++; 27340026Smckusick if (dirp->d_ino != 0) 27440026Smckusick lncntp[dirp->d_ino]--; 27540026Smckusick return (ret|KEEPON); 27616264Smckusick chk2: 27716264Smckusick if (dirp->d_ino == 0) 27816264Smckusick return (ret|KEEPON); 27916264Smckusick if (dirp->d_namlen <= 2 && 28016264Smckusick dirp->d_name[0] == '.' && 28116264Smckusick idesc->id_entryno >= 2) { 28216264Smckusick if (dirp->d_namlen == 1) { 28339973Smckusick direrror(idesc->id_number, "EXTRA '.' ENTRY"); 28416264Smckusick dirp->d_ino = 0; 28516264Smckusick if (reply("FIX") == 1) 28616264Smckusick ret |= ALTERED; 28716264Smckusick return (KEEPON | ret); 28816264Smckusick } 28916264Smckusick if (dirp->d_name[1] == '.') { 29039973Smckusick direrror(idesc->id_number, "EXTRA '..' ENTRY"); 29116264Smckusick dirp->d_ino = 0; 29216264Smckusick if (reply("FIX") == 1) 29316264Smckusick ret |= ALTERED; 29416264Smckusick return (KEEPON | ret); 29516264Smckusick } 29616264Smckusick } 29716264Smckusick idesc->id_entryno++; 29816264Smckusick n = 0; 29939973Smckusick if (dirp->d_ino > maxino || dirp->d_ino <= 0) { 30040026Smckusick fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 30116264Smckusick n = reply("REMOVE"); 30216264Smckusick } else { 30316264Smckusick again: 30416264Smckusick switch (statemap[dirp->d_ino]) { 30516264Smckusick case USTATE: 30640569Smckusick if (idesc->id_entryno <= 2) 30740569Smckusick break; 30840026Smckusick fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 30916264Smckusick n = reply("REMOVE"); 31016264Smckusick break; 31116264Smckusick 31217937Smckusick case DCLEAR: 31317937Smckusick case FCLEAR: 31440569Smckusick if (idesc->id_entryno <= 2) 31540569Smckusick break; 316*41136Smckusick if (statemap[dirp->d_ino] == DCLEAR) 317*41136Smckusick errmsg = "ZERO LENGTH DIRECTORY"; 318*41136Smckusick else 319*41136Smckusick errmsg = "DUP/BAD"; 320*41136Smckusick fileerror(idesc->id_number, dirp->d_ino, errmsg); 32116264Smckusick if ((n = reply("REMOVE")) == 1) 32216264Smckusick break; 32317943Smckusick dp = ginode(dirp->d_ino); 32439973Smckusick statemap[dirp->d_ino] = 32539973Smckusick (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 32630599Smckusick lncntp[dirp->d_ino] = dp->di_nlink; 32716264Smckusick goto again; 32816264Smckusick 32940026Smckusick case DSTATE: 33040026Smckusick if (statemap[idesc->id_number] == DFOUND) 33140026Smckusick statemap[dirp->d_ino] = DFOUND; 33240026Smckusick /* fall through */ 33340026Smckusick 33417937Smckusick case DFOUND: 33540026Smckusick inp = getinoinfo(dirp->d_ino); 33640026Smckusick if (inp->i_parent != 0 && idesc->id_entryno > 2) { 33740026Smckusick getpathname(pathbuf, idesc->id_number, 33840026Smckusick idesc->id_number); 33917991Smckusick getpathname(namebuf, dirp->d_ino, dirp->d_ino); 34040026Smckusick pwarn("%s %s %s\n", pathbuf, 34117991Smckusick "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 34217991Smckusick namebuf); 34317991Smckusick if (preen) 34417991Smckusick printf(" (IGNORED)\n"); 34517991Smckusick else if ((n = reply("REMOVE")) == 1) 34617991Smckusick break; 34717991Smckusick } 34840026Smckusick if (idesc->id_entryno > 2) 34940026Smckusick inp->i_parent = idesc->id_number; 35017937Smckusick /* fall through */ 35117937Smckusick 35216264Smckusick case FSTATE: 35316264Smckusick lncntp[dirp->d_ino]--; 35416264Smckusick break; 35516264Smckusick 35626480Smckusick default: 35726480Smckusick errexit("BAD STATE %d FOR INODE I=%d", 35826480Smckusick statemap[dirp->d_ino], dirp->d_ino); 35916264Smckusick } 36016264Smckusick } 36116264Smckusick if (n == 0) 36216264Smckusick return (ret|KEEPON); 36316264Smckusick dirp->d_ino = 0; 36416264Smckusick return (ret|KEEPON|ALTERED); 36516264Smckusick } 36640026Smckusick 36740026Smckusick /* 36840026Smckusick * Routine to sort disk blocks. 36940026Smckusick */ 37040026Smckusick blksort(inpp1, inpp2) 37140026Smckusick struct inoinfo **inpp1, **inpp2; 37240026Smckusick { 37340026Smckusick 37440026Smckusick return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); 37540026Smckusick } 376