1 /* $NetBSD: pass2.c,v 1.1 1997/06/11 11:21:53 bouyer Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Manuel Bouyer. 5 * Copyright (c) 1980, 1986, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)pass2.c 8.6 (Berkeley) 10/27/94"; 40 #else 41 static char rcsid[] = "$NetBSD: pass2.c,v 1.1 1997/06/11 11:21:53 bouyer Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/time.h> 47 #include <ufs/ext2fs/ext2fs_dinode.h> 48 #include <ufs/ext2fs/ext2fs_dir.h> 49 #include <ufs/ext2fs/ext2fs.h> 50 51 #include <ufs/ufs/dinode.h> /* for IFMT & friends */ 52 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 57 #include "fsck.h" 58 #include "fsutil.h" 59 #include "extern.h" 60 61 #define MINDIRSIZE (sizeof (struct ext2fs_dirtemplate)) 62 63 static int pass2check __P((struct inodesc *)); 64 static int blksort __P((const void *, const void *)); 65 66 void 67 pass2() 68 { 69 register struct ext2fs_dinode *dp; 70 register struct inoinfo **inpp, *inp; 71 struct inoinfo **inpend; 72 struct inodesc curino; 73 struct ext2fs_dinode dino; 74 char pathbuf[MAXPATHLEN + 1]; 75 76 switch (statemap[EXT2_ROOTINO]) { 77 78 case USTATE: 79 pfatal("ROOT INODE UNALLOCATED"); 80 if (reply("ALLOCATE") == 0) 81 errexit("%s\n", ""); 82 if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO) 83 errexit("CANNOT ALLOCATE ROOT INODE\n"); 84 break; 85 86 case DCLEAR: 87 pfatal("DUPS/BAD IN ROOT INODE"); 88 if (reply("REALLOCATE")) { 89 freeino(EXT2_ROOTINO); 90 if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO) 91 errexit("CANNOT ALLOCATE ROOT INODE\n"); 92 break; 93 } 94 if (reply("CONTINUE") == 0) 95 errexit("%s\n", ""); 96 break; 97 98 case FSTATE: 99 case FCLEAR: 100 pfatal("ROOT INODE NOT DIRECTORY"); 101 if (reply("REALLOCATE")) { 102 freeino(EXT2_ROOTINO); 103 if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO) 104 errexit("CANNOT ALLOCATE ROOT INODE\n"); 105 break; 106 } 107 if (reply("FIX") == 0) 108 errexit("%s\n", ""); 109 dp = ginode(EXT2_ROOTINO); 110 dp->e2di_mode &= ~IFMT; 111 dp->e2di_mode |= IFDIR; 112 inodirty(); 113 break; 114 115 case DSTATE: 116 break; 117 118 default: 119 errexit("BAD STATE %d FOR ROOT INODE\n", statemap[EXT2_ROOTINO]); 120 } 121 122 /* 123 * Sort the directory list into disk block order. 124 */ 125 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 126 /* 127 * Check the integrity of each directory. 128 */ 129 memset(&curino, 0, sizeof(struct inodesc)); 130 curino.id_type = DATA; 131 curino.id_func = pass2check; 132 inpend = &inpsort[inplast]; 133 for (inpp = inpsort; inpp < inpend; inpp++) { 134 inp = *inpp; 135 if (inp->i_isize == 0) 136 continue; 137 if (inp->i_isize < MINDIRSIZE) { 138 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 139 inp->i_isize = roundup(MINDIRSIZE, sblock.e2fs_bsize); 140 if (reply("FIX") == 1) { 141 dp = ginode(inp->i_number); 142 dp->e2di_size = inp->i_isize; 143 inodirty(); 144 } 145 } else if ((inp->i_isize & (sblock.e2fs_bsize - 1)) != 0) { 146 getpathname(pathbuf, inp->i_number, inp->i_number); 147 pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", 148 pathbuf, inp->i_isize, sblock.e2fs_bsize); 149 if (preen) 150 printf(" (ADJUSTED)\n"); 151 inp->i_isize = roundup(inp->i_isize, sblock.e2fs_bsize); 152 if (preen || reply("ADJUST") == 1) { 153 dp = ginode(inp->i_number); 154 dp->e2di_size = inp->i_isize; 155 inodirty(); 156 } 157 } 158 memset(&dino, 0, sizeof(struct ext2fs_dinode)); 159 dino.e2di_mode = IFDIR; 160 dino.e2di_size = inp->i_isize; 161 memcpy(&dino.e2di_blocks[0], &inp->i_blks[0], (size_t)inp->i_numblks); 162 curino.id_number = inp->i_number; 163 curino.id_parent = inp->i_parent; 164 (void)ckinode(&dino, &curino); 165 } 166 /* 167 * Now that the parents of all directories have been found, 168 * make another pass to verify the value of `..' 169 */ 170 for (inpp = inpsort; inpp < inpend; inpp++) { 171 inp = *inpp; 172 if (inp->i_parent == 0 || inp->i_isize == 0) 173 continue; 174 if (inp->i_dotdot == inp->i_parent || 175 inp->i_dotdot == (ino_t)-1) 176 continue; 177 if (inp->i_dotdot == 0) { 178 inp->i_dotdot = inp->i_parent; 179 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 180 if (reply("FIX") == 0) 181 continue; 182 (void)makeentry(inp->i_number, inp->i_parent, ".."); 183 lncntp[inp->i_parent]--; 184 continue; 185 } 186 fileerror(inp->i_parent, inp->i_number, 187 "BAD INODE NUMBER FOR '..'"); 188 if (reply("FIX") == 0) 189 continue; 190 lncntp[inp->i_dotdot]++; 191 lncntp[inp->i_parent]--; 192 inp->i_dotdot = inp->i_parent; 193 (void)changeino(inp->i_number, "..", inp->i_parent); 194 } 195 /* 196 * Mark all the directories that can be found from the root. 197 */ 198 propagate(); 199 } 200 201 static int 202 pass2check(idesc) 203 struct inodesc *idesc; 204 { 205 register struct ext2fs_direct *dirp = idesc->id_dirp; 206 register struct inoinfo *inp; 207 int n, entrysize, ret = 0; 208 struct ext2fs_dinode *dp; 209 char *errmsg; 210 struct ext2fs_direct proto; 211 char namebuf[MAXPATHLEN + 1]; 212 char pathbuf[MAXPATHLEN + 1]; 213 214 /* 215 * check for "." 216 */ 217 if (idesc->id_entryno != 0) 218 goto chk1; 219 if (dirp->e2d_ino != 0 && dirp->e2d_namlen == 1 && 220 dirp->e2d_name[0] == '.') { 221 if (dirp->e2d_ino != idesc->id_number) { 222 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 223 dirp->e2d_ino = idesc->id_number; 224 if (reply("FIX") == 1) 225 ret |= ALTERED; 226 } 227 goto chk1; 228 } 229 direrror(idesc->id_number, "MISSING '.'"); 230 proto.e2d_ino = idesc->id_number; 231 proto.e2d_namlen = 1; 232 (void)strcpy(proto.e2d_name, "."); 233 entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen); 234 if (dirp->e2d_ino != 0 && strcmp(dirp->e2d_name, "..") != 0) { 235 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 236 dirp->e2d_name); 237 } else if (dirp->e2d_reclen < entrysize) { 238 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 239 } else if (dirp->e2d_reclen < 2 * entrysize) { 240 proto.e2d_reclen = dirp->e2d_reclen; 241 memcpy(dirp, &proto, (size_t)entrysize); 242 if (reply("FIX") == 1) 243 ret |= ALTERED; 244 } else { 245 n = dirp->e2d_reclen - entrysize; 246 proto.e2d_reclen = entrysize; 247 memcpy(dirp, &proto, (size_t)entrysize); 248 idesc->id_entryno++; 249 lncntp[dirp->e2d_ino]--; 250 dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize); 251 memset(dirp, 0, (size_t)n); 252 dirp->e2d_reclen = n; 253 if (reply("FIX") == 1) 254 ret |= ALTERED; 255 } 256 chk1: 257 if (idesc->id_entryno > 1) 258 goto chk2; 259 inp = getinoinfo(idesc->id_number); 260 proto.e2d_ino = inp->i_parent; 261 proto.e2d_namlen = 2; 262 (void)strcpy(proto.e2d_name, ".."); 263 entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen); 264 if (idesc->id_entryno == 0) { 265 n = EXT2FS_DIRSIZ(dirp->e2d_namlen); 266 if (dirp->e2d_reclen < n + entrysize) 267 goto chk2; 268 proto.e2d_reclen = dirp->e2d_reclen - n; 269 dirp->e2d_reclen = n; 270 idesc->id_entryno++; 271 lncntp[dirp->e2d_ino]--; 272 dirp = (struct ext2fs_direct *)((char *)(dirp) + n); 273 memset(dirp, 0, (size_t)proto.e2d_reclen); 274 dirp->e2d_reclen = proto.e2d_reclen; 275 } 276 if (dirp->e2d_ino != 0 && 277 dirp->e2d_namlen == 2 && 278 strncmp(dirp->e2d_name, "..", dirp->e2d_namlen) == 0) { 279 inp->i_dotdot = dirp->e2d_ino; 280 goto chk2; 281 } 282 if (dirp->e2d_ino != 0 && 283 dirp->e2d_namlen == 1 && 284 strncmp(dirp->e2d_name, ".", dirp->e2d_namlen) != 0) { 285 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 286 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 287 dirp->e2d_name); 288 inp->i_dotdot = (ino_t)-1; 289 } else if (dirp->e2d_reclen < entrysize) { 290 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 291 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 292 inp->i_dotdot = (ino_t)-1; 293 } else if (inp->i_parent != 0) { 294 /* 295 * We know the parent, so fix now. 296 */ 297 inp->i_dotdot = inp->i_parent; 298 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 299 proto.e2d_reclen = dirp->e2d_reclen; 300 memcpy(dirp, &proto, (size_t)entrysize); 301 if (reply("FIX") == 1) 302 ret |= ALTERED; 303 } 304 idesc->id_entryno++; 305 if (dirp->e2d_ino != 0) 306 lncntp[dirp->e2d_ino]--; 307 return (ret|KEEPON); 308 chk2: 309 if (dirp->e2d_ino == 0) 310 return (ret|KEEPON); 311 if (dirp->e2d_namlen <= 2 && 312 dirp->e2d_name[0] == '.' && 313 idesc->id_entryno >= 2) { 314 if (dirp->e2d_namlen == 1) { 315 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 316 dirp->e2d_ino = 0; 317 if (reply("FIX") == 1) 318 ret |= ALTERED; 319 return (KEEPON | ret); 320 } 321 if (dirp->e2d_name[1] == '.') { 322 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 323 dirp->e2d_ino = 0; 324 if (reply("FIX") == 1) 325 ret |= ALTERED; 326 return (KEEPON | ret); 327 } 328 } 329 idesc->id_entryno++; 330 n = 0; 331 if (dirp->e2d_ino > maxino || 332 (dirp->e2d_ino < EXT2_FIRSTINO && dirp->e2d_ino != EXT2_ROOTINO)) { 333 fileerror(idesc->id_number, dirp->e2d_ino, "I OUT OF RANGE"); 334 n = reply("REMOVE"); 335 } else { 336 again: 337 switch (statemap[dirp->e2d_ino]) { 338 case USTATE: 339 if (idesc->id_entryno <= 2) 340 break; 341 fileerror(idesc->id_number, dirp->e2d_ino, "UNALLOCATED"); 342 n = reply("REMOVE"); 343 break; 344 345 case DCLEAR: 346 case FCLEAR: 347 if (idesc->id_entryno <= 2) 348 break; 349 if (statemap[dirp->e2d_ino] == FCLEAR) 350 errmsg = "DUP/BAD"; 351 else if (!preen) 352 errmsg = "ZERO LENGTH DIRECTORY"; 353 else { 354 n = 1; 355 break; 356 } 357 fileerror(idesc->id_number, dirp->e2d_ino, errmsg); 358 if ((n = reply("REMOVE")) == 1) 359 break; 360 dp = ginode(dirp->e2d_ino); 361 statemap[dirp->e2d_ino] = 362 (dp->e2di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 363 lncntp[dirp->e2d_ino] = dp->e2di_nlink; 364 goto again; 365 366 case DSTATE: 367 case DFOUND: 368 inp = getinoinfo(dirp->e2d_ino); 369 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 370 getpathname(pathbuf, idesc->id_number, 371 idesc->id_number); 372 getpathname(namebuf, dirp->e2d_ino, dirp->e2d_ino); 373 pwarn("%s %s %s\n", pathbuf, 374 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 375 namebuf); 376 if (preen) 377 printf(" (IGNORED)\n"); 378 else if ((n = reply("REMOVE")) == 1) 379 break; 380 } 381 if (idesc->id_entryno > 2) 382 inp->i_parent = idesc->id_number; 383 /* fall through */ 384 385 case FSTATE: 386 lncntp[dirp->e2d_ino]--; 387 break; 388 389 default: 390 errexit("BAD STATE %d FOR INODE I=%d\n", 391 statemap[dirp->e2d_ino], dirp->e2d_ino); 392 } 393 } 394 if (n == 0) 395 return (ret|KEEPON); 396 dirp->e2d_ino = 0; 397 return (ret|KEEPON|ALTERED); 398 } 399 400 /* 401 * Routine to sort disk blocks. 402 */ 403 static int 404 blksort(inpp1, inpp2) 405 const void *inpp1, *inpp2; 406 { 407 return ((* (struct inoinfo **) inpp1)->i_blks[0] - 408 (* (struct inoinfo **) inpp2)->i_blks[0]); 409 } 410