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