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