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