1 /* 2 * Copyright (c) 1980, 1986 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 static char sccsid[] = "@(#)pass2.c 5.8 (Berkeley) 02/01/90"; 20 #endif /* not lint */ 21 22 #include <sys/param.h> 23 #include <ufs/dinode.h> 24 #include <ufs/fs.h> 25 #include <ufs/dir.h> 26 #include <strings.h> 27 #include "fsck.h" 28 29 int pass2check(); 30 31 pass2() 32 { 33 register struct dinode *dp; 34 struct inodesc rootdesc; 35 36 bzero((char *)&rootdesc, sizeof(struct inodesc)); 37 rootdesc.id_type = ADDR; 38 rootdesc.id_func = pass2check; 39 rootdesc.id_number = ROOTINO; 40 pathp = pathname; 41 switch (statemap[ROOTINO]) { 42 43 case USTATE: 44 pfatal("ROOT INODE UNALLOCATED"); 45 if (reply("ALLOCATE") == 0) 46 errexit(""); 47 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 48 errexit("CANNOT ALLOCATE ROOT INODE\n"); 49 descend(&rootdesc, ROOTINO); 50 break; 51 52 case DCLEAR: 53 pfatal("DUPS/BAD IN ROOT INODE"); 54 if (reply("REALLOCATE")) { 55 freeino(ROOTINO); 56 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 57 errexit("CANNOT ALLOCATE ROOT INODE\n"); 58 descend(&rootdesc, ROOTINO); 59 break; 60 } 61 if (reply("CONTINUE") == 0) 62 errexit(""); 63 statemap[ROOTINO] = DSTATE; 64 descend(&rootdesc, ROOTINO); 65 break; 66 67 case FSTATE: 68 case FCLEAR: 69 pfatal("ROOT INODE NOT DIRECTORY"); 70 if (reply("REALLOCATE")) { 71 freeino(ROOTINO); 72 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 73 errexit("CANNOT ALLOCATE ROOT INODE\n"); 74 descend(&rootdesc, ROOTINO); 75 break; 76 } 77 if (reply("FIX") == 0) 78 errexit(""); 79 dp = ginode(ROOTINO); 80 dp->di_mode &= ~IFMT; 81 dp->di_mode |= IFDIR; 82 inodirty(); 83 statemap[ROOTINO] = DSTATE; 84 /* fall into ... */ 85 86 case DSTATE: 87 descend(&rootdesc, ROOTINO); 88 break; 89 90 default: 91 errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 92 } 93 } 94 95 pass2check(idesc) 96 struct inodesc *idesc; 97 { 98 register struct direct *dirp = idesc->id_dirp; 99 char *curpathloc; 100 int n, entrysize, ret = 0; 101 struct dinode *dp; 102 struct direct proto; 103 char namebuf[BUFSIZ]; 104 105 /* 106 * check for "." 107 */ 108 if (idesc->id_entryno != 0) 109 goto chk1; 110 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 111 if (dirp->d_ino != idesc->id_number) { 112 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 113 dirp->d_ino = idesc->id_number; 114 if (reply("FIX") == 1) 115 ret |= ALTERED; 116 } 117 goto chk1; 118 } 119 direrror(idesc->id_number, "MISSING '.'"); 120 proto.d_ino = idesc->id_number; 121 proto.d_namlen = 1; 122 (void)strcpy(proto.d_name, "."); 123 entrysize = DIRSIZ(&proto); 124 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 125 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 126 dirp->d_name); 127 } else if (dirp->d_reclen < entrysize) { 128 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 129 } else if (dirp->d_reclen < 2 * entrysize) { 130 proto.d_reclen = dirp->d_reclen; 131 bcopy((char *)&proto, (char *)dirp, entrysize); 132 if (reply("FIX") == 1) 133 ret |= ALTERED; 134 } else { 135 n = dirp->d_reclen - entrysize; 136 proto.d_reclen = entrysize; 137 bcopy((char *)&proto, (char *)dirp, entrysize); 138 idesc->id_entryno++; 139 lncntp[dirp->d_ino]--; 140 dirp = (struct direct *)((char *)(dirp) + entrysize); 141 bzero((char *)dirp, n); 142 dirp->d_reclen = n; 143 if (reply("FIX") == 1) 144 ret |= ALTERED; 145 } 146 chk1: 147 if (idesc->id_entryno > 1) 148 goto chk2; 149 proto.d_ino = idesc->id_parent; 150 proto.d_namlen = 2; 151 (void)strcpy(proto.d_name, ".."); 152 entrysize = DIRSIZ(&proto); 153 if (idesc->id_entryno == 0) { 154 n = DIRSIZ(dirp); 155 if (dirp->d_reclen < n + entrysize) 156 goto chk2; 157 proto.d_reclen = dirp->d_reclen - n; 158 dirp->d_reclen = n; 159 idesc->id_entryno++; 160 lncntp[dirp->d_ino]--; 161 dirp = (struct direct *)((char *)(dirp) + n); 162 bzero((char *)dirp, n); 163 dirp->d_reclen = n; 164 } 165 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 166 if (dirp->d_ino != idesc->id_parent) { 167 direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'"); 168 dirp->d_ino = idesc->id_parent; 169 if (reply("FIX") == 1) 170 ret |= ALTERED; 171 } 172 goto chk2; 173 } 174 direrror(idesc->id_number, "MISSING '..'"); 175 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 176 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 177 dirp->d_name); 178 } else if (dirp->d_reclen < entrysize) { 179 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 180 } else { 181 proto.d_reclen = dirp->d_reclen; 182 bcopy((char *)&proto, (char *)dirp, entrysize); 183 if (reply("FIX") == 1) 184 ret |= ALTERED; 185 } 186 chk2: 187 if (dirp->d_ino == 0) 188 return (ret|KEEPON); 189 if (dirp->d_namlen <= 2 && 190 dirp->d_name[0] == '.' && 191 idesc->id_entryno >= 2) { 192 if (dirp->d_namlen == 1) { 193 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 194 dirp->d_ino = 0; 195 if (reply("FIX") == 1) 196 ret |= ALTERED; 197 return (KEEPON | ret); 198 } 199 if (dirp->d_name[1] == '.') { 200 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 201 dirp->d_ino = 0; 202 if (reply("FIX") == 1) 203 ret |= ALTERED; 204 return (KEEPON | ret); 205 } 206 } 207 curpathloc = pathp; 208 *pathp++ = '/'; 209 if (pathp + dirp->d_namlen >= endpathname) { 210 *pathp = '\0'; 211 errexit("NAME TOO LONG %s%s\n", pathname, dirp->d_name); 212 } 213 bcopy(dirp->d_name, pathp, (int)dirp->d_namlen + 1); 214 pathp += dirp->d_namlen; 215 idesc->id_entryno++; 216 n = 0; 217 if (dirp->d_ino > maxino || dirp->d_ino <= 0) { 218 direrror(dirp->d_ino, "I OUT OF RANGE"); 219 n = reply("REMOVE"); 220 } else { 221 again: 222 switch (statemap[dirp->d_ino]) { 223 case USTATE: 224 direrror(dirp->d_ino, "UNALLOCATED"); 225 n = reply("REMOVE"); 226 break; 227 228 case DCLEAR: 229 case FCLEAR: 230 direrror(dirp->d_ino, "DUP/BAD"); 231 if ((n = reply("REMOVE")) == 1) 232 break; 233 dp = ginode(dirp->d_ino); 234 statemap[dirp->d_ino] = 235 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 236 lncntp[dirp->d_ino] = dp->di_nlink; 237 goto again; 238 239 case DFOUND: 240 if (idesc->id_entryno > 2) { 241 getpathname(namebuf, dirp->d_ino, dirp->d_ino); 242 pwarn("%s %s %s\n", pathname, 243 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 244 namebuf); 245 if (preen) 246 printf(" (IGNORED)\n"); 247 else if ((n = reply("REMOVE")) == 1) 248 break; 249 } 250 /* fall through */ 251 252 case FSTATE: 253 lncntp[dirp->d_ino]--; 254 break; 255 256 case DSTATE: 257 descend(idesc, dirp->d_ino); 258 if (statemap[dirp->d_ino] == DFOUND) { 259 lncntp[dirp->d_ino]--; 260 } else if (statemap[dirp->d_ino] == DCLEAR) { 261 dirp->d_ino = 0; 262 ret |= ALTERED; 263 } else 264 errexit("BAD RETURN STATE %d FROM DESCEND", 265 statemap[dirp->d_ino]); 266 break; 267 268 default: 269 errexit("BAD STATE %d FOR INODE I=%d", 270 statemap[dirp->d_ino], dirp->d_ino); 271 } 272 } 273 pathp = curpathloc; 274 *pathp = '\0'; 275 if (n == 0) 276 return (ret|KEEPON); 277 dirp->d_ino = 0; 278 return (ret|KEEPON|ALTERED); 279 } 280