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