1 #ifndef lint 2 static char version[] = "@(#)dir.c 3.5 (Berkeley) 02/11/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 19 DIRECT *fsck_readdir(); 20 21 descend(parentino, inumber) 22 struct inodesc *parentino; 23 ino_t inumber; 24 { 25 register DINODE *dp; 26 struct inodesc curino; 27 28 bzero((char *)&curino, sizeof(struct inodesc)); 29 if (statemap[inumber] != DSTATE) 30 errexit("BAD INODE %d TO DESCEND", statemap[inumber]); 31 statemap[inumber] = DFOUND; 32 dp = ginode(inumber); 33 if (dp->di_size == 0) { 34 direrr(inumber, "ZERO LENGTH DIRECTORY"); 35 if (reply("REMOVE") == 1) 36 statemap[inumber] = DCLEAR; 37 return; 38 } 39 if (dp->di_size < MINDIRSIZE) { 40 direrr(inumber, "DIRECTORY TOO SHORT"); 41 dp->di_size = MINDIRSIZE; 42 if (reply("FIX") == 1) 43 inodirty(); 44 } 45 curino.id_type = DATA; 46 curino.id_func = parentino->id_func; 47 curino.id_parent = parentino->id_number; 48 curino.id_number = inumber; 49 curino.id_filesize = dp->di_size; 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 blksiz = idesc->id_numfrags * sblock.fs_fsize; 64 if (outrange(idesc->id_blkno, idesc->id_numfrags)) { 65 idesc->id_filesize -= blksiz; 66 return (SKIP); 67 } 68 idesc->id_loc = 0; 69 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 70 dsize = dp->d_reclen; 71 bcopy((char *)dp, dbuf, dsize); 72 idesc->id_dirp = (DIRECT *)dbuf; 73 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 74 if (getblk(&fileblk, idesc->id_blkno, blksiz) != NULL) { 75 bcopy(dbuf, (char *)dp, dsize); 76 dirty(&fileblk); 77 sbdirty(); 78 } else 79 n &= ~ALTERED; 80 } 81 if (n & STOP) 82 return (n); 83 } 84 return (idesc->id_filesize > 0 ? KEEPON : STOP); 85 } 86 87 /* 88 * get next entry in a directory. 89 */ 90 DIRECT * 91 fsck_readdir(idesc) 92 register struct inodesc *idesc; 93 { 94 register DIRECT *dp, *ndp; 95 long size, blksiz; 96 97 blksiz = idesc->id_numfrags * sblock.fs_fsize; 98 if (getblk(&fileblk, idesc->id_blkno, blksiz) == NULL) { 99 idesc->id_filesize -= blksiz - idesc->id_loc; 100 return NULL; 101 } 102 if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 103 idesc->id_loc < blksiz) { 104 dp = (DIRECT *)(dirblk.b_buf + idesc->id_loc); 105 if (dircheck(idesc, dp)) 106 goto dpok; 107 idesc->id_loc += DIRBLKSIZ; 108 idesc->id_filesize -= DIRBLKSIZ; 109 dp->d_reclen = DIRBLKSIZ; 110 dp->d_ino = 0; 111 dp->d_namlen = 0; 112 dp->d_name[0] = '\0'; 113 if (dofix(idesc, "DIRECTORY CORRUPTED")) 114 dirty(&fileblk); 115 return (dp); 116 } 117 dpok: 118 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 119 return NULL; 120 dp = (DIRECT *)(dirblk.b_buf + idesc->id_loc); 121 idesc->id_loc += dp->d_reclen; 122 idesc->id_filesize -= dp->d_reclen; 123 if ((idesc->id_loc % DIRBLKSIZ) == 0) 124 return (dp); 125 ndp = (DIRECT *)(dirblk.b_buf + idesc->id_loc); 126 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 127 dircheck(idesc, ndp) == 0) { 128 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 129 dp->d_reclen += size; 130 idesc->id_loc += size; 131 idesc->id_filesize -= size; 132 if (dofix(idesc, "DIRECTORY CORRUPTED")) 133 dirty(&fileblk); 134 } 135 return (dp); 136 } 137 138 /* 139 * Verify that a directory entry is valid. 140 * This is a superset of the checks made in the kernel. 141 */ 142 dircheck(idesc, dp) 143 struct inodesc *idesc; 144 register DIRECT *dp; 145 { 146 register int size; 147 register char *cp; 148 int spaceleft; 149 150 size = DIRSIZ(dp); 151 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 152 if (dp->d_ino < imax && 153 dp->d_reclen != 0 && 154 dp->d_reclen <= spaceleft && 155 (dp->d_reclen & 0x3) == 0 && 156 dp->d_reclen >= size && 157 idesc->id_filesize >= size && 158 dp->d_namlen <= MAXNAMLEN) { 159 if (dp->d_ino == 0) 160 return (1); 161 for (cp = dp->d_name, size = 0; size < dp->d_namlen; size++) 162 if (*cp == 0 || (*cp++ & 0200)) 163 return (0); 164 if (*cp == 0) 165 return (1); 166 } 167 return (0); 168 } 169 170 direrr(ino, s) 171 ino_t ino; 172 char *s; 173 { 174 register DINODE *dp; 175 176 pwarn("%s ", s); 177 pinode(ino); 178 printf("\n"); 179 if (ino < ROOTINO || ino > imax) { 180 pfatal("NAME=%s\n", pathname); 181 return; 182 } 183 dp = ginode(ino); 184 if (ftypeok(dp)) 185 pfatal("%s=%s\n", DIRCT(dp) ? "DIR" : "FILE", pathname); 186 else 187 pfatal("NAME=%s\n", pathname); 188 } 189 190 adjust(idesc, lcnt) 191 register struct inodesc *idesc; 192 short lcnt; 193 { 194 register DINODE *dp; 195 196 dp = ginode(idesc->id_number); 197 if (dp->di_nlink == lcnt) { 198 if (linkup(idesc->id_number, (ino_t)0) == 0) 199 clri(idesc, "UNREF", 0); 200 } else { 201 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 202 (DIRCT(dp) ? "DIR" : "FILE")); 203 pinode(idesc->id_number); 204 printf(" COUNT %d SHOULD BE %d", 205 dp->di_nlink, dp->di_nlink-lcnt); 206 if (preen) { 207 if (lcnt < 0) { 208 printf("\n"); 209 pfatal("LINK COUNT INCREASING"); 210 } 211 printf(" (ADJUSTED)\n"); 212 } 213 if (preen || reply("ADJUST") == 1) { 214 dp->di_nlink -= lcnt; 215 inodirty(); 216 } 217 } 218 } 219 220 mkentry(idesc) 221 struct inodesc *idesc; 222 { 223 register DIRECT *dirp = idesc->id_dirp; 224 DIRECT newent; 225 int newlen, oldlen; 226 227 newent.d_namlen = 11; 228 newlen = DIRSIZ(&newent); 229 if (dirp->d_ino != 0) 230 oldlen = DIRSIZ(dirp); 231 else 232 oldlen = 0; 233 if (dirp->d_reclen - oldlen < newlen) 234 return (KEEPON); 235 newent.d_reclen = dirp->d_reclen - oldlen; 236 dirp->d_reclen = oldlen; 237 dirp = (struct direct *)(((char *)dirp) + oldlen); 238 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 239 dirp->d_reclen = newent.d_reclen; 240 dirp->d_namlen = lftempname(dirp->d_name, idesc->id_parent); 241 return (ALTERED|STOP); 242 } 243 244 chgdd(idesc) 245 struct inodesc *idesc; 246 { 247 register DIRECT *dirp = idesc->id_dirp; 248 249 if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' && 250 dirp->d_name[2] == 0) { 251 dirp->d_ino = lfdir; 252 return (ALTERED|STOP); 253 } 254 return (KEEPON); 255 } 256 257 linkup(orphan, pdir) 258 ino_t orphan; 259 ino_t pdir; 260 { 261 register DINODE *dp; 262 int lostdir, len; 263 struct inodesc idesc; 264 265 bzero((char *)&idesc, sizeof(struct inodesc)); 266 dp = ginode(orphan); 267 lostdir = DIRCT(dp); 268 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 269 pinode(orphan); 270 if (preen && dp->di_size == 0) 271 return (0); 272 if (preen) 273 printf(" (RECONNECTED)\n"); 274 else 275 if (reply("RECONNECT") == 0) 276 return (0); 277 pathp = pathname; 278 *pathp++ = '/'; 279 *pathp = '\0'; 280 if (lfdir == 0) { 281 dp = ginode(ROOTINO); 282 idesc.id_name = lfname; 283 idesc.id_type = DATA; 284 idesc.id_func = findino; 285 idesc.id_number = ROOTINO; 286 idesc.id_filesize = dp->di_size; 287 (void)ckinode(dp, &idesc); 288 lfdir = idesc.id_parent; 289 if (lfdir < ROOTINO || lfdir > imax) 290 lfdir = 0; 291 if (lfdir == 0) { 292 pfatal("SORRY. NO lost+found DIRECTORY"); 293 printf("\n\n"); 294 return (0); 295 } 296 } 297 dp = ginode(lfdir); 298 if (!DIRCT(dp) || statemap[lfdir] != DFOUND) { 299 pfatal("SORRY. NO lost+found DIRECTORY"); 300 printf("\n\n"); 301 return (0); 302 } 303 if (dp->di_size % DIRBLKSIZ) { 304 dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 305 inodirty(); 306 } 307 len = strlen(lfname); 308 bcopy(lfname, pathp, len + 1); 309 pathp += len; 310 idesc.id_type = DATA; 311 idesc.id_func = mkentry; 312 idesc.id_number = lfdir; 313 idesc.id_filesize = dp->di_size; 314 idesc.id_parent = orphan; /* this is the inode to enter */ 315 idesc.id_fix = DONTKNOW; 316 if (makeentry(dp, &idesc) == 0) { 317 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 318 printf("\n\n"); 319 return (0); 320 } 321 lncntp[orphan]--; 322 *pathp++ = '/'; 323 pathp += lftempname(pathp, orphan); 324 if (lostdir) { 325 dp = ginode(orphan); 326 idesc.id_type = DATA; 327 idesc.id_func = chgdd; 328 idesc.id_number = orphan; 329 idesc.id_filesize = dp->di_size; 330 idesc.id_fix = DONTKNOW; 331 (void)ckinode(dp, &idesc); 332 dp = ginode(lfdir); 333 dp->di_nlink++; 334 inodirty(); 335 lncntp[lfdir]++; 336 pwarn("DIR I=%u CONNECTED. ", orphan); 337 printf("PARENT WAS I=%u\n", pdir); 338 if (preen == 0) 339 printf("\n"); 340 } 341 return (1); 342 } 343 344 /* 345 * make an entry in a directory 346 */ 347 makeentry(dp, idesc) 348 DINODE *dp; 349 struct inodesc *idesc; 350 { 351 352 if ((ckinode(dp, idesc) & ALTERED) != 0) 353 return (1); 354 if (expanddir(dp) == 0) 355 return (0); 356 idesc->id_filesize = dp->di_size; 357 return (ckinode(dp, idesc) & ALTERED); 358 } 359 360 /* 361 * Attempt to expand the size of a directory 362 */ 363 expanddir(dp) 364 register DINODE *dp; 365 { 366 daddr_t lastbn, newblk; 367 char *cp, firstblk[DIRBLKSIZ]; 368 369 lastbn = lblkno(&sblock, dp->di_size); 370 if (lastbn >= NDADDR - 1) 371 return (0); 372 if ((newblk = allocblk(sblock.fs_frag)) == 0) 373 return (0); 374 dp->di_db[lastbn + 1] = dp->di_db[lastbn]; 375 dp->di_db[lastbn] = newblk; 376 dp->di_size += sblock.fs_bsize; 377 dp->di_blocks += btodb(sblock.fs_bsize); 378 if (getblk(&fileblk, dp->di_db[lastbn + 1], 379 dblksize(&sblock, dp, lastbn + 1)) == NULL) 380 goto bad; 381 bcopy(dirblk.b_buf, firstblk, DIRBLKSIZ); 382 if (getblk(&fileblk, newblk, sblock.fs_bsize) == NULL) 383 goto bad; 384 bcopy(firstblk, dirblk.b_buf, DIRBLKSIZ); 385 for (cp = &dirblk.b_buf[DIRBLKSIZ]; 386 cp < &dirblk.b_buf[sblock.fs_bsize]; 387 cp += DIRBLKSIZ) 388 bcopy((char *)&emptydir, cp, sizeof emptydir); 389 dirty(&fileblk); 390 if (getblk(&fileblk, dp->di_db[lastbn + 1], 391 dblksize(&sblock, dp, lastbn + 1)) == NULL) 392 goto bad; 393 bcopy((char *)&emptydir, dirblk.b_buf, sizeof emptydir); 394 pwarn("NO SPACE LEFT IN %s", pathname); 395 if (preen) 396 printf(" (EXPANDED)\n"); 397 else if (reply("EXPAND") == 0) 398 goto bad; 399 dirty(&fileblk); 400 inodirty(); 401 return (1); 402 bad: 403 dp->di_db[lastbn] = dp->di_db[lastbn + 1]; 404 dp->di_db[lastbn + 1] = 0; 405 dp->di_size -= sblock.fs_bsize; 406 dp->di_blocks -= btodb(sblock.fs_bsize); 407 freeblk(newblk, sblock.fs_frag); 408 return (0); 409 } 410 411 /* 412 * generate a temporary name for the lost+found directory. 413 */ 414 lftempname(bufp, ino) 415 char *bufp; 416 ino_t ino; 417 { 418 register ino_t in; 419 register char *cp; 420 int namlen; 421 422 cp = bufp + 2; 423 for (in = imax; in > 0; in /= 10) 424 cp++; 425 *--cp = 0; 426 namlen = cp - bufp; 427 in = ino; 428 while (cp > bufp) { 429 *--cp = (in % 10) + '0'; 430 in /= 10; 431 } 432 *cp = '#'; 433 return (namlen); 434 } 435