1 /* $NetBSD: pass2.c,v 1.34 2015/09/21 01:24:23 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_accessors.h> 40 #include <ufs/lfs/lfs_inode.h> 41 42 #include <err.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "bufcache.h" 48 #include "vnode.h" 49 #include "lfs_user.h" 50 51 #include "fsck.h" 52 #include "fsutil.h" 53 #include "extern.h" 54 55 #define MINDIRSIZE(fs) \ 56 ((fs)->lfs_is64 ? sizeof(struct lfs_dirtemplate64) : \ 57 sizeof(struct lfs_dirtemplate32)) 58 59 static int pass2check(struct inodesc *); 60 static int blksort(const void *, const void *); 61 62 void 63 pass2(void) 64 { 65 union lfs_dinode *dp; 66 struct uvnode *vp; 67 struct inoinfo **inpp, *inp; 68 struct inoinfo **inpend; 69 struct inodesc curino; 70 union lfs_dinode dino; 71 char pathbuf[MAXPATHLEN + 1]; 72 uint16_t mode; 73 unsigned ii; 74 75 switch (statemap[ULFS_ROOTINO]) { 76 77 case USTATE: 78 pfatal("ROOT INODE UNALLOCATED"); 79 if (reply("ALLOCATE") == 0) 80 err(EEXIT, "%s", ""); 81 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO) 82 err(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 83 break; 84 85 case DCLEAR: 86 pfatal("DUPS/BAD IN ROOT INODE"); 87 if (reply("REALLOCATE")) { 88 freeino(ULFS_ROOTINO); 89 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO) 90 err(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 91 break; 92 } 93 if (reply("CONTINUE") == 0) 94 err(EEXIT, "%s", ""); 95 break; 96 97 case FSTATE: 98 case FCLEAR: 99 pfatal("ROOT INODE NOT DIRECTORY"); 100 if (reply("REALLOCATE")) { 101 freeino(ULFS_ROOTINO); 102 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO) 103 err(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 104 break; 105 } 106 if (reply("FIX") == 0) 107 errx(EEXIT, "%s", ""); 108 vp = vget(fs, ULFS_ROOTINO); 109 dp = VTOD(vp); 110 mode = lfs_dino_getmode(fs, dp); 111 mode &= ~LFS_IFMT; 112 mode |= LFS_IFDIR; 113 lfs_dino_setmode(fs, dp, mode); 114 inodirty(VTOI(vp)); 115 break; 116 117 case DSTATE: 118 break; 119 120 default: 121 errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ULFS_ROOTINO]); 122 } 123 statemap[ULFS_WINO] = FSTATE; 124 typemap[ULFS_WINO] = LFS_DT_WHT; 125 /* 126 * Sort the directory list into disk block order. 127 */ 128 qsort((char *) inpsort, (size_t) inplast, sizeof *inpsort, blksort); 129 /* 130 * Check the integrity of each directory. 131 */ 132 memset(&curino, 0, sizeof(struct inodesc)); 133 curino.id_type = DATA; 134 curino.id_func = pass2check; 135 inpend = &inpsort[inplast]; 136 for (inpp = inpsort; inpp < inpend; inpp++) { 137 inp = *inpp; 138 if (inp->i_isize == 0) 139 continue; 140 if (inp->i_isize < MINDIRSIZE(fs)) { 141 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 142 inp->i_isize = roundup(MINDIRSIZE(fs), LFS_DIRBLKSIZ); 143 if (reply("FIX") == 1) { 144 vp = vget(fs, inp->i_number); 145 dp = VTOD(vp); 146 lfs_dino_setsize(fs, dp, inp->i_isize); 147 inodirty(VTOI(vp)); 148 } 149 } else if ((inp->i_isize & (LFS_DIRBLKSIZ - 1)) != 0) { 150 getpathname(pathbuf, sizeof(pathbuf), inp->i_number, 151 inp->i_number); 152 pwarn("DIRECTORY %s: LENGTH %lu NOT MULTIPLE OF %d", 153 pathbuf, (unsigned long) inp->i_isize, LFS_DIRBLKSIZ); 154 if (preen) 155 printf(" (ADJUSTED)\n"); 156 inp->i_isize = roundup(inp->i_isize, LFS_DIRBLKSIZ); 157 if (preen || reply("ADJUST") == 1) { 158 vp = vget(fs, inp->i_number); 159 dp = VTOD(vp); 160 lfs_dino_setsize(fs, dp, inp->i_isize); 161 inodirty(VTOI(vp)); 162 } 163 } 164 memset(&dino, 0, sizeof(dino)); 165 lfs_dino_setmode(fs, &dino, LFS_IFDIR); 166 lfs_dino_setsize(fs, &dino, inp->i_isize); 167 for (ii = 0; ii < inp->i_numblks / sizeof(inp->i_blks[0]) && 168 ii < ULFS_NDADDR; ii++) { 169 lfs_dino_setdb(fs, &dino, ii, inp->i_blks[ii]); 170 } 171 for (; ii < inp->i_numblks / sizeof(inp->i_blks[0]); ii++) { 172 lfs_dino_setib(fs, &dino, ii - ULFS_NDADDR, 173 inp->i_blks[ii]); 174 } 175 curino.id_number = inp->i_number; 176 curino.id_parent = inp->i_parent; 177 (void) ckinode(&dino, &curino); 178 } 179 /* 180 * Now that the parents of all directories have been found, 181 * make another pass to verify the value of `..' 182 */ 183 for (inpp = inpsort; inpp < inpend; inpp++) { 184 inp = *inpp; 185 if (inp->i_parent == 0 || inp->i_isize == 0) 186 continue; 187 if (inp->i_dotdot == inp->i_parent || 188 inp->i_dotdot == (ino_t) - 1) 189 continue; 190 if (inp->i_dotdot == 0) { 191 inp->i_dotdot = inp->i_parent; 192 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 193 if (reply("FIX") == 0) 194 continue; 195 (void) makeentry(inp->i_number, inp->i_parent, ".."); 196 lncntp[inp->i_parent]--; 197 continue; 198 } 199 fileerror(inp->i_parent, inp->i_number, 200 "BAD INODE NUMBER FOR '..'"); 201 if (reply("FIX") == 0) 202 continue; 203 lncntp[inp->i_dotdot]++; 204 lncntp[inp->i_parent]--; 205 inp->i_dotdot = inp->i_parent; 206 (void) changeino(inp->i_number, "..", inp->i_parent); 207 } 208 /* 209 * Mark all the directories that can be found from the root. 210 */ 211 propagate(); 212 } 213 214 static int 215 pass2check(struct inodesc * idesc) 216 { 217 LFS_DIRHEADER *dirp = idesc->id_dirp; 218 struct inoinfo *inp; 219 int n, entrysize, ret = 0; 220 union lfs_dinode *dp; 221 const char *errmsg; 222 LFS_DIRHEADER proto; 223 char namebuf[MAXPATHLEN + 1]; 224 char pathbuf[MAXPATHLEN + 1]; 225 226 /* 227 * check for "." 228 */ 229 if (idesc->id_entryno != 0) 230 goto chk1; 231 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), ".") == 0) { 232 if (lfs_dir_getino(fs, dirp) != idesc->id_number) { 233 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 234 if (reply("FIX") == 1) { 235 lfs_dir_setino(fs, dirp, idesc->id_number); 236 ret |= ALTERED; 237 } 238 } 239 if (lfs_dir_gettype(fs, dirp) != LFS_DT_DIR) { 240 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 241 if (reply("FIX") == 1) { 242 lfs_dir_settype(fs, dirp, LFS_DT_DIR); 243 ret |= ALTERED; 244 } 245 } 246 goto chk1; 247 } 248 direrror(idesc->id_number, "MISSING '.'"); 249 lfs_dir_setino(fs, &proto, idesc->id_number); 250 lfs_dir_settype(fs, &proto, LFS_DT_DIR); 251 lfs_dir_setnamlen(fs, &proto, 1); 252 entrysize = LFS_DIRECTSIZ(fs, 1); 253 lfs_dir_setreclen(fs, &proto, entrysize); 254 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), "..") != 0) { 255 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 256 lfs_dir_nameptr(fs, dirp)); 257 } else if (lfs_dir_getreclen(fs, dirp) < entrysize) { 258 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 259 } else if (lfs_dir_getreclen(fs, dirp) < 2 * entrysize) { 260 /* convert this entry to a . entry */ 261 lfs_dir_setreclen(fs, &proto, lfs_dir_getreclen(fs, dirp)); 262 memcpy(dirp, &proto, sizeof(proto)); 263 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), ".", 1, 264 lfs_dir_getreclen(fs, dirp)); 265 if (reply("FIX") == 1) 266 ret |= ALTERED; 267 } else { 268 /* split this entry and use the beginning for the . entry */ 269 n = lfs_dir_getreclen(fs, dirp) - entrysize; 270 memcpy(dirp, &proto, sizeof(proto)); 271 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), ".", 1, 272 lfs_dir_getreclen(fs, dirp)); 273 idesc->id_entryno++; 274 lncntp[lfs_dir_getino(fs, dirp)]--; 275 dirp = LFS_NEXTDIR(fs, dirp); 276 memset(dirp, 0, (size_t) n); 277 lfs_dir_setreclen(fs, dirp, n); 278 if (reply("FIX") == 1) 279 ret |= ALTERED; 280 } 281 chk1: 282 if (idesc->id_entryno > 1) 283 goto chk2; 284 inp = getinoinfo(idesc->id_number); 285 lfs_dir_setino(fs, &proto, inp->i_parent); 286 lfs_dir_settype(fs, &proto, LFS_DT_DIR); 287 lfs_dir_setnamlen(fs, &proto, 2); 288 entrysize = LFS_DIRECTSIZ(fs, 2); 289 lfs_dir_setreclen(fs, &proto, entrysize); 290 if (idesc->id_entryno == 0) { 291 n = LFS_DIRSIZ(fs, dirp); 292 if (lfs_dir_getreclen(fs, dirp) < n + entrysize) 293 goto chk2; 294 lfs_dir_setreclen(fs, &proto, lfs_dir_getreclen(fs, dirp) - n); 295 lfs_dir_setreclen(fs, dirp, n); 296 idesc->id_entryno++; 297 lncntp[lfs_dir_getino(fs, dirp)]--; 298 dirp = (LFS_DIRHEADER *) ((char *) (dirp) + n); 299 memset(dirp, 0, lfs_dir_getreclen(fs, &proto)); 300 lfs_dir_setreclen(fs, dirp, lfs_dir_getreclen(fs, &proto)); 301 } 302 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), "..") == 0) { 303 inp->i_dotdot = lfs_dir_getino(fs, dirp); 304 if (lfs_dir_gettype(fs, dirp) != LFS_DT_DIR) { 305 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 306 lfs_dir_settype(fs, dirp, LFS_DT_DIR); 307 if (reply("FIX") == 1) 308 ret |= ALTERED; 309 } 310 goto chk2; 311 } 312 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), ".") != 0) { 313 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 314 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 315 lfs_dir_nameptr(fs, dirp)); 316 inp->i_dotdot = (ino_t) - 1; 317 } else if (lfs_dir_getreclen(fs, dirp) < entrysize) { 318 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 319 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 320 inp->i_dotdot = (ino_t) - 1; 321 } else if (inp->i_parent != 0) { 322 /* 323 * We know the parent, so fix now. 324 */ 325 inp->i_dotdot = inp->i_parent; 326 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 327 lfs_dir_setreclen(fs, &proto, lfs_dir_getreclen(fs, dirp)); 328 memcpy(dirp, &proto, (size_t) entrysize); 329 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "..", 2, 330 lfs_dir_getreclen(fs, dirp)); 331 if (reply("FIX") == 1) 332 ret |= ALTERED; 333 } 334 idesc->id_entryno++; 335 if (lfs_dir_getino(fs, dirp) != 0) 336 lncntp[lfs_dir_getino(fs, dirp)]--; 337 return (ret | KEEPON); 338 chk2: 339 if (lfs_dir_getino(fs, dirp) == 0) 340 return (ret | KEEPON); 341 if (lfs_dir_getnamlen(fs, dirp) <= 2 && 342 lfs_dir_nameptr(fs, dirp)[0] == '.' && 343 idesc->id_entryno >= 2) { 344 if (lfs_dir_getnamlen(fs, dirp) == 1) { 345 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 346 if (reply("FIX") == 1) { 347 lfs_dir_setino(fs, dirp, 0); 348 ret |= ALTERED; 349 } 350 return (KEEPON | ret); 351 } 352 if (lfs_dir_nameptr(fs, dirp)[1] == '.') { 353 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 354 if (reply("FIX") == 1) { 355 lfs_dir_setino(fs, dirp, 0); 356 ret |= ALTERED; 357 } 358 return (KEEPON | ret); 359 } 360 } 361 idesc->id_entryno++; 362 n = 0; 363 if (lfs_dir_getino(fs, dirp) >= maxino) { 364 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), "I OUT OF RANGE"); 365 n = reply("REMOVE"); 366 } else if (lfs_dir_getino(fs, dirp) == LFS_IFILE_INUM && 367 idesc->id_number == ULFS_ROOTINO) { 368 if (lfs_dir_gettype(fs, dirp) != LFS_DT_REG) { 369 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), 370 "BAD TYPE FOR IFILE"); 371 if (reply("FIX") == 1) { 372 lfs_dir_settype(fs, dirp, LFS_DT_REG); 373 ret |= ALTERED; 374 } 375 } 376 } else if (((lfs_dir_getino(fs, dirp) == ULFS_WINO && lfs_dir_gettype(fs, dirp) != LFS_DT_WHT) || 377 (lfs_dir_getino(fs, dirp) != ULFS_WINO && lfs_dir_gettype(fs, dirp) == LFS_DT_WHT))) { 378 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), "BAD WHITEOUT ENTRY"); 379 if (reply("FIX") == 1) { 380 lfs_dir_setino(fs, dirp, ULFS_WINO); 381 lfs_dir_settype(fs, dirp, LFS_DT_WHT); 382 ret |= ALTERED; 383 } 384 } else { 385 again: 386 switch (statemap[lfs_dir_getino(fs, dirp)]) { 387 case USTATE: 388 if (idesc->id_entryno <= 2) 389 break; 390 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), 391 "UNALLOCATED"); 392 n = reply("REMOVE"); 393 break; 394 395 case DCLEAR: 396 case FCLEAR: 397 if (idesc->id_entryno <= 2) 398 break; 399 if (statemap[lfs_dir_getino(fs, dirp)] == FCLEAR) 400 errmsg = "DUP/BAD"; 401 else if (!preen) 402 errmsg = "ZERO LENGTH DIRECTORY"; 403 else { 404 n = 1; 405 break; 406 } 407 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), errmsg); 408 if ((n = reply("REMOVE")) == 1) 409 break; 410 dp = ginode(lfs_dir_getino(fs, dirp)); 411 statemap[lfs_dir_getino(fs, dirp)] = 412 (lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR ? DSTATE : FSTATE; 413 lncntp[lfs_dir_getino(fs, dirp)] = lfs_dino_getnlink(fs, dp); 414 goto again; 415 416 case DSTATE: 417 case DFOUND: 418 inp = getinoinfo(lfs_dir_getino(fs, dirp)); 419 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 420 getpathname(pathbuf, sizeof(pathbuf), 421 idesc->id_number, idesc->id_number); 422 getpathname(namebuf, sizeof(namebuf), 423 lfs_dir_getino(fs, dirp), 424 lfs_dir_getino(fs, dirp)); 425 pwarn("%s %s %s\n", pathbuf, 426 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 427 namebuf); 428 if (preen) 429 printf(" (IGNORED)\n"); 430 else if ((n = reply("REMOVE")) == 1) 431 break; 432 } 433 if (idesc->id_entryno > 2) 434 inp->i_parent = idesc->id_number; 435 /* fall through */ 436 437 case FSTATE: 438 if (lfs_dir_gettype(fs, dirp) != typemap[lfs_dir_getino(fs, dirp)]) { 439 fileerror(idesc->id_number, 440 lfs_dir_getino(fs, dirp), 441 "BAD TYPE VALUE"); 442 if (debug) 443 pwarn("dir has %d, typemap has %d\n", 444 lfs_dir_gettype(fs, dirp), typemap[lfs_dir_getino(fs, dirp)]); 445 lfs_dir_settype(fs, dirp, typemap[lfs_dir_getino(fs, dirp)]); 446 if (reply("FIX") == 1) 447 ret |= ALTERED; 448 } 449 lncntp[lfs_dir_getino(fs, dirp)]--; 450 break; 451 452 default: 453 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju", 454 statemap[lfs_dir_getino(fs, dirp)], 455 (uintmax_t)lfs_dir_getino(fs, dirp)); 456 } 457 } 458 if (n == 0) 459 return (ret | KEEPON); 460 lfs_dir_setino(fs, dirp, 0); 461 return (ret | KEEPON | ALTERED); 462 } 463 /* 464 * Routine to sort disk blocks. 465 */ 466 static int 467 blksort(const void *inpp1, const void *inpp2) 468 { 469 return ((*(const struct inoinfo *const *) inpp1)->i_blks[0] - 470 (*(const struct inoinfo *const *) inpp2)->i_blks[0]); 471 } 472