1 /* $NetBSD: pass2.c,v 1.6 2000/01/28 16:01:46 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 #include <sys/cdefs.h> 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)pass2.c 8.6 (Berkeley) 10/27/94"; 41 #else 42 __RCSID("$NetBSD: pass2.c,v 1.6 2000/01/28 16:01:46 bouyer Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/time.h> 48 #include <ufs/ext2fs/ext2fs_dinode.h> 49 #include <ufs/ext2fs/ext2fs_dir.h> 50 #include <ufs/ext2fs/ext2fs.h> 51 52 #include <ufs/ufs/dinode.h> /* for IFMT & friends */ 53 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 58 #include "fsck.h" 59 #include "fsutil.h" 60 #include "extern.h" 61 62 #define MINDIRSIZE (sizeof (struct ext2fs_dirtemplate)) 63 64 static int pass2check __P((struct inodesc *)); 65 static int blksort __P((const void *, const void *)); 66 67 void 68 pass2() 69 { 70 struct ext2fs_dinode *dp; 71 struct inoinfo **inpp, *inp; 72 struct inoinfo **inpend; 73 struct inodesc curino; 74 struct ext2fs_dinode dino; 75 char pathbuf[MAXPATHLEN + 1]; 76 77 switch (statemap[EXT2_ROOTINO]) { 78 79 case USTATE: 80 pfatal("ROOT INODE UNALLOCATED"); 81 if (reply("ALLOCATE") == 0) 82 errexit("%s\n", ""); 83 if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO) 84 errexit("CANNOT ALLOCATE ROOT INODE\n"); 85 break; 86 87 case DCLEAR: 88 pfatal("DUPS/BAD IN ROOT INODE"); 89 if (reply("REALLOCATE")) { 90 freeino(EXT2_ROOTINO); 91 if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO) 92 errexit("CANNOT ALLOCATE ROOT INODE\n"); 93 break; 94 } 95 if (reply("CONTINUE") == 0) 96 errexit("%s\n", ""); 97 break; 98 99 case FSTATE: 100 case FCLEAR: 101 pfatal("ROOT INODE NOT DIRECTORY"); 102 if (reply("REALLOCATE")) { 103 freeino(EXT2_ROOTINO); 104 if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO) 105 errexit("CANNOT ALLOCATE ROOT INODE\n"); 106 break; 107 } 108 if (reply("FIX") == 0) 109 errexit("%s\n", ""); 110 dp = ginode(EXT2_ROOTINO); 111 dp->e2di_mode = h2fs16((fs2h16(dp->e2di_mode) & ~IFMT) | 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 = h2fs32(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 %lu NOT MULTIPLE OF %d", 148 pathbuf, (u_long)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 = h2fs32(inp->i_isize); 155 inodirty(); 156 } 157 } 158 memset(&dino, 0, sizeof(struct ext2fs_dinode)); 159 dino.e2di_mode = h2fs16(IFDIR); 160 dino.e2di_size = h2fs32(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 struct ext2fs_direct *dirp = idesc->id_dirp; 206 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 (fs2h32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 1 && 220 dirp->e2d_name[0] == '.') { 221 if (fs2h32(dirp->e2d_ino) != idesc->id_number) { 222 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 223 dirp->e2d_ino = h2fs32(idesc->id_number); 224 if (reply("FIX") == 1) 225 ret |= ALTERED; 226 } 227 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 228 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) 229 && (dirp->e2d_type != EXT2_FT_DIR)) { 230 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 231 dirp->e2d_type = EXT2_FT_DIR; 232 if (reply("FIX") == 1) 233 ret |= ALTERED; 234 } 235 goto chk1; 236 } 237 direrror(idesc->id_number, "MISSING '.'"); 238 proto.e2d_ino = h2fs32(idesc->id_number); 239 proto.e2d_namlen = 1; 240 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 241 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 242 proto.e2d_type = EXT2_FT_DIR; 243 else 244 proto.e2d_type = 0; 245 (void)strcpy(proto.e2d_name, "."); 246 entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen); 247 if (fs2h32(dirp->e2d_ino) != 0 && strcmp(dirp->e2d_name, "..") != 0) { 248 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 249 dirp->e2d_name); 250 } else if (fs2h16(dirp->e2d_reclen) < entrysize) { 251 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 252 } else if (fs2h16(dirp->e2d_reclen) < 2 * entrysize) { 253 proto.e2d_reclen = dirp->e2d_reclen; 254 memcpy(dirp, &proto, (size_t)entrysize); 255 if (reply("FIX") == 1) 256 ret |= ALTERED; 257 } else { 258 n = fs2h16(dirp->e2d_reclen) - entrysize; 259 proto.e2d_reclen = h2fs16(entrysize); 260 memcpy(dirp, &proto, (size_t)entrysize); 261 idesc->id_entryno++; 262 lncntp[fs2h32(dirp->e2d_ino)]--; 263 dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize); 264 memset(dirp, 0, (size_t)n); 265 dirp->e2d_reclen = h2fs16(n); 266 if (reply("FIX") == 1) 267 ret |= ALTERED; 268 } 269 chk1: 270 if (idesc->id_entryno > 1) 271 goto chk2; 272 inp = getinoinfo(idesc->id_number); 273 proto.e2d_ino = h2fs32(inp->i_parent); 274 proto.e2d_namlen = 2; 275 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 276 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 277 proto.e2d_type = EXT2_FT_DIR; 278 else 279 proto.e2d_type = 0; 280 (void)strcpy(proto.e2d_name, ".."); 281 entrysize = EXT2FS_DIRSIZ(2); 282 if (idesc->id_entryno == 0) { 283 n = EXT2FS_DIRSIZ(dirp->e2d_namlen); 284 if (fs2h16(dirp->e2d_reclen) < n + entrysize) 285 goto chk2; 286 proto.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - n); 287 dirp->e2d_reclen = h2fs16(n); 288 idesc->id_entryno++; 289 lncntp[fs2h32(dirp->e2d_ino)]--; 290 dirp = (struct ext2fs_direct *)((char *)(dirp) + n); 291 memset(dirp, 0, (size_t)fs2h16(proto.e2d_reclen)); 292 dirp->e2d_reclen = proto.e2d_reclen; 293 } 294 if (fs2h32(dirp->e2d_ino) != 0 && 295 dirp->e2d_namlen == 2 && 296 strncmp(dirp->e2d_name, "..", 2) == 0) { 297 inp->i_dotdot = fs2h32(dirp->e2d_ino); 298 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 299 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) 300 && dirp->e2d_type != EXT2_FT_DIR) { 301 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 302 dirp->e2d_type = EXT2_FT_DIR; 303 if (reply("FIX") == 1) 304 ret |= ALTERED; 305 } 306 goto chk2; 307 } 308 if (fs2h32(dirp->e2d_ino) != 0 && 309 dirp->e2d_namlen == 1 && 310 strncmp(dirp->e2d_name, ".", 1) != 0) { 311 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 312 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 313 dirp->e2d_name); 314 inp->i_dotdot = (ino_t)-1; 315 } else if (fs2h16(dirp->e2d_reclen) < entrysize) { 316 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 317 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 318 inp->i_dotdot = (ino_t)-1; 319 } else if (inp->i_parent != 0) { 320 /* 321 * We know the parent, so fix now. 322 */ 323 inp->i_dotdot = inp->i_parent; 324 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 325 proto.e2d_reclen = dirp->e2d_reclen; 326 memcpy(dirp, &proto, (size_t)entrysize); 327 if (reply("FIX") == 1) 328 ret |= ALTERED; 329 } 330 idesc->id_entryno++; 331 if (fs2h32(dirp->e2d_ino) != 0) 332 lncntp[fs2h32(dirp->e2d_ino)]--; 333 return (ret|KEEPON); 334 chk2: 335 if (fs2h32(dirp->e2d_ino) == 0) 336 return (ret|KEEPON); 337 if (dirp->e2d_namlen <= 2 && 338 dirp->e2d_name[0] == '.' && 339 idesc->id_entryno >= 2) { 340 if (dirp->e2d_namlen == 1) { 341 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 342 dirp->e2d_ino = 0; 343 if (reply("FIX") == 1) 344 ret |= ALTERED; 345 return (KEEPON | ret); 346 } 347 if (dirp->e2d_name[1] == '.') { 348 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 349 dirp->e2d_ino = 0; 350 if (reply("FIX") == 1) 351 ret |= ALTERED; 352 return (KEEPON | ret); 353 } 354 } 355 idesc->id_entryno++; 356 n = 0; 357 if (fs2h32(dirp->e2d_ino) > maxino || 358 (fs2h32(dirp->e2d_ino) < EXT2_FIRSTINO && 359 fs2h32(dirp->e2d_ino) != EXT2_ROOTINO)) { 360 fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "I OUT OF RANGE"); 361 n = reply("REMOVE"); 362 } else { 363 again: 364 switch (statemap[fs2h32(dirp->e2d_ino)]) { 365 case USTATE: 366 if (idesc->id_entryno <= 2) 367 break; 368 fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "UNALLOCATED"); 369 n = reply("REMOVE"); 370 break; 371 372 case DCLEAR: 373 case FCLEAR: 374 if (idesc->id_entryno <= 2) 375 break; 376 if (statemap[fs2h32(dirp->e2d_ino)] == FCLEAR) 377 errmsg = "DUP/BAD"; 378 else if (!preen) 379 errmsg = "ZERO LENGTH DIRECTORY"; 380 else { 381 n = 1; 382 break; 383 } 384 fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), errmsg); 385 if ((n = reply("REMOVE")) == 1) 386 break; 387 dp = ginode(fs2h32(dirp->e2d_ino)); 388 statemap[fs2h32(dirp->e2d_ino)] = 389 (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 390 lncntp[fs2h32(dirp->e2d_ino)] = fs2h16(dp->e2di_nlink); 391 goto again; 392 393 case DSTATE: 394 case DFOUND: 395 inp = getinoinfo(fs2h32(dirp->e2d_ino)); 396 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 397 getpathname(pathbuf, idesc->id_number, 398 idesc->id_number); 399 getpathname(namebuf, fs2h32(dirp->e2d_ino), 400 fs2h32(dirp->e2d_ino)); 401 pwarn("%s %s %s\n", pathbuf, 402 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 403 namebuf); 404 if (preen) 405 printf(" (IGNORED)\n"); 406 else if ((n = reply("REMOVE")) == 1) 407 break; 408 } 409 if (idesc->id_entryno > 2) 410 inp->i_parent = idesc->id_number; 411 /* fall through */ 412 413 case FSTATE: 414 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 415 (sblock.e2fs.e2fs_features_incompat & 416 EXT2F_INCOMPAT_FTYPE) && 417 dirp->e2d_type != 418 inot2ext2dt(typemap[fs2h32(dirp->e2d_ino)])) { 419 dirp->e2d_type = 420 inot2ext2dt(typemap[fs2h32(dirp->e2d_ino)]); 421 fileerror(idesc->id_number, 422 fs2h32(dirp->e2d_ino), 423 "BAD TYPE VALUE"); 424 if (reply("FIX") == 1) 425 ret |= ALTERED; 426 } 427 lncntp[fs2h32(dirp->e2d_ino)]--; 428 break; 429 430 default: 431 errexit("BAD STATE %d FOR INODE I=%d\n", 432 statemap[fs2h32(dirp->e2d_ino)], fs2h32(dirp->e2d_ino)); 433 } 434 } 435 if (n == 0) 436 return (ret|KEEPON); 437 dirp->e2d_ino = 0; 438 return (ret|KEEPON|ALTERED); 439 } 440 441 /* 442 * Routine to sort disk blocks. 443 */ 444 static int 445 blksort(inpp1, inpp2) 446 const void *inpp1, *inpp2; 447 { 448 return ((* (struct inoinfo **) inpp1)->i_blks[0] - 449 (* (struct inoinfo **) inpp2)->i_blks[0]); 450 } 451