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