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