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