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