1/* $NetBSD: pass2.c,v 1.35 2020/04/03 19:36:33 joerg 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 58static int pass2check(struct inodesc *); 59static int blksort(const void *, const void *); 60 61void 62pass2(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 213static int 214pass2check(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 } 280chk1: 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); 337chk2: 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 } else if (lfs_dir_getino(fs, dirp) == LFS_IFILE_INUM && 366 idesc->id_number == ULFS_ROOTINO) { 367 if (lfs_dir_gettype(fs, dirp) != LFS_DT_REG) { 368 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), 369 "BAD TYPE FOR IFILE"); 370 if (reply("FIX") == 1) { 371 lfs_dir_settype(fs, dirp, LFS_DT_REG); 372 ret |= ALTERED; 373 } 374 } 375 } else if (((lfs_dir_getino(fs, dirp) == ULFS_WINO && lfs_dir_gettype(fs, dirp) != LFS_DT_WHT) || 376 (lfs_dir_getino(fs, dirp) != ULFS_WINO && lfs_dir_gettype(fs, dirp) == LFS_DT_WHT))) { 377 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), "BAD WHITEOUT ENTRY"); 378 if (reply("FIX") == 1) { 379 lfs_dir_setino(fs, dirp, ULFS_WINO); 380 lfs_dir_settype(fs, dirp, LFS_DT_WHT); 381 ret |= ALTERED; 382 } 383 } else { 384again: 385 switch (statemap[lfs_dir_getino(fs, dirp)]) { 386 case USTATE: 387 if (idesc->id_entryno <= 2) 388 break; 389 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), 390 "UNALLOCATED"); 391 n = reply("REMOVE"); 392 break; 393 394 case DCLEAR: 395 case FCLEAR: 396 if (idesc->id_entryno <= 2) 397 break; 398 if (statemap[lfs_dir_getino(fs, dirp)] == FCLEAR) 399 errmsg = "DUP/BAD"; 400 else if (!preen) 401 errmsg = "ZERO LENGTH DIRECTORY"; 402 else { 403 n = 1; 404 break; 405 } 406 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), errmsg); 407 if ((n = reply("REMOVE")) == 1) 408 break; 409 dp = ginode(lfs_dir_getino(fs, dirp)); 410 statemap[lfs_dir_getino(fs, dirp)] = 411 (lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR ? DSTATE : FSTATE; 412 lncntp[lfs_dir_getino(fs, dirp)] = lfs_dino_getnlink(fs, dp); 413 goto again; 414 415 case DSTATE: 416 case DFOUND: 417 inp = getinoinfo(lfs_dir_getino(fs, dirp)); 418 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 419 getpathname(pathbuf, sizeof(pathbuf), 420 idesc->id_number, idesc->id_number); 421 getpathname(namebuf, sizeof(namebuf), 422 lfs_dir_getino(fs, dirp), 423 lfs_dir_getino(fs, dirp)); 424 pwarn("%s %s %s\n", pathbuf, 425 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 426 namebuf); 427 if (preen) 428 printf(" (IGNORED)\n"); 429 else if ((n = reply("REMOVE")) == 1) 430 break; 431 } 432 if (idesc->id_entryno > 2) 433 inp->i_parent = idesc->id_number; 434 /* fall through */ 435 436 case FSTATE: 437 if (lfs_dir_gettype(fs, dirp) != typemap[lfs_dir_getino(fs, dirp)]) { 438 fileerror(idesc->id_number, 439 lfs_dir_getino(fs, dirp), 440 "BAD TYPE VALUE"); 441 if (debug) 442 pwarn("dir has %d, typemap has %d\n", 443 lfs_dir_gettype(fs, dirp), typemap[lfs_dir_getino(fs, dirp)]); 444 lfs_dir_settype(fs, dirp, typemap[lfs_dir_getino(fs, dirp)]); 445 if (reply("FIX") == 1) 446 ret |= ALTERED; 447 } 448 lncntp[lfs_dir_getino(fs, dirp)]--; 449 break; 450 451 default: 452 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju", 453 statemap[lfs_dir_getino(fs, dirp)], 454 (uintmax_t)lfs_dir_getino(fs, dirp)); 455 } 456 } 457 if (n == 0) 458 return (ret | KEEPON); 459 lfs_dir_setino(fs, dirp, 0); 460 return (ret | KEEPON | ALTERED); 461} 462/* 463 * Routine to sort disk blocks. 464 */ 465static int 466blksort(const void *inpp1, const void *inpp2) 467{ 468 return ((*(const struct inoinfo *const *) inpp1)->i_blks[0] - 469 (*(const struct inoinfo *const *) inpp2)->i_blks[0]); 470} 471