1 /* 2 * Copyright (c) 1980, 1986 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)dir.c 5.21 (Berkeley) 05/27/92"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/time.h> 14 #include <ufs/ufs/dinode.h> 15 #define KERNEL 16 #include <ufs/ufs/dir.h> 17 #undef KERNEL 18 #include <ufs/ffs/fs.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include "fsck.h" 22 23 char *lfname = "lost+found"; 24 int lfmode = 01777; 25 struct dirtemplate emptydir = { 0, DIRBLKSIZ }; 26 struct dirtemplate dirhead = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." }; 27 28 struct direct *fsck_readdir(); 29 struct bufarea *getdirblk(); 30 31 /* 32 * Propagate connected state through the tree. 33 */ 34 propagate() 35 { 36 register struct inoinfo **inpp, *inp; 37 struct inoinfo **inpend; 38 long change; 39 40 inpend = &inpsort[inplast]; 41 do { 42 change = 0; 43 for (inpp = inpsort; inpp < inpend; inpp++) { 44 inp = *inpp; 45 if (inp->i_parent == 0) 46 continue; 47 if (statemap[inp->i_parent] == DFOUND && 48 statemap[inp->i_number] == DSTATE) { 49 statemap[inp->i_number] = DFOUND; 50 change++; 51 } 52 } 53 } while (change > 0); 54 } 55 56 /* 57 * Scan each entry in a directory block. 58 */ 59 dirscan(idesc) 60 register struct inodesc *idesc; 61 { 62 register struct direct *dp; 63 register struct bufarea *bp; 64 int dsize, n; 65 long blksiz; 66 char dbuf[DIRBLKSIZ]; 67 68 if (idesc->id_type != DATA) 69 errexit("wrong type to dirscan %d\n", idesc->id_type); 70 if (idesc->id_entryno == 0 && 71 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 72 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 73 blksiz = idesc->id_numfrags * sblock.fs_fsize; 74 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 75 idesc->id_filesize -= blksiz; 76 return (SKIP); 77 } 78 idesc->id_loc = 0; 79 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 80 dsize = dp->d_reclen; 81 bcopy((char *)dp, dbuf, (size_t)dsize); 82 idesc->id_dirp = (struct direct *)dbuf; 83 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 84 bp = getdirblk(idesc->id_blkno, blksiz); 85 bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, 86 (size_t)dsize); 87 dirty(bp); 88 sbdirty(); 89 } 90 if (n & STOP) 91 return (n); 92 } 93 return (idesc->id_filesize > 0 ? KEEPON : STOP); 94 } 95 96 /* 97 * get next entry in a directory. 98 */ 99 struct direct * 100 fsck_readdir(idesc) 101 register struct inodesc *idesc; 102 { 103 register struct direct *dp, *ndp; 104 register struct bufarea *bp; 105 long size, blksiz, fix; 106 107 blksiz = idesc->id_numfrags * sblock.fs_fsize; 108 bp = getdirblk(idesc->id_blkno, blksiz); 109 if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 110 idesc->id_loc < blksiz) { 111 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 112 if (dircheck(idesc, dp)) 113 goto dpok; 114 idesc->id_loc += DIRBLKSIZ; 115 idesc->id_filesize -= DIRBLKSIZ; 116 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 117 bp = getdirblk(idesc->id_blkno, blksiz); 118 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 119 dp->d_reclen = DIRBLKSIZ; 120 dp->d_ino = 0; 121 dp->d_namlen = 0; 122 dp->d_name[0] = '\0'; 123 if (fix) 124 dirty(bp); 125 return (dp); 126 } 127 dpok: 128 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 129 return NULL; 130 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 131 idesc->id_loc += dp->d_reclen; 132 idesc->id_filesize -= dp->d_reclen; 133 if ((idesc->id_loc % DIRBLKSIZ) == 0) 134 return (dp); 135 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 136 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 137 dircheck(idesc, ndp) == 0) { 138 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 139 idesc->id_loc += size; 140 idesc->id_filesize -= size; 141 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 142 bp = getdirblk(idesc->id_blkno, blksiz); 143 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 144 dp->d_reclen += size; 145 if (fix) 146 dirty(bp); 147 } 148 return (dp); 149 } 150 151 /* 152 * Verify that a directory entry is valid. 153 * This is a superset of the checks made in the kernel. 154 */ 155 dircheck(idesc, dp) 156 struct inodesc *idesc; 157 register struct direct *dp; 158 { 159 register int size; 160 register char *cp; 161 int spaceleft; 162 163 size = DIRSIZ(dp); 164 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 165 if (dp->d_ino < maxino && 166 dp->d_reclen != 0 && 167 dp->d_reclen <= spaceleft && 168 (dp->d_reclen & 0x3) == 0 && 169 dp->d_reclen >= size && 170 idesc->id_filesize >= size && 171 dp->d_namlen <= MAXNAMLEN) { 172 if (dp->d_ino == 0) 173 return (1); 174 for (cp = dp->d_name, size = 0; size < dp->d_namlen; size++) 175 if (*cp == 0 || (*cp++ == '/')) 176 return (0); 177 if (*cp == 0) 178 return (1); 179 } 180 return (0); 181 } 182 183 direrror(ino, errmesg) 184 ino_t ino; 185 char *errmesg; 186 { 187 188 fileerror(ino, ino, errmesg); 189 } 190 191 fileerror(cwd, ino, errmesg) 192 ino_t cwd, ino; 193 char *errmesg; 194 { 195 register struct dinode *dp; 196 char pathbuf[MAXPATHLEN + 1]; 197 198 pwarn("%s ", errmesg); 199 pinode(ino); 200 printf("\n"); 201 getpathname(pathbuf, cwd, ino); 202 if (ino < ROOTINO || ino > maxino) { 203 pfatal("NAME=%s\n", pathbuf); 204 return; 205 } 206 dp = ginode(ino); 207 if (ftypeok(dp)) 208 pfatal("%s=%s\n", 209 (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 210 else 211 pfatal("NAME=%s\n", pathbuf); 212 } 213 214 adjust(idesc, lcnt) 215 register struct inodesc *idesc; 216 short lcnt; 217 { 218 register struct dinode *dp; 219 220 dp = ginode(idesc->id_number); 221 if (dp->di_nlink == lcnt) { 222 if (linkup(idesc->id_number, (ino_t)0) == 0) 223 clri(idesc, "UNREF", 0); 224 } else { 225 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 226 ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); 227 pinode(idesc->id_number); 228 printf(" COUNT %d SHOULD BE %d", 229 dp->di_nlink, dp->di_nlink - lcnt); 230 if (preen) { 231 if (lcnt < 0) { 232 printf("\n"); 233 pfatal("LINK COUNT INCREASING"); 234 } 235 printf(" (ADJUSTED)\n"); 236 } 237 if (preen || reply("ADJUST") == 1) { 238 dp->di_nlink -= lcnt; 239 inodirty(); 240 } 241 } 242 } 243 244 mkentry(idesc) 245 struct inodesc *idesc; 246 { 247 register struct direct *dirp = idesc->id_dirp; 248 struct direct newent; 249 int newlen, oldlen; 250 251 newent.d_namlen = strlen(idesc->id_name); 252 newlen = DIRSIZ(&newent); 253 if (dirp->d_ino != 0) 254 oldlen = DIRSIZ(dirp); 255 else 256 oldlen = 0; 257 if (dirp->d_reclen - oldlen < newlen) 258 return (KEEPON); 259 newent.d_reclen = dirp->d_reclen - oldlen; 260 dirp->d_reclen = oldlen; 261 dirp = (struct direct *)(((char *)dirp) + oldlen); 262 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 263 dirp->d_reclen = newent.d_reclen; 264 dirp->d_namlen = newent.d_namlen; 265 bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); 266 return (ALTERED|STOP); 267 } 268 269 chgino(idesc) 270 struct inodesc *idesc; 271 { 272 register struct direct *dirp = idesc->id_dirp; 273 274 if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 275 return (KEEPON); 276 dirp->d_ino = idesc->id_parent; 277 return (ALTERED|STOP); 278 } 279 280 linkup(orphan, parentdir) 281 ino_t orphan; 282 ino_t parentdir; 283 { 284 register struct dinode *dp; 285 int lostdir; 286 ino_t oldlfdir; 287 struct inodesc idesc; 288 char tempname[BUFSIZ]; 289 extern int pass4check(); 290 291 bzero((char *)&idesc, sizeof(struct inodesc)); 292 dp = ginode(orphan); 293 lostdir = (dp->di_mode & IFMT) == IFDIR; 294 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 295 pinode(orphan); 296 if (preen && dp->di_size == 0) 297 return (0); 298 if (preen) 299 printf(" (RECONNECTED)\n"); 300 else 301 if (reply("RECONNECT") == 0) 302 return (0); 303 if (lfdir == 0) { 304 dp = ginode(ROOTINO); 305 idesc.id_name = lfname; 306 idesc.id_type = DATA; 307 idesc.id_func = findino; 308 idesc.id_number = ROOTINO; 309 if ((ckinode(dp, &idesc) & FOUND) != 0) { 310 lfdir = idesc.id_parent; 311 } else { 312 pwarn("NO lost+found DIRECTORY"); 313 if (preen || reply("CREATE")) { 314 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 315 if (lfdir != 0) { 316 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 317 if (preen) 318 printf(" (CREATED)\n"); 319 } else { 320 freedir(lfdir, ROOTINO); 321 lfdir = 0; 322 if (preen) 323 printf("\n"); 324 } 325 } 326 } 327 } 328 if (lfdir == 0) { 329 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 330 printf("\n\n"); 331 return (0); 332 } 333 } 334 dp = ginode(lfdir); 335 if ((dp->di_mode & IFMT) != IFDIR) { 336 pfatal("lost+found IS NOT A DIRECTORY"); 337 if (reply("REALLOCATE") == 0) 338 return (0); 339 oldlfdir = lfdir; 340 if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 341 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 342 return (0); 343 } 344 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 345 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 346 return (0); 347 } 348 inodirty(); 349 idesc.id_type = ADDR; 350 idesc.id_func = pass4check; 351 idesc.id_number = oldlfdir; 352 adjust(&idesc, lncntp[oldlfdir] + 1); 353 lncntp[oldlfdir] = 0; 354 dp = ginode(lfdir); 355 } 356 if (statemap[lfdir] != DFOUND) { 357 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 358 return (0); 359 } 360 (void)lftempname(tempname, orphan); 361 if (makeentry(lfdir, orphan, tempname) == 0) { 362 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 363 printf("\n\n"); 364 return (0); 365 } 366 lncntp[orphan]--; 367 if (lostdir) { 368 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 369 parentdir != (ino_t)-1) 370 (void)makeentry(orphan, lfdir, ".."); 371 dp = ginode(lfdir); 372 dp->di_nlink++; 373 inodirty(); 374 lncntp[lfdir]++; 375 pwarn("DIR I=%lu CONNECTED. ", orphan); 376 if (parentdir != (ino_t)-1) 377 printf("PARENT WAS I=%lu\n", parentdir); 378 if (preen == 0) 379 printf("\n"); 380 } 381 return (1); 382 } 383 384 /* 385 * fix an entry in a directory. 386 */ 387 changeino(dir, name, newnum) 388 ino_t dir; 389 char *name; 390 ino_t newnum; 391 { 392 struct inodesc idesc; 393 394 bzero((char *)&idesc, sizeof(struct inodesc)); 395 idesc.id_type = DATA; 396 idesc.id_func = chgino; 397 idesc.id_number = dir; 398 idesc.id_fix = DONTKNOW; 399 idesc.id_name = name; 400 idesc.id_parent = newnum; /* new value for name */ 401 return (ckinode(ginode(dir), &idesc)); 402 } 403 404 /* 405 * make an entry in a directory 406 */ 407 makeentry(parent, ino, name) 408 ino_t parent, ino; 409 char *name; 410 { 411 struct dinode *dp; 412 struct inodesc idesc; 413 char pathbuf[MAXPATHLEN + 1]; 414 415 if (parent < ROOTINO || parent >= maxino || 416 ino < ROOTINO || ino >= maxino) 417 return (0); 418 bzero((char *)&idesc, sizeof(struct inodesc)); 419 idesc.id_type = DATA; 420 idesc.id_func = mkentry; 421 idesc.id_number = parent; 422 idesc.id_parent = ino; /* this is the inode to enter */ 423 idesc.id_fix = DONTKNOW; 424 idesc.id_name = name; 425 dp = ginode(parent); 426 if (dp->di_size % DIRBLKSIZ) { 427 dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 428 inodirty(); 429 } 430 if ((ckinode(dp, &idesc) & ALTERED) != 0) 431 return (1); 432 getpathname(pathbuf, parent, parent); 433 dp = ginode(parent); 434 if (expanddir(dp, pathbuf) == 0) 435 return (0); 436 return (ckinode(dp, &idesc) & ALTERED); 437 } 438 439 /* 440 * Attempt to expand the size of a directory 441 */ 442 expanddir(dp, name) 443 register struct dinode *dp; 444 char *name; 445 { 446 daddr_t lastbn, newblk; 447 register struct bufarea *bp; 448 char *cp, firstblk[DIRBLKSIZ]; 449 450 lastbn = lblkno(&sblock, dp->di_size); 451 if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) 452 return (0); 453 if ((newblk = allocblk(sblock.fs_frag)) == 0) 454 return (0); 455 dp->di_db[lastbn + 1] = dp->di_db[lastbn]; 456 dp->di_db[lastbn] = newblk; 457 dp->di_size += sblock.fs_bsize; 458 dp->di_blocks += btodb(sblock.fs_bsize); 459 bp = getdirblk(dp->di_db[lastbn + 1], 460 (long)dblksize(&sblock, dp, lastbn + 1)); 461 if (bp->b_errs) 462 goto bad; 463 bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 464 bp = getdirblk(newblk, sblock.fs_bsize); 465 if (bp->b_errs) 466 goto bad; 467 bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 468 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 469 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 470 cp += DIRBLKSIZ) 471 bcopy((char *)&emptydir, cp, sizeof emptydir); 472 dirty(bp); 473 bp = getdirblk(dp->di_db[lastbn + 1], 474 (long)dblksize(&sblock, dp, lastbn + 1)); 475 if (bp->b_errs) 476 goto bad; 477 bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); 478 pwarn("NO SPACE LEFT IN %s", name); 479 if (preen) 480 printf(" (EXPANDED)\n"); 481 else if (reply("EXPAND") == 0) 482 goto bad; 483 dirty(bp); 484 inodirty(); 485 return (1); 486 bad: 487 dp->di_db[lastbn] = dp->di_db[lastbn + 1]; 488 dp->di_db[lastbn + 1] = 0; 489 dp->di_size -= sblock.fs_bsize; 490 dp->di_blocks -= btodb(sblock.fs_bsize); 491 freeblk(newblk, sblock.fs_frag); 492 return (0); 493 } 494 495 /* 496 * allocate a new directory 497 */ 498 allocdir(parent, request, mode) 499 ino_t parent, request; 500 int mode; 501 { 502 ino_t ino; 503 char *cp; 504 struct dinode *dp; 505 register struct bufarea *bp; 506 507 ino = allocino(request, IFDIR|mode); 508 dirhead.dot_ino = ino; 509 dirhead.dotdot_ino = parent; 510 dp = ginode(ino); 511 bp = getdirblk(dp->di_db[0], sblock.fs_fsize); 512 if (bp->b_errs) { 513 freeino(ino); 514 return (0); 515 } 516 bcopy((char *)&dirhead, bp->b_un.b_buf, sizeof dirhead); 517 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 518 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 519 cp += DIRBLKSIZ) 520 bcopy((char *)&emptydir, cp, sizeof emptydir); 521 dirty(bp); 522 dp->di_nlink = 2; 523 inodirty(); 524 if (ino == ROOTINO) { 525 lncntp[ino] = dp->di_nlink; 526 cacheino(dp, ino); 527 return(ino); 528 } 529 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 530 freeino(ino); 531 return (0); 532 } 533 cacheino(dp, ino); 534 statemap[ino] = statemap[parent]; 535 if (statemap[ino] == DSTATE) { 536 lncntp[ino] = dp->di_nlink; 537 lncntp[parent]++; 538 } 539 dp = ginode(parent); 540 dp->di_nlink++; 541 inodirty(); 542 return (ino); 543 } 544 545 /* 546 * free a directory inode 547 */ 548 freedir(ino, parent) 549 ino_t ino, parent; 550 { 551 struct dinode *dp; 552 553 if (ino != parent) { 554 dp = ginode(parent); 555 dp->di_nlink--; 556 inodirty(); 557 } 558 freeino(ino); 559 } 560 561 /* 562 * generate a temporary name for the lost+found directory. 563 */ 564 lftempname(bufp, ino) 565 char *bufp; 566 ino_t ino; 567 { 568 register ino_t in; 569 register char *cp; 570 int namlen; 571 572 cp = bufp + 2; 573 for (in = maxino; in > 0; in /= 10) 574 cp++; 575 *--cp = 0; 576 namlen = cp - bufp; 577 in = ino; 578 while (cp > bufp) { 579 *--cp = (in % 10) + '0'; 580 in /= 10; 581 } 582 *cp = '#'; 583 return (namlen); 584 } 585 586 /* 587 * Get a directory block. 588 * Insure that it is held until another is requested. 589 */ 590 struct bufarea * 591 getdirblk(blkno, size) 592 daddr_t blkno; 593 long size; 594 { 595 596 if (pdirbp != 0) 597 pdirbp->b_flags &= ~B_INUSE; 598 pdirbp = getdatablk(blkno, size); 599 return (pdirbp); 600 } 601