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