1 /* $NetBSD: pass2.c,v 1.4 1997/10/09 13:19:38 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.4 1997/10/09 13:19:38 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 && fs2h16(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 goto chk1; 228 } 229 direrror(idesc->id_number, "MISSING '.'"); 230 proto.e2d_ino = h2fs32(idesc->id_number); 231 proto.e2d_namlen = h2fs16(1); 232 (void)strcpy(proto.e2d_name, "."); 233 entrysize = EXT2FS_DIRSIZ(fs2h16(proto.e2d_namlen)); 234 if (fs2h32(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 (fs2h16(dirp->e2d_reclen) < entrysize) { 238 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 239 } else if (fs2h16(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 = fs2h16(dirp->e2d_reclen) - entrysize; 246 proto.e2d_reclen = h2fs16(entrysize); 247 memcpy(dirp, &proto, (size_t)entrysize); 248 idesc->id_entryno++; 249 lncntp[fs2h32(dirp->e2d_ino)]--; 250 dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize); 251 memset(dirp, 0, (size_t)n); 252 dirp->e2d_reclen = h2fs16(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 = h2fs32(inp->i_parent); 261 proto.e2d_namlen = h2fs16(2); 262 (void)strcpy(proto.e2d_name, ".."); 263 entrysize = EXT2FS_DIRSIZ(2); 264 if (idesc->id_entryno == 0) { 265 n = EXT2FS_DIRSIZ(fs2h16(dirp->e2d_namlen)); 266 if (fs2h16(dirp->e2d_reclen) < n + entrysize) 267 goto chk2; 268 proto.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - n); 269 dirp->e2d_reclen = h2fs16(n); 270 idesc->id_entryno++; 271 lncntp[fs2h32(dirp->e2d_ino)]--; 272 dirp = (struct ext2fs_direct *)((char *)(dirp) + n); 273 memset(dirp, 0, (size_t)fs2h16(proto.e2d_reclen)); 274 dirp->e2d_reclen = proto.e2d_reclen; 275 } 276 if (fs2h32(dirp->e2d_ino) != 0 && 277 fs2h16(dirp->e2d_namlen) == 2 && 278 strncmp(dirp->e2d_name, "..", 2) == 0) { 279 inp->i_dotdot = fs2h32(dirp->e2d_ino); 280 goto chk2; 281 } 282 if (fs2h32(dirp->e2d_ino) != 0 && 283 fs2h16(dirp->e2d_namlen) == 1 && 284 strncmp(dirp->e2d_name, ".", 1) != 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 (fs2h16(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 (fs2h32(dirp->e2d_ino) != 0) 306 lncntp[fs2h32(dirp->e2d_ino)]--; 307 return (ret|KEEPON); 308 chk2: 309 if (fs2h32(dirp->e2d_ino) == 0) 310 return (ret|KEEPON); 311 if (fs2h16(dirp->e2d_namlen) <= 2 && 312 dirp->e2d_name[0] == '.' && 313 idesc->id_entryno >= 2) { 314 if (fs2h16(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 (fs2h32(dirp->e2d_ino) > maxino || 332 (fs2h32(dirp->e2d_ino) < EXT2_FIRSTINO && 333 fs2h32(dirp->e2d_ino) != EXT2_ROOTINO)) { 334 fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "I OUT OF RANGE"); 335 n = reply("REMOVE"); 336 } else { 337 again: 338 switch (statemap[fs2h32(dirp->e2d_ino)]) { 339 case USTATE: 340 if (idesc->id_entryno <= 2) 341 break; 342 fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "UNALLOCATED"); 343 n = reply("REMOVE"); 344 break; 345 346 case DCLEAR: 347 case FCLEAR: 348 if (idesc->id_entryno <= 2) 349 break; 350 if (statemap[fs2h32(dirp->e2d_ino)] == FCLEAR) 351 errmsg = "DUP/BAD"; 352 else if (!preen) 353 errmsg = "ZERO LENGTH DIRECTORY"; 354 else { 355 n = 1; 356 break; 357 } 358 fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), errmsg); 359 if ((n = reply("REMOVE")) == 1) 360 break; 361 dp = ginode(fs2h32(dirp->e2d_ino)); 362 statemap[fs2h32(dirp->e2d_ino)] = 363 (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 364 lncntp[fs2h32(dirp->e2d_ino)] = fs2h16(dp->e2di_nlink); 365 goto again; 366 367 case DSTATE: 368 case DFOUND: 369 inp = getinoinfo(fs2h32(dirp->e2d_ino)); 370 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 371 getpathname(pathbuf, idesc->id_number, 372 idesc->id_number); 373 getpathname(namebuf, fs2h32(dirp->e2d_ino), 374 fs2h32(dirp->e2d_ino)); 375 pwarn("%s %s %s\n", pathbuf, 376 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 377 namebuf); 378 if (preen) 379 printf(" (IGNORED)\n"); 380 else if ((n = reply("REMOVE")) == 1) 381 break; 382 } 383 if (idesc->id_entryno > 2) 384 inp->i_parent = idesc->id_number; 385 /* fall through */ 386 387 case FSTATE: 388 lncntp[fs2h32(dirp->e2d_ino)]--; 389 break; 390 391 default: 392 errexit("BAD STATE %d FOR INODE I=%d\n", 393 statemap[fs2h32(dirp->e2d_ino)], fs2h32(dirp->e2d_ino)); 394 } 395 } 396 if (n == 0) 397 return (ret|KEEPON); 398 dirp->e2d_ino = 0; 399 return (ret|KEEPON|ALTERED); 400 } 401 402 /* 403 * Routine to sort disk blocks. 404 */ 405 static int 406 blksort(inpp1, inpp2) 407 const void *inpp1, *inpp2; 408 { 409 return ((* (struct inoinfo **) inpp1)->i_blks[0] - 410 (* (struct inoinfo **) inpp2)->i_blks[0]); 411 } 412