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