calc and print out pathname of offending hard links to directories
[unix-history] / usr / src / sbin / fsck / dir.c
CommitLineData
250baf55 1#ifndef lint
f0ed004a 2static char version[] = "@(#)dir.c 3.8 (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 }
46 curino.id_type = DATA;
47 curino.id_func = parentino->id_func;
48 curino.id_parent = parentino->id_number;
49 curino.id_number = inumber;
250baf55
KM
50 (void)ckinode(dp, &curino);
51}
52
53dirscan(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 */
90DIRECT *
91fsck_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';
7718c0e6 113 if (dofix(idesc, "DIRECTORY CORRUPTED"))
250baf55
KM
114 dirty(&fileblk);
115 return (dp);
116 }
117dpok:
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;
7718c0e6 132 if (dofix(idesc, "DIRECTORY CORRUPTED"))
250baf55
KM
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 */
142dircheck(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
170direrr(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");
39c18287
KM
179 if (ino < ROOTINO || ino > imax) {
180 pfatal("NAME=%s\n", pathname);
181 return;
182 }
183 dp = ginode(ino);
184 if (ftypeok(dp))
7718c0e6 185 pfatal("%s=%s\n", DIRCT(dp) ? "DIR" : "FILE", pathname);
250baf55
KM
186 else
187 pfatal("NAME=%s\n", pathname);
188}
189
190adjust(idesc, lcnt)
191 register struct inodesc *idesc;
192 short lcnt;
193{
194 register DINODE *dp;
195
39c18287 196 dp = ginode(idesc->id_number);
250baf55
KM
197 if (dp->di_nlink == lcnt) {
198 if (linkup(idesc->id_number, (ino_t)0) == 0)
199 clri(idesc, "UNREF", 0);
993a756c 200 } else {
7718c0e6
KM
201 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
202 (DIRCT(dp) ? "DIR" : "FILE"));
250baf55
KM
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");
7718c0e6 209 pfatal("LINK COUNT INCREASING");
250baf55
KM
210 }
211 printf(" (ADJUSTED)\n");
212 }
213 if (preen || reply("ADJUST") == 1) {
214 dp->di_nlink -= lcnt;
215 inodirty();
216 }
217 }
218}
219
220mkentry(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;
ebd05fde
KM
240 dirp->d_namlen = strlen(idesc->id_name);
241 bcopy(idesc->id_name, dirp->d_name, dirp->d_namlen + 1);
250baf55
KM
242 return (ALTERED|STOP);
243}
244
59e3daa2 245chgino(idesc)
250baf55
KM
246 struct inodesc *idesc;
247{
248 register DIRECT *dirp = idesc->id_dirp;
249
59e3daa2
KM
250 if (bcmp(dirp->d_name, idesc->id_name, dirp->d_namlen + 1))
251 return (KEEPON);
252 dirp->d_ino = idesc->id_parent;;
253 return (ALTERED|STOP);
250baf55
KM
254}
255
256linkup(orphan, pdir)
257 ino_t orphan;
258 ino_t pdir;
259{
260 register DINODE *dp;
261 int lostdir, len;
ebd05fde 262 ino_t oldlfdir;
250baf55 263 struct inodesc idesc;
ebd05fde 264 char tempname[BUFSIZ];
59e3daa2 265 extern int pass4check();
250baf55
KM
266
267 bzero((char *)&idesc, sizeof(struct inodesc));
39c18287 268 dp = ginode(orphan);
7718c0e6 269 lostdir = DIRCT(dp);
250baf55
KM
270 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
271 pinode(orphan);
272 if (preen && dp->di_size == 0)
273 return (0);
274 if (preen)
275 printf(" (RECONNECTED)\n");
276 else
277 if (reply("RECONNECT") == 0)
278 return (0);
279 pathp = pathname;
280 *pathp++ = '/';
281 *pathp = '\0';
282 if (lfdir == 0) {
39c18287 283 dp = ginode(ROOTINO);
7718c0e6 284 idesc.id_name = lfname;
250baf55
KM
285 idesc.id_type = DATA;
286 idesc.id_func = findino;
287 idesc.id_number = ROOTINO;
250baf55 288 (void)ckinode(dp, &idesc);
ebd05fde
KM
289 if (idesc.id_parent >= ROOTINO && idesc.id_parent < imax) {
290 lfdir = idesc.id_parent;
291 } else {
292 pwarn("NO lost+found DIRECTORY");
293 if (preen || reply("CREATE")) {
294 idesc.id_func = mkentry;
295 idesc.id_parent = allocdir(ROOTINO, 0);
ebd05fde
KM
296 if (idesc.id_parent != 0) {
297 if (makeentry(dp, &idesc) != 0) {
298 lfdir = idesc.id_parent;
299 if (preen)
300 printf(" (CREATED)\n");
301 } else {
302 freedir(idesc.id_parent, ROOTINO);
303 if (preen)
304 printf("\n");
305 }
306 }
307 }
308 }
39c18287 309 if (lfdir == 0) {
ebd05fde 310 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
250baf55
KM
311 printf("\n\n");
312 return (0);
313 }
314 }
39c18287 315 dp = ginode(lfdir);
59e3daa2
KM
316 if (!DIRCT(dp)) {
317 pfatal("lost+found IS NOT A DIRECTORY");
318 if (reply("REALLOCATE") == 0)
319 return (0);
320 oldlfdir = lfdir;
321 if ((lfdir = allocdir(ROOTINO, 0)) == 0) {
322 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
323 return (0);
324 }
325 idesc.id_type = DATA;
326 idesc.id_func = chgino;
327 idesc.id_number = ROOTINO;
328 idesc.id_parent = lfdir; /* new inumber for lost+found */
329 idesc.id_name = lfname;
330 if ((ckinode(ginode(ROOTINO), &idesc) & ALTERED) == 0) {
331 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
332 return (0);
333 }
334 inodirty();
335 idesc.id_type = ADDR;
336 idesc.id_func = pass4check;
337 idesc.id_number = oldlfdir;
338 adjust(&idesc, lncntp[oldlfdir] + 1);
339 lncntp[oldlfdir] = 0;
340 dp = ginode(lfdir);
341 }
342 if (statemap[lfdir] != DFOUND) {
343 pfatal("SORRY. NO lost+found DIRECTORY\n\n");
250baf55
KM
344 return (0);
345 }
3e2a23ca
KM
346 if (dp->di_size % DIRBLKSIZ) {
347 dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
250baf55
KM
348 inodirty();
349 }
350 len = strlen(lfname);
351 bcopy(lfname, pathp, len + 1);
352 pathp += len;
353 idesc.id_type = DATA;
354 idesc.id_func = mkentry;
355 idesc.id_number = lfdir;
250baf55
KM
356 idesc.id_parent = orphan; /* this is the inode to enter */
357 idesc.id_fix = DONTKNOW;
ebd05fde
KM
358 idesc.id_name = tempname;
359 len = lftempname(idesc.id_name, orphan);
3e2a23ca 360 if (makeentry(dp, &idesc) == 0) {
250baf55
KM
361 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
362 printf("\n\n");
363 return (0);
364 }
365 lncntp[orphan]--;
366 *pathp++ = '/';
ebd05fde
KM
367 bcopy(idesc.id_name, pathp, len + 1);
368 pathp += len;
250baf55
KM
369 if (lostdir) {
370 dp = ginode(orphan);
371 idesc.id_type = DATA;
59e3daa2 372 idesc.id_func = chgino;
250baf55 373 idesc.id_number = orphan;
250baf55 374 idesc.id_fix = DONTKNOW;
59e3daa2
KM
375 idesc.id_name = "..";
376 idesc.id_parent = lfdir; /* new value for ".." */
250baf55 377 (void)ckinode(dp, &idesc);
39c18287
KM
378 dp = ginode(lfdir);
379 dp->di_nlink++;
380 inodirty();
381 lncntp[lfdir]++;
250baf55
KM
382 pwarn("DIR I=%u CONNECTED. ", orphan);
383 printf("PARENT WAS I=%u\n", pdir);
384 if (preen == 0)
385 printf("\n");
386 }
387 return (1);
388}
389
3e2a23ca
KM
390/*
391 * make an entry in a directory
392 */
393makeentry(dp, idesc)
394 DINODE *dp;
395 struct inodesc *idesc;
396{
397
398 if ((ckinode(dp, idesc) & ALTERED) != 0)
399 return (1);
400 if (expanddir(dp) == 0)
401 return (0);
3e2a23ca
KM
402 return (ckinode(dp, idesc) & ALTERED);
403}
404
405/*
406 * Attempt to expand the size of a directory
407 */
408expanddir(dp)
409 register DINODE *dp;
410{
411 daddr_t lastbn, newblk;
412 char *cp, firstblk[DIRBLKSIZ];
413
414 lastbn = lblkno(&sblock, dp->di_size);
415 if (lastbn >= NDADDR - 1)
416 return (0);
417 if ((newblk = allocblk(sblock.fs_frag)) == 0)
418 return (0);
419 dp->di_db[lastbn + 1] = dp->di_db[lastbn];
420 dp->di_db[lastbn] = newblk;
421 dp->di_size += sblock.fs_bsize;
422 dp->di_blocks += btodb(sblock.fs_bsize);
423 if (getblk(&fileblk, dp->di_db[lastbn + 1],
424 dblksize(&sblock, dp, lastbn + 1)) == NULL)
425 goto bad;
426 bcopy(dirblk.b_buf, firstblk, DIRBLKSIZ);
427 if (getblk(&fileblk, newblk, sblock.fs_bsize) == NULL)
428 goto bad;
429 bcopy(firstblk, dirblk.b_buf, DIRBLKSIZ);
430 for (cp = &dirblk.b_buf[DIRBLKSIZ];
431 cp < &dirblk.b_buf[sblock.fs_bsize];
432 cp += DIRBLKSIZ)
433 bcopy((char *)&emptydir, cp, sizeof emptydir);
434 dirty(&fileblk);
435 if (getblk(&fileblk, dp->di_db[lastbn + 1],
436 dblksize(&sblock, dp, lastbn + 1)) == NULL)
437 goto bad;
438 bcopy((char *)&emptydir, dirblk.b_buf, sizeof emptydir);
439 pwarn("NO SPACE LEFT IN %s", pathname);
440 if (preen)
441 printf(" (EXPANDED)\n");
442 else if (reply("EXPAND") == 0)
443 goto bad;
444 dirty(&fileblk);
445 inodirty();
446 return (1);
447bad:
448 dp->di_db[lastbn] = dp->di_db[lastbn + 1];
449 dp->di_db[lastbn + 1] = 0;
450 dp->di_size -= sblock.fs_bsize;
451 dp->di_blocks -= btodb(sblock.fs_bsize);
452 freeblk(newblk, sblock.fs_frag);
453 return (0);
454}
455
ebd05fde
KM
456/*
457 * allocate a new directory
458 */
459allocdir(parent, request)
460 ino_t parent, request;
461{
462 ino_t ino;
463 char *cp;
464 DINODE *dp;
465
466 ino = allocino(request, IFDIR|0755);
467 dirhead.dot_ino = ino;
468 dirhead.dotdot_ino = parent;
469 dp = ginode(ino);
470 if (getblk(&fileblk, dp->di_db[0], sblock.fs_fsize) == NULL) {
471 freeino(ino);
472 return (0);
473 }
474 bcopy((char *)&dirhead, dirblk.b_buf, sizeof dirhead);
475 for (cp = &dirblk.b_buf[DIRBLKSIZ];
476 cp < &dirblk.b_buf[sblock.fs_fsize];
477 cp += DIRBLKSIZ)
478 bcopy((char *)&emptydir, cp, sizeof emptydir);
479 dirty(&fileblk);
480 dp->di_nlink = 2;
481 inodirty();
482 if (ino == ROOTINO) {
483 lncntp[ino] = dp->di_nlink;
484 return(ino);
485 }
486 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
487 freeino(ino);
488 return (0);
489 }
490 statemap[ino] = statemap[parent];
491 if (statemap[ino] == DSTATE) {
492 lncntp[ino] = dp->di_nlink;
493 lncntp[parent]++;
494 }
495 dp = ginode(parent);
496 dp->di_nlink++;
497 inodirty();
498 return (ino);
499}
500
501/*
502 * free a directory inode
503 */
504freedir(ino, parent)
505 ino_t ino, parent;
506{
507 DINODE *dp;
508
509 if (ino != parent) {
510 dp = ginode(parent);
511 dp->di_nlink--;
512 inodirty();
513 }
514 freeino(ino);
515}
516
250baf55
KM
517/*
518 * generate a temporary name for the lost+found directory.
519 */
520lftempname(bufp, ino)
521 char *bufp;
522 ino_t ino;
523{
524 register ino_t in;
525 register char *cp;
526 int namlen;
527
528 cp = bufp + 2;
529 for (in = imax; in > 0; in /= 10)
530 cp++;
531 *--cp = 0;
532 namlen = cp - bufp;
533 in = ino;
534 while (cp > bufp) {
535 *--cp = (in % 10) + '0';
536 in /= 10;
537 }
538 *cp = '#';
539 return (namlen);
540}