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