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