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