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*40569Smckusick static char sccsid[] = "@(#)pass2.c 5.10 (Berkeley) 03/22/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; 10440026Smckusick if (inp->i_isize < MINDIRSIZE) { 10540026Smckusick direrror(inp->i_number, "DIRECTORY TOO SHORT"); 10640026Smckusick inp->i_isize = MINDIRSIZE; 10740026Smckusick if (reply("FIX") == 1) { 10840026Smckusick dp = ginode(inp->i_number); 10940026Smckusick dp->di_size = MINDIRSIZE; 11040026Smckusick inodirty(); 11140026Smckusick dp = &dino; 11240026Smckusick } 11340026Smckusick } 11440026Smckusick 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 } 12840026Smckusick dp->di_size = inp->i_isize; 12940026Smckusick bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 13040026Smckusick (int)inp->i_numblks); 13140026Smckusick curino.id_number = inp->i_number; 13240026Smckusick curino.id_parent = inp->i_parent; 13340026Smckusick (void)ckinode(dp, &curino); 13440026Smckusick } 13540026Smckusick /* 13640026Smckusick * Now that the parents of all directories have been found, 13740026Smckusick * make another pass to verify the value of `..' 13840026Smckusick */ 13940026Smckusick for (inpp = inpsort; inpp < inpend; inpp++) { 14040026Smckusick inp = *inpp; 14140026Smckusick if (inp->i_parent == 0) 14240026Smckusick continue; 14340026Smckusick if (statemap[inp->i_parent] == DFOUND && 14440026Smckusick statemap[inp->i_number] == DSTATE) 14540026Smckusick statemap[inp->i_number] = DFOUND; 14640026Smckusick if (inp->i_dotdot == inp->i_parent || 14740026Smckusick inp->i_dotdot == (ino_t)-1) 14840026Smckusick continue; 14940026Smckusick if (inp->i_dotdot == 0) { 15040026Smckusick inp->i_dotdot = inp->i_parent; 15140026Smckusick fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 15240026Smckusick if (reply("FIX") == 0) 15340026Smckusick continue; 15440026Smckusick makeentry(inp->i_number, inp->i_parent, ".."); 15540026Smckusick lncntp[inp->i_parent]--; 15640026Smckusick continue; 15740026Smckusick } 15840026Smckusick fileerror(inp->i_parent, inp->i_number, 15940026Smckusick "BAD INODE NUMBER FOR '..'"); 16040026Smckusick if (reply("FIX") == 0) 16140026Smckusick continue; 16240026Smckusick lncntp[inp->i_dotdot]++; 16340026Smckusick lncntp[inp->i_parent]--; 16440026Smckusick inp->i_dotdot = inp->i_parent; 16540026Smckusick (void)changeino(inp->i_number, "..", inp->i_parent); 16640026Smckusick } 16740026Smckusick /* 16840026Smckusick * Mark all the directories that can be found from the root. 16940026Smckusick */ 17040026Smckusick propagate(); 17116264Smckusick } 17216264Smckusick 17316264Smckusick pass2check(idesc) 17416264Smckusick struct inodesc *idesc; 17516264Smckusick { 17639973Smckusick register struct direct *dirp = idesc->id_dirp; 17740026Smckusick register struct inoinfo *inp; 17816264Smckusick int n, entrysize, ret = 0; 17939973Smckusick struct dinode *dp; 18039973Smckusick struct direct proto; 18140026Smckusick char namebuf[MAXPATHLEN + 1]; 18240026Smckusick char pathbuf[MAXPATHLEN + 1]; 18316264Smckusick 18416264Smckusick /* 18516264Smckusick * check for "." 18616264Smckusick */ 18716264Smckusick if (idesc->id_entryno != 0) 18816264Smckusick goto chk1; 18916264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 19016264Smckusick if (dirp->d_ino != idesc->id_number) { 19139973Smckusick direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 19216264Smckusick dirp->d_ino = idesc->id_number; 19316264Smckusick if (reply("FIX") == 1) 19416264Smckusick ret |= ALTERED; 19516264Smckusick } 19616264Smckusick goto chk1; 19716264Smckusick } 19839973Smckusick direrror(idesc->id_number, "MISSING '.'"); 19916264Smckusick proto.d_ino = idesc->id_number; 20016264Smckusick proto.d_namlen = 1; 20116264Smckusick (void)strcpy(proto.d_name, "."); 20216264Smckusick entrysize = DIRSIZ(&proto); 20316264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 20416264Smckusick pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 20516264Smckusick dirp->d_name); 20616264Smckusick } else if (dirp->d_reclen < entrysize) { 20716264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 20816264Smckusick } else if (dirp->d_reclen < 2 * entrysize) { 20916264Smckusick proto.d_reclen = dirp->d_reclen; 21016264Smckusick bcopy((char *)&proto, (char *)dirp, entrysize); 21116264Smckusick if (reply("FIX") == 1) 21216264Smckusick ret |= ALTERED; 21316264Smckusick } else { 21416264Smckusick n = dirp->d_reclen - entrysize; 21516264Smckusick proto.d_reclen = entrysize; 21616264Smckusick bcopy((char *)&proto, (char *)dirp, entrysize); 21716264Smckusick idesc->id_entryno++; 21816264Smckusick lncntp[dirp->d_ino]--; 21939973Smckusick dirp = (struct direct *)((char *)(dirp) + entrysize); 22016264Smckusick bzero((char *)dirp, n); 22116264Smckusick dirp->d_reclen = n; 22216264Smckusick if (reply("FIX") == 1) 22316264Smckusick ret |= ALTERED; 22416264Smckusick } 22516264Smckusick chk1: 22616264Smckusick if (idesc->id_entryno > 1) 22716264Smckusick goto chk2; 22840026Smckusick inp = getinoinfo(idesc->id_number); 22940026Smckusick proto.d_ino = inp->i_parent; 23016264Smckusick proto.d_namlen = 2; 23116264Smckusick (void)strcpy(proto.d_name, ".."); 23216264Smckusick entrysize = DIRSIZ(&proto); 23316264Smckusick if (idesc->id_entryno == 0) { 23416264Smckusick n = DIRSIZ(dirp); 23516264Smckusick if (dirp->d_reclen < n + entrysize) 23616264Smckusick goto chk2; 23716264Smckusick proto.d_reclen = dirp->d_reclen - n; 23816264Smckusick dirp->d_reclen = n; 23916264Smckusick idesc->id_entryno++; 24016264Smckusick lncntp[dirp->d_ino]--; 24139973Smckusick dirp = (struct direct *)((char *)(dirp) + n); 24240026Smckusick bzero((char *)dirp, (int)proto.d_reclen); 24340026Smckusick dirp->d_reclen = proto.d_reclen; 24416264Smckusick } 24516264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 24640026Smckusick inp->i_dotdot = dirp->d_ino; 24716264Smckusick goto chk2; 24816264Smckusick } 24916264Smckusick if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 25040026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 25116264Smckusick pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 25216264Smckusick dirp->d_name); 25340026Smckusick inp->i_dotdot = (ino_t)-1; 25416264Smckusick } else if (dirp->d_reclen < entrysize) { 25540026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 25616264Smckusick pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 25740026Smckusick inp->i_dotdot = (ino_t)-1; 25840026Smckusick } else if (inp->i_parent != 0) { 25940026Smckusick /* 26040026Smckusick * We know the parent, so fix now. 26140026Smckusick */ 26240026Smckusick inp->i_dotdot = inp->i_parent; 26340026Smckusick fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 26416264Smckusick proto.d_reclen = dirp->d_reclen; 26516264Smckusick bcopy((char *)&proto, (char *)dirp, entrysize); 26616264Smckusick if (reply("FIX") == 1) 26716264Smckusick ret |= ALTERED; 26816264Smckusick } 26940026Smckusick idesc->id_entryno++; 27040026Smckusick if (dirp->d_ino != 0) 27140026Smckusick lncntp[dirp->d_ino]--; 27240026Smckusick return (ret|KEEPON); 27316264Smckusick chk2: 27416264Smckusick if (dirp->d_ino == 0) 27516264Smckusick return (ret|KEEPON); 27616264Smckusick if (dirp->d_namlen <= 2 && 27716264Smckusick dirp->d_name[0] == '.' && 27816264Smckusick idesc->id_entryno >= 2) { 27916264Smckusick if (dirp->d_namlen == 1) { 28039973Smckusick direrror(idesc->id_number, "EXTRA '.' ENTRY"); 28116264Smckusick dirp->d_ino = 0; 28216264Smckusick if (reply("FIX") == 1) 28316264Smckusick ret |= ALTERED; 28416264Smckusick return (KEEPON | ret); 28516264Smckusick } 28616264Smckusick if (dirp->d_name[1] == '.') { 28739973Smckusick direrror(idesc->id_number, "EXTRA '..' ENTRY"); 28816264Smckusick dirp->d_ino = 0; 28916264Smckusick if (reply("FIX") == 1) 29016264Smckusick ret |= ALTERED; 29116264Smckusick return (KEEPON | ret); 29216264Smckusick } 29316264Smckusick } 29416264Smckusick idesc->id_entryno++; 29516264Smckusick n = 0; 29639973Smckusick if (dirp->d_ino > maxino || dirp->d_ino <= 0) { 29740026Smckusick fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 29816264Smckusick n = reply("REMOVE"); 29916264Smckusick } else { 30016264Smckusick again: 30116264Smckusick switch (statemap[dirp->d_ino]) { 30216264Smckusick case USTATE: 303*40569Smckusick if (idesc->id_entryno <= 2) 304*40569Smckusick break; 30540026Smckusick fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 30616264Smckusick n = reply("REMOVE"); 30716264Smckusick break; 30816264Smckusick 30917937Smckusick case DCLEAR: 31017937Smckusick case FCLEAR: 311*40569Smckusick if (idesc->id_entryno <= 2) 312*40569Smckusick break; 31340026Smckusick fileerror(idesc->id_number, dirp->d_ino, "DUP/BAD"); 31416264Smckusick if ((n = reply("REMOVE")) == 1) 31516264Smckusick break; 31617943Smckusick dp = ginode(dirp->d_ino); 31739973Smckusick statemap[dirp->d_ino] = 31839973Smckusick (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 31930599Smckusick lncntp[dirp->d_ino] = dp->di_nlink; 32016264Smckusick goto again; 32116264Smckusick 32240026Smckusick case DSTATE: 32340026Smckusick if (statemap[idesc->id_number] == DFOUND) 32440026Smckusick statemap[dirp->d_ino] = DFOUND; 32540026Smckusick /* fall through */ 32640026Smckusick 32717937Smckusick case DFOUND: 32840026Smckusick inp = getinoinfo(dirp->d_ino); 32940026Smckusick if (inp->i_isize == 0) { 33040026Smckusick direrror(dirp->d_ino, "ZERO LENGTH DIRECTORY"); 33140026Smckusick if ((n = reply("REMOVE")) == 1) { 33240026Smckusick statemap[dirp->d_ino] = DCLEAR; 33340026Smckusick break; 33440026Smckusick } 33540026Smckusick } 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