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