pass2.c revision 8871
1124208Sdes/* 2124208Sdes * Copyright (c) 1980, 1986, 1993 3124208Sdes * The Regents of the University of California. All rights reserved. 4124208Sdes * 5124208Sdes * Redistribution and use in source and binary forms, with or without 6124208Sdes * modification, are permitted provided that the following conditions 7124208Sdes * are met: 8124208Sdes * 1. Redistributions of source code must retain the above copyright 9124208Sdes * notice, this list of conditions and the following disclaimer. 10124208Sdes * 2. Redistributions in binary form must reproduce the above copyright 11124208Sdes * notice, this list of conditions and the following disclaimer in the 12124208Sdes * documentation and/or other materials provided with the distribution. 13124208Sdes * 3. All advertising materials mentioning features or use of this software 14124208Sdes * must display the following acknowledgement: 15124208Sdes * This product includes software developed by the University of 16124208Sdes * California, Berkeley and its contributors. 17124208Sdes * 4. Neither the name of the University nor the names of its contributors 18124208Sdes * may be used to endorse or promote products derived from this software 19124208Sdes * without specific prior written permission. 20124208Sdes * 21124208Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22124208Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23124208Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24124208Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2598937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2698937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2798937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28181111Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2998937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3098937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3198937Sdes * SUCH DAMAGE. 3298937Sdes */ 33162852Sdes 3498937Sdes#ifndef lint 3598937Sdesstatic const char sccsid[] = "@(#)pass2.c 8.2 (Berkeley) 2/27/94"; 3698937Sdes#endif /* not lint */ 3798937Sdes 3898937Sdes#include <sys/param.h> 3998937Sdes#include <sys/time.h> 4098937Sdes#include <ufs/ufs/dinode.h> 4198937Sdes#include <ufs/ufs/dir.h> 4298937Sdes#include <ufs/ffs/fs.h> 4398937Sdes#include <stdio.h> 4498937Sdes#include <stdlib.h> 4598937Sdes#include <string.h> 4698937Sdes#include "fsck.h" 4798937Sdes 4898937Sdes#define MINDIRSIZE (sizeof (struct dirtemplate)) 4998937Sdes 5098937Sdesint pass2check(), blksort(); 5198937Sdes 5298937Sdesvoid 5398937Sdespass2() 5498937Sdes{ 5598937Sdes register struct dinode *dp; 5698937Sdes register struct inoinfo **inpp, *inp; 57149749Sdes struct inoinfo **inpend; 58149749Sdes struct inodesc curino; 59149749Sdes struct dinode dino; 60149749Sdes char pathbuf[MAXPATHLEN + 1]; 61149749Sdes 6298937Sdes switch (statemap[ROOTINO]) { 6398937Sdes 6498937Sdes case USTATE: 65149749Sdes pfatal("ROOT INODE UNALLOCATED"); 66149749Sdes if (reply("ALLOCATE") == 0) 67149749Sdes errexit(""); 68149749Sdes if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 69149749Sdes errexit("CANNOT ALLOCATE ROOT INODE\n"); 70149749Sdes break; 71181111Sdes 72149749Sdes case DCLEAR: 73149749Sdes pfatal("DUPS/BAD IN ROOT INODE"); 74149749Sdes if (reply("REALLOCATE")) { 7598937Sdes freeino(ROOTINO); 7698937Sdes if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 7798937Sdes errexit("CANNOT ALLOCATE ROOT INODE\n"); 7898937Sdes break; 7998937Sdes } 8098937Sdes if (reply("CONTINUE") == 0) 8198937Sdes errexit(""); 8298937Sdes break; 8398937Sdes 8498937Sdes case FSTATE: 8598937Sdes case FCLEAR: 8698937Sdes pfatal("ROOT INODE NOT DIRECTORY"); 8798937Sdes if (reply("REALLOCATE")) { 8898937Sdes freeino(ROOTINO); 8998937Sdes if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 9098937Sdes errexit("CANNOT ALLOCATE ROOT INODE\n"); 9198937Sdes break; 9298937Sdes } 93162852Sdes if (reply("FIX") == 0) 94162852Sdes errexit(""); 9598937Sdes dp = ginode(ROOTINO); 9698937Sdes dp->di_mode &= ~IFMT; 9798937Sdes dp->di_mode |= IFDIR; 9898937Sdes inodirty(); 9998937Sdes break; 10098937Sdes 101126274Sdes case DSTATE: 10298937Sdes break; 10398937Sdes 10498937Sdes default: 10598937Sdes errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 10698937Sdes } 10798937Sdes statemap[ROOTINO] = DFOUND; 10898937Sdes /* 10998937Sdes * Sort the directory list into disk block order. 11098937Sdes */ 11198937Sdes qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 11298937Sdes /* 11398937Sdes * Check the integrity of each directory. 11498937Sdes */ 11598937Sdes bzero((char *)&curino, sizeof(struct inodesc)); 11698937Sdes curino.id_type = DATA; 11798937Sdes curino.id_func = pass2check; 11898937Sdes dp = &dino; 11998937Sdes inpend = &inpsort[inplast]; 12098937Sdes for (inpp = inpsort; inpp < inpend; inpp++) { 12198937Sdes inp = *inpp; 12298937Sdes if (inp->i_isize == 0) 12398937Sdes continue; 12498937Sdes if (inp->i_isize < MINDIRSIZE) { 12598937Sdes direrror(inp->i_number, "DIRECTORY TOO SHORT"); 12698937Sdes inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 12798937Sdes if (reply("FIX") == 1) { 12898937Sdes dp = ginode(inp->i_number); 12998937Sdes dp->di_size = inp->i_isize; 13098937Sdes inodirty(); 13198937Sdes dp = &dino; 13298937Sdes } 13398937Sdes } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 13498937Sdes getpathname(pathbuf, inp->i_number, inp->i_number); 13598937Sdes pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", 13698937Sdes pathbuf, inp->i_isize, DIRBLKSIZ); 13798937Sdes if (preen) 13898937Sdes printf(" (ADJUSTED)\n"); 13998937Sdes inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 14098937Sdes if (preen || reply("ADJUST") == 1) { 14198937Sdes dp = ginode(inp->i_number); 14298937Sdes dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); 143106121Sdes inodirty(); 14498937Sdes dp = &dino; 14598937Sdes } 14698937Sdes } 14798937Sdes bzero((char *)&dino, sizeof(struct dinode)); 14898937Sdes dino.di_mode = IFDIR; 14998937Sdes dp->di_size = inp->i_isize; 15098937Sdes bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 15198937Sdes (size_t)inp->i_numblks); 15298937Sdes curino.id_number = inp->i_number; 15398937Sdes curino.id_parent = inp->i_parent; 15498937Sdes (void)ckinode(dp, &curino); 15598937Sdes } 15698937Sdes /* 15798937Sdes * Now that the parents of all directories have been found, 15898937Sdes * make another pass to verify the value of `..' 15998937Sdes */ 16098937Sdes for (inpp = inpsort; inpp < inpend; inpp++) { 16198937Sdes inp = *inpp; 16298937Sdes if (inp->i_parent == 0 || inp->i_isize == 0) 16398937Sdes continue; 164106121Sdes if (statemap[inp->i_parent] == DFOUND && 16598937Sdes statemap[inp->i_number] == DSTATE) 16698937Sdes statemap[inp->i_number] = DFOUND; 16798937Sdes if (inp->i_dotdot == inp->i_parent || 16898937Sdes inp->i_dotdot == (ino_t)-1) 16998937Sdes continue; 17098937Sdes if (inp->i_dotdot == 0) { 17198937Sdes inp->i_dotdot = inp->i_parent; 172106121Sdes fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 17398937Sdes if (reply("FIX") == 0) 17498937Sdes continue; 17598937Sdes (void)makeentry(inp->i_number, inp->i_parent, ".."); 17698937Sdes lncntp[inp->i_parent]--; 177106121Sdes continue; 17898937Sdes } 17998937Sdes fileerror(inp->i_parent, inp->i_number, 18098937Sdes "BAD INODE NUMBER FOR '..'"); 181106121Sdes if (reply("FIX") == 0) 18298937Sdes continue; 18398937Sdes lncntp[inp->i_dotdot]++; 18498937Sdes lncntp[inp->i_parent]--; 18598937Sdes inp->i_dotdot = inp->i_parent; 18698937Sdes (void)changeino(inp->i_number, "..", inp->i_parent); 18798937Sdes } 18898937Sdes /* 18998937Sdes * Mark all the directories that can be found from the root. 19098937Sdes */ 19198937Sdes propagate(); 19298937Sdes} 19398937Sdes 19498937Sdesint 19598937Sdespass2check(idesc) 19698937Sdes struct inodesc *idesc; 19798937Sdes{ 19898937Sdes register struct direct *dirp = idesc->id_dirp; 19998937Sdes register struct inoinfo *inp; 20098937Sdes int n, entrysize, ret = 0; 201106121Sdes struct dinode *dp; 20298937Sdes char *errmsg; 20398937Sdes struct direct proto; 20498937Sdes char namebuf[MAXPATHLEN + 1]; 20598937Sdes char pathbuf[MAXPATHLEN + 1]; 20698937Sdes 20798937Sdes /* 20898937Sdes * If converting, set directory entry type. 20998937Sdes */ 21098937Sdes if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { 21198937Sdes dirp->d_type = typemap[dirp->d_ino]; 21298937Sdes ret |= ALTERED; 21398937Sdes } 214106121Sdes /* 21598937Sdes * check for "." 21698937Sdes */ 21798937Sdes if (idesc->id_entryno != 0) 21898937Sdes goto chk1; 21998937Sdes if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 22098937Sdes if (dirp->d_ino != idesc->id_number) { 22198937Sdes direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 22298937Sdes dirp->d_ino = idesc->id_number; 22398937Sdes if (reply("FIX") == 1) 22498937Sdes ret |= ALTERED; 22598937Sdes } 22698937Sdes if (newinofmt && dirp->d_type != DT_DIR) { 22798937Sdes direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 22898937Sdes dirp->d_type = DT_DIR; 22998937Sdes if (reply("FIX") == 1) 23098937Sdes ret |= ALTERED; 23198937Sdes } 23298937Sdes goto chk1; 23398937Sdes } 23498937Sdes direrror(idesc->id_number, "MISSING '.'"); 23598937Sdes proto.d_ino = idesc->id_number; 23698937Sdes if (newinofmt) 23798937Sdes proto.d_type = DT_DIR; 23898937Sdes else 23998937Sdes proto.d_type = 0; 24098937Sdes proto.d_namlen = 1; 24198937Sdes (void)strcpy(proto.d_name, "."); 24298937Sdes entrysize = DIRSIZ(0, &proto); 24398937Sdes if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 24498937Sdes pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 24598937Sdes dirp->d_name); 24698937Sdes } else if (dirp->d_reclen < entrysize) { 24798937Sdes pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 24898937Sdes } else if (dirp->d_reclen < 2 * entrysize) { 249106121Sdes proto.d_reclen = dirp->d_reclen; 250106121Sdes bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 251106121Sdes if (reply("FIX") == 1) 252106121Sdes ret |= ALTERED; 25398937Sdes } else { 25498937Sdes n = dirp->d_reclen - entrysize; 25598937Sdes proto.d_reclen = entrysize; 256126274Sdes bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 25798937Sdes idesc->id_entryno++; 25898937Sdes lncntp[dirp->d_ino]--; 25998937Sdes dirp = (struct direct *)((char *)(dirp) + entrysize); 26098937Sdes bzero((char *)dirp, (size_t)n); 26198937Sdes dirp->d_reclen = n; 26298937Sdes if (reply("FIX") == 1) 26398937Sdes ret |= ALTERED; 26498937Sdes } 26598937Sdeschk1: 26698937Sdes if (idesc->id_entryno > 1) 26798937Sdes goto chk2; 26898937Sdes inp = getinoinfo(idesc->id_number); 26998937Sdes proto.d_ino = inp->i_parent; 27098937Sdes if (newinofmt) 27198937Sdes proto.d_type = DT_DIR; 27298937Sdes else 27398937Sdes proto.d_type = 0; 27498937Sdes proto.d_namlen = 2; 27598937Sdes (void)strcpy(proto.d_name, ".."); 27698937Sdes entrysize = DIRSIZ(0, &proto); 27798937Sdes if (idesc->id_entryno == 0) { 27898937Sdes n = DIRSIZ(0, dirp); 27998937Sdes if (dirp->d_reclen < n + entrysize) 28098937Sdes goto chk2; 28198937Sdes proto.d_reclen = dirp->d_reclen - n; 28298937Sdes dirp->d_reclen = n; 28398937Sdes idesc->id_entryno++; 28498937Sdes lncntp[dirp->d_ino]--; 28598937Sdes dirp = (struct direct *)((char *)(dirp) + n); 28698937Sdes bzero((char *)dirp, (size_t)proto.d_reclen); 28798937Sdes dirp->d_reclen = proto.d_reclen; 28898937Sdes } 28998937Sdes if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 29098937Sdes inp->i_dotdot = dirp->d_ino; 29198937Sdes if (newinofmt && dirp->d_type != DT_DIR) { 29298937Sdes direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 29398937Sdes dirp->d_type = DT_DIR; 29498937Sdes if (reply("FIX") == 1) 29598937Sdes ret |= ALTERED; 29698937Sdes } 29798937Sdes goto chk2; 29898937Sdes } 29998937Sdes if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 300146998Sdes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 301146998Sdes pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 302146998Sdes dirp->d_name); 303146998Sdes inp->i_dotdot = (ino_t)-1; 30498937Sdes } else if (dirp->d_reclen < entrysize) { 30598937Sdes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 30698937Sdes pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 30798937Sdes inp->i_dotdot = (ino_t)-1; 30898937Sdes } else if (inp->i_parent != 0) { 30998937Sdes /* 31098937Sdes * We know the parent, so fix now. 31198937Sdes */ 31298937Sdes inp->i_dotdot = inp->i_parent; 31398937Sdes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 31498937Sdes proto.d_reclen = dirp->d_reclen; 31598937Sdes bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 31698937Sdes if (reply("FIX") == 1) 31798937Sdes ret |= ALTERED; 31898937Sdes } 31998937Sdes idesc->id_entryno++; 32098937Sdes if (dirp->d_ino != 0) 32198937Sdes lncntp[dirp->d_ino]--; 32298937Sdes return (ret|KEEPON); 32398937Sdeschk2: 32498937Sdes if (dirp->d_ino == 0) 32598937Sdes return (ret|KEEPON); 32698937Sdes if (dirp->d_namlen <= 2 && 32798937Sdes dirp->d_name[0] == '.' && 32898937Sdes idesc->id_entryno >= 2) { 32998937Sdes if (dirp->d_namlen == 1) { 33098937Sdes direrror(idesc->id_number, "EXTRA '.' ENTRY"); 33198937Sdes dirp->d_ino = 0; 33298937Sdes if (reply("FIX") == 1) 33398937Sdes ret |= ALTERED; 33498937Sdes return (KEEPON | ret); 33598937Sdes } 336124208Sdes if (dirp->d_name[1] == '.') { 337124208Sdes direrror(idesc->id_number, "EXTRA '..' ENTRY"); 338124208Sdes dirp->d_ino = 0; 339124208Sdes if (reply("FIX") == 1) 34098937Sdes ret |= ALTERED; 34198937Sdes return (KEEPON | ret); 34298937Sdes } 34398937Sdes } 34498937Sdes idesc->id_entryno++; 34598937Sdes n = 0; 34698937Sdes if (dirp->d_ino > maxino) { 34798937Sdes fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 34898937Sdes n = reply("REMOVE"); 34998937Sdes } else { 35098937Sdesagain: 35198937Sdes switch (statemap[dirp->d_ino]) { 35298937Sdes case USTATE: 35398937Sdes if (idesc->id_entryno <= 2) 35498937Sdes break; 35598937Sdes fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 35698937Sdes n = reply("REMOVE"); 35798937Sdes break; 35898937Sdes 35998937Sdes case DCLEAR: 36098937Sdes case FCLEAR: 36198937Sdes if (idesc->id_entryno <= 2) 36298937Sdes break; 36398937Sdes if (statemap[dirp->d_ino] == FCLEAR) 36498937Sdes errmsg = "DUP/BAD"; 36598937Sdes else if (!preen) 36698937Sdes errmsg = "ZERO LENGTH DIRECTORY"; 36798937Sdes else { 36898937Sdes n = 1; 36998937Sdes break; 37098937Sdes } 37198937Sdes fileerror(idesc->id_number, dirp->d_ino, errmsg); 37298937Sdes if ((n = reply("REMOVE")) == 1) 37398937Sdes break; 37498937Sdes dp = ginode(dirp->d_ino); 37598937Sdes statemap[dirp->d_ino] = 37698937Sdes (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 37798937Sdes lncntp[dirp->d_ino] = dp->di_nlink; 37898937Sdes goto again; 37998937Sdes 38098937Sdes case DSTATE: 38198937Sdes if (statemap[idesc->id_number] == DFOUND) 38298937Sdes statemap[dirp->d_ino] = DFOUND; 38398937Sdes /* fall through */ 38498937Sdes 38598937Sdes case DFOUND: 38698937Sdes inp = getinoinfo(dirp->d_ino); 38798937Sdes if (inp->i_parent != 0 && idesc->id_entryno > 2) { 38898937Sdes getpathname(pathbuf, idesc->id_number, 38998937Sdes idesc->id_number); 39098937Sdes getpathname(namebuf, dirp->d_ino, dirp->d_ino); 39198937Sdes pwarn("%s %s %s\n", pathbuf, 39298937Sdes "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 39398937Sdes namebuf); 39498937Sdes if (preen) 39598937Sdes printf(" (IGNORED)\n"); 39698937Sdes else if ((n = reply("REMOVE")) == 1) 39798937Sdes break; 39898937Sdes } 39998937Sdes if (idesc->id_entryno > 2) 40098937Sdes inp->i_parent = idesc->id_number; 40198937Sdes /* fall through */ 40298937Sdes 40398937Sdes case FSTATE: 40498937Sdes if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { 40598937Sdes fileerror(idesc->id_number, dirp->d_ino, 406113908Sdes "BAD TYPE VALUE"); 407113908Sdes dirp->d_type = typemap[dirp->d_ino]; 408113908Sdes if (reply("FIX") == 1) 409113908Sdes ret |= ALTERED; 410113908Sdes } 411113908Sdes lncntp[dirp->d_ino]--; 412113908Sdes break; 413113908Sdes 414113908Sdes default: 415113908Sdes errexit("BAD STATE %d FOR INODE I=%d", 416113908Sdes statemap[dirp->d_ino], dirp->d_ino); 417113908Sdes } 418113908Sdes } 419113908Sdes if (n == 0) 42098937Sdes return (ret|KEEPON); 42198937Sdes dirp->d_ino = 0; 42298937Sdes return (ret|KEEPON|ALTERED); 42398937Sdes} 42498937Sdes 42598937Sdes/* 42698937Sdes * Routine to sort disk blocks. 42798937Sdes */ 42898937Sdesint 42998937Sdesblksort(inpp1, inpp2) 43098937Sdes struct inoinfo **inpp1, **inpp2; 43198937Sdes{ 43298937Sdes 43398937Sdes return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); 434149749Sdes} 435149749Sdes