Kernel tree reorganization stage 2: Major cvs repository work.
[dragonfly.git] / sbin / fsck / utilities.c
1 /*
2  * Copyright (c) 1980, 1986, 1993
3  *      The Regents of the University of California.  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  * @(#)utilities.c      8.6 (Berkeley) 5/19/95
34  * $FreeBSD: src/sbin/fsck/utilities.c,v 1.11.2.3 2001/01/23 23:11:07 iedowse Exp $
35  * $DragonFly: src/sbin/fsck/utilities.c,v 1.3 2003/08/08 04:18:37 dillon Exp $
36  */
37
38 #include <sys/param.h>
39
40 #include <vfs/ufs/dinode.h>
41 #include <vfs/ufs/dir.h>
42 #include <vfs/ufs/fs.h>
43
44 #include <err.h>
45 #include <string.h>
46
47 #include "fsck.h"
48
49 long    diskreads, totalreads;  /* Disk cache statistics */
50
51 static void rwerror __P((char *mesg, ufs_daddr_t blk));
52
53 int
54 ftypeok(dp)
55         struct dinode *dp;
56 {
57         switch (dp->di_mode & IFMT) {
58
59         case IFDIR:
60         case IFREG:
61         case IFBLK:
62         case IFCHR:
63         case IFLNK:
64         case IFSOCK:
65         case IFIFO:
66                 return (1);
67
68         default:
69                 if (debug)
70                         printf("bad file type 0%o\n", dp->di_mode);
71                 return (0);
72         }
73 }
74
75 int
76 reply(question)
77         char *question;
78 {
79         int persevere;
80         char c;
81
82         if (preen)
83                 pfatal("INTERNAL ERROR: GOT TO reply()");
84         persevere = !strcmp(question, "CONTINUE");
85         printf("\n");
86         if (!persevere && (nflag || fswritefd < 0)) {
87                 printf("%s? no\n\n", question);
88                 resolved = 0;
89                 return (0);
90         }
91         if (yflag || (persevere && nflag)) {
92                 printf("%s? yes\n\n", question);
93                 return (1);
94         }
95         do      {
96                 printf("%s? [yn] ", question);
97                 (void) fflush(stdout);
98                 c = getc(stdin);
99                 while (c != '\n' && getc(stdin) != '\n') {
100                         if (feof(stdin)) {
101                                 resolved = 0;
102                                 return (0);
103                         }
104                 }
105         } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
106         printf("\n");
107         if (c == 'y' || c == 'Y')
108                 return (1);
109         resolved = 0;
110         return (0);
111 }
112
113 /*
114  * Look up state information for an inode.
115  */
116 struct inostat *
117 inoinfo(inum)
118         ino_t inum;
119 {
120         static struct inostat unallocated = { USTATE, 0, 0 };
121         struct inostatlist *ilp;
122         int iloff;
123
124         if (inum > maxino)
125                 errx(EEXIT, "inoinfo: inumber %d out of range", inum);
126         ilp = &inostathead[inum / sblock.fs_ipg];
127         iloff = inum % sblock.fs_ipg;
128         if (iloff >= ilp->il_numalloced)
129                 return (&unallocated);
130         return (&ilp->il_stat[iloff]);
131 }
132
133 /*
134  * Malloc buffers and set up cache.
135  */
136 void
137 bufinit()
138 {
139         register struct bufarea *bp;
140         long bufcnt, i;
141         char *bufp;
142
143         pbp = pdirbp = (struct bufarea *)0;
144         bufp = malloc((unsigned int)sblock.fs_bsize);
145         if (bufp == 0)
146                 errx(EEXIT, "cannot allocate buffer pool");
147         cgblk.b_un.b_buf = bufp;
148         initbarea(&cgblk);
149         bufhead.b_next = bufhead.b_prev = &bufhead;
150         bufcnt = MAXBUFSPACE / sblock.fs_bsize;
151         if (bufcnt < MINBUFS)
152                 bufcnt = MINBUFS;
153         for (i = 0; i < bufcnt; i++) {
154                 bp = (struct bufarea *)malloc(sizeof(struct bufarea));
155                 bufp = malloc((unsigned int)sblock.fs_bsize);
156                 if (bp == NULL || bufp == NULL) {
157                         if (i >= MINBUFS)
158                                 break;
159                         errx(EEXIT, "cannot allocate buffer pool");
160                 }
161                 bp->b_un.b_buf = bufp;
162                 bp->b_prev = &bufhead;
163                 bp->b_next = bufhead.b_next;
164                 bufhead.b_next->b_prev = bp;
165                 bufhead.b_next = bp;
166                 initbarea(bp);
167         }
168         bufhead.b_size = i;     /* save number of buffers */
169 }
170
171 /*
172  * Manage a cache of directory blocks.
173  */
174 struct bufarea *
175 getdatablk(blkno, size)
176         ufs_daddr_t blkno;
177         long size;
178 {
179         register struct bufarea *bp;
180
181         for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
182                 if (bp->b_bno == fsbtodb(&sblock, blkno))
183                         goto foundit;
184         for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
185                 if ((bp->b_flags & B_INUSE) == 0)
186                         break;
187         if (bp == &bufhead)
188                 errx(EEXIT, "deadlocked buffer pool");
189         getblk(bp, blkno, size);
190         /* fall through */
191 foundit:
192         totalreads++;
193         bp->b_prev->b_next = bp->b_next;
194         bp->b_next->b_prev = bp->b_prev;
195         bp->b_prev = &bufhead;
196         bp->b_next = bufhead.b_next;
197         bufhead.b_next->b_prev = bp;
198         bufhead.b_next = bp;
199         bp->b_flags |= B_INUSE;
200         return (bp);
201 }
202
203 void
204 getblk(bp, blk, size)
205         register struct bufarea *bp;
206         ufs_daddr_t blk;
207         long size;
208 {
209         ufs_daddr_t dblk;
210
211         dblk = fsbtodb(&sblock, blk);
212         if (bp->b_bno != dblk) {
213                 flush(fswritefd, bp);
214                 diskreads++;
215                 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
216                 bp->b_bno = dblk;
217                 bp->b_size = size;
218         }
219 }
220
221 void
222 flush(fd, bp)
223         int fd;
224         register struct bufarea *bp;
225 {
226         register int i, j;
227
228         if (!bp->b_dirty)
229                 return;
230         if (bp->b_errs != 0)
231                 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
232                     (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
233                     bp->b_bno);
234         bp->b_dirty = 0;
235         bp->b_errs = 0;
236         bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
237         if (bp != &sblk)
238                 return;
239         for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
240                 bwrite(fswritefd, (char *)sblock.fs_csp + i,
241                     fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
242                     sblock.fs_cssize - i < sblock.fs_bsize ?
243                     sblock.fs_cssize - i : sblock.fs_bsize);
244         }
245 }
246
247 static void
248 rwerror(mesg, blk)
249         char *mesg;
250         ufs_daddr_t blk;
251 {
252
253         if (preen == 0)
254                 printf("\n");
255         pfatal("CANNOT %s: BLK %ld", mesg, blk);
256         if (reply("CONTINUE") == 0)
257                 exit(EEXIT);
258 }
259
260 void
261 ckfini(markclean)
262         int markclean;
263 {
264         register struct bufarea *bp, *nbp;
265         int ofsmodified, cnt = 0;
266
267         if (fswritefd < 0) {
268                 (void)close(fsreadfd);
269                 return;
270         }
271         flush(fswritefd, &sblk);
272         if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
273             !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
274                 sblk.b_bno = SBOFF / dev_bsize;
275                 sbdirty();
276                 flush(fswritefd, &sblk);
277         }
278         flush(fswritefd, &cgblk);
279         free(cgblk.b_un.b_buf);
280         for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
281                 cnt++;
282                 flush(fswritefd, bp);
283                 nbp = bp->b_prev;
284                 free(bp->b_un.b_buf);
285                 free((char *)bp);
286         }
287         if (bufhead.b_size != cnt)
288                 errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt);
289         pbp = pdirbp = (struct bufarea *)0;
290         if (sblock.fs_clean != markclean) {
291                 sblock.fs_clean = markclean;
292                 sbdirty();
293                 ofsmodified = fsmodified;
294                 flush(fswritefd, &sblk);
295                 fsmodified = ofsmodified;
296                 if (!preen) {
297                         printf("\n***** FILE SYSTEM MARKED %s *****\n",
298                             markclean ? "CLEAN" : "DIRTY");
299                         if (!markclean)
300                                 rerun = 1;
301                 }
302         } else if (!preen && !markclean) {
303                 printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
304                 rerun = 1;
305         }
306         if (debug)
307                 printf("cache missed %ld of %ld (%d%%)\n", diskreads,
308                     totalreads, (int)(diskreads * 100 / totalreads));
309         (void)close(fsreadfd);
310         (void)close(fswritefd);
311 }
312
313 int
314 bread(fd, buf, blk, size)
315         int fd;
316         char *buf;
317         ufs_daddr_t blk;
318         long size;
319 {
320         char *cp;
321         int i, errs;
322         off_t offset;
323
324         offset = blk;
325         offset *= dev_bsize;
326         if (lseek(fd, offset, 0) < 0)
327                 rwerror("SEEK", blk);
328         else if (read(fd, buf, (int)size) == size)
329                 return (0);
330         rwerror("READ", blk);
331         if (lseek(fd, offset, 0) < 0)
332                 rwerror("SEEK", blk);
333         errs = 0;
334         memset(buf, 0, (size_t)size);
335         printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
336         for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
337                 if (read(fd, cp, (int)secsize) != secsize) {
338                         (void)lseek(fd, offset + i + secsize, 0);
339                         if (secsize != dev_bsize && dev_bsize != 1)
340                                 printf(" %ld (%ld),",
341                                     (blk * dev_bsize + i) / secsize,
342                                     blk + i / dev_bsize);
343                         else
344                                 printf(" %ld,", blk + i / dev_bsize);
345                         errs++;
346                 }
347         }
348         printf("\n");
349         if (errs)
350                 resolved = 0;
351         return (errs);
352 }
353
354 void
355 bwrite(fd, buf, blk, size)
356         int fd;
357         char *buf;
358         ufs_daddr_t blk;
359         long size;
360 {
361         int i;
362         char *cp;
363         off_t offset;
364
365         if (fd < 0)
366                 return;
367         offset = blk;
368         offset *= dev_bsize;
369         if (lseek(fd, offset, 0) < 0)
370                 rwerror("SEEK", blk);
371         else if (write(fd, buf, (int)size) == size) {
372                 fsmodified = 1;
373                 return;
374         }
375         resolved = 0;
376         rwerror("WRITE", blk);
377         if (lseek(fd, offset, 0) < 0)
378                 rwerror("SEEK", blk);
379         printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
380         for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
381                 if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
382                         (void)lseek(fd, offset + i + dev_bsize, 0);
383                         printf(" %ld,", blk + i / dev_bsize);
384                 }
385         printf("\n");
386         return;
387 }
388
389 /*
390  * allocate a data block with the specified number of fragments
391  */
392 ufs_daddr_t
393 allocblk(frags)
394         long frags;
395 {
396         int i, j, k, cg, baseblk;
397         struct cg *cgp = &cgrp;
398
399         if (frags <= 0 || frags > sblock.fs_frag)
400                 return (0);
401         for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
402                 for (j = 0; j <= sblock.fs_frag - frags; j++) {
403                         if (testbmap(i + j))
404                                 continue;
405                         for (k = 1; k < frags; k++)
406                                 if (testbmap(i + j + k))
407                                         break;
408                         if (k < frags) {
409                                 j += k;
410                                 continue;
411                         }
412                         cg = dtog(&sblock, i + j);
413                         getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
414                         if (!cg_chkmagic(cgp))
415                                 pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
416                         baseblk = dtogd(&sblock, i + j);
417                         for (k = 0; k < frags; k++) {
418                                 setbmap(i + j + k);
419                                 clrbit(cg_blksfree(cgp), baseblk + k);
420                         }
421                         n_blks += frags;
422                         if (frags == sblock.fs_frag)
423                                 cgp->cg_cs.cs_nbfree--;
424                         else
425                                 cgp->cg_cs.cs_nffree -= frags;
426                         cgdirty();
427                         return (i + j);
428                 }
429         }
430         return (0);
431 }
432
433 /*
434  * Free a previously allocated block
435  */
436 void
437 freeblk(blkno, frags)
438         ufs_daddr_t blkno;
439         long frags;
440 {
441         struct inodesc idesc;
442
443         idesc.id_blkno = blkno;
444         idesc.id_numfrags = frags;
445         (void)pass4check(&idesc);
446 }
447
448 /*
449  * Find a pathname
450  */
451 void
452 getpathname(namebuf, curdir, ino)
453         char *namebuf;
454         ino_t curdir, ino;
455 {
456         int len;
457         register char *cp;
458         struct inodesc idesc;
459         static int busy = 0;
460
461         if (curdir == ino && ino == ROOTINO) {
462                 (void)strcpy(namebuf, "/");
463                 return;
464         }
465         if (busy ||
466             (inoinfo(curdir)->ino_state != DSTATE &&
467              inoinfo(curdir)->ino_state != DFOUND)) {
468                 (void)strcpy(namebuf, "?");
469                 return;
470         }
471         busy = 1;
472         memset(&idesc, 0, sizeof(struct inodesc));
473         idesc.id_type = DATA;
474         idesc.id_fix = IGNORE;
475         cp = &namebuf[MAXPATHLEN - 1];
476         *cp = '\0';
477         if (curdir != ino) {
478                 idesc.id_parent = curdir;
479                 goto namelookup;
480         }
481         while (ino != ROOTINO) {
482                 idesc.id_number = ino;
483                 idesc.id_func = findino;
484                 idesc.id_name = "..";
485                 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
486                         break;
487         namelookup:
488                 idesc.id_number = idesc.id_parent;
489                 idesc.id_parent = ino;
490                 idesc.id_func = findname;
491                 idesc.id_name = namebuf;
492                 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
493                         break;
494                 len = strlen(namebuf);
495                 cp -= len;
496                 memmove(cp, namebuf, (size_t)len);
497                 *--cp = '/';
498                 if (cp < &namebuf[MAXNAMLEN])
499                         break;
500                 ino = idesc.id_number;
501         }
502         busy = 0;
503         if (ino != ROOTINO)
504                 *--cp = '?';
505         memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
506 }
507
508 void
509 catch(sig)
510         int sig;
511 {
512         if (!doinglevel2)
513                 ckfini(0);
514         exit(12);
515 }
516
517 /*
518  * When preening, allow a single quit to signal
519  * a special exit after filesystem checks complete
520  * so that reboot sequence may be interrupted.
521  */
522 void
523 catchquit(sig)
524         int sig;
525 {
526         printf("returning to single-user after filesystem check\n");
527         returntosingle = 1;
528         (void)signal(SIGQUIT, SIG_DFL);
529 }
530
531 /*
532  * Ignore a single quit signal; wait and flush just in case.
533  * Used by child processes in preen.
534  */
535 void
536 voidquit(sig)
537         int sig;
538 {
539
540         sleep(1);
541         (void)signal(SIGQUIT, SIG_IGN);
542         (void)signal(SIGQUIT, SIG_DFL);
543 }
544
545 void
546 infohandler(sig)
547         int sig;
548 {
549         got_siginfo = 1;
550 }
551
552 /*
553  * determine whether an inode should be fixed.
554  */
555 int
556 dofix(idesc, msg)
557         register struct inodesc *idesc;
558         char *msg;
559 {
560
561         switch (idesc->id_fix) {
562
563         case DONTKNOW:
564                 if (idesc->id_type == DATA)
565                         direrror(idesc->id_number, msg);
566                 else
567                         pwarn("%s", msg);
568                 if (preen) {
569                         printf(" (SALVAGED)\n");
570                         idesc->id_fix = FIX;
571                         return (ALTERED);
572                 }
573                 if (reply("SALVAGE") == 0) {
574                         idesc->id_fix = NOFIX;
575                         return (0);
576                 }
577                 idesc->id_fix = FIX;
578                 return (ALTERED);
579
580         case FIX:
581                 return (ALTERED);
582
583         case NOFIX:
584         case IGNORE:
585                 return (0);
586
587         default:
588                 errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
589         }
590         /* NOTREACHED */
591         return (0);
592 }
593
594 #if __STDC__
595 #include <stdarg.h>
596 #else
597 #include <varargs.h>
598 #endif
599
600 /*
601  * An unexpected inconsistency occured.
602  * Die if preening or filesystem is running with soft dependency protocol,
603  * otherwise just print message and continue.
604  */
605 void
606 #if __STDC__
607 pfatal(const char *fmt, ...)
608 #else
609 pfatal(fmt, va_alist)
610         char *fmt;
611         va_dcl
612 #endif
613 {
614         va_list ap;
615 #if __STDC__
616         va_start(ap, fmt);
617 #else
618         va_start(ap);
619 #endif
620         if (!preen) {
621                 (void)vfprintf(stderr, fmt, ap);
622                 va_end(ap);
623                 if (usedsoftdep)
624                         (void)fprintf(stderr,
625                             "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n");
626                 return;
627         }
628         if (cdevname == NULL)
629                 cdevname = "fsck";
630         (void)fprintf(stderr, "%s: ", cdevname);
631         (void)vfprintf(stderr, fmt, ap);
632         (void)fprintf(stderr,
633             "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n",
634             cdevname, usedsoftdep ? " SOFT UPDATE " : " ");
635         ckfini(0);
636         exit(EEXIT);
637 }
638
639 /*
640  * Pwarn just prints a message when not preening or running soft dependency
641  * protocol, or a warning (preceded by filename) when preening.
642  */
643 void
644 #if __STDC__
645 pwarn(const char *fmt, ...)
646 #else
647 pwarn(fmt, va_alist)
648         char *fmt;
649         va_dcl
650 #endif
651 {
652         va_list ap;
653 #if __STDC__
654         va_start(ap, fmt);
655 #else
656         va_start(ap);
657 #endif
658         if (preen)
659                 (void)fprintf(stderr, "%s: ", cdevname);
660         (void)vfprintf(stderr, fmt, ap);
661         va_end(ap);
662 }
663
664 /*
665  * Stub for routines from kernel.
666  */
667 void
668 #if __STDC__
669 panic(const char *fmt, ...)
670 #else
671 panic(fmt, va_alist)
672         char *fmt;
673         va_dcl
674 #endif
675 {
676         va_list ap;
677 #if __STDC__
678         va_start(ap, fmt);
679 #else
680         va_start(ap);
681 #endif
682         pfatal("INTERNAL INCONSISTENCY:");
683         (void)vfprintf(stderr, fmt, ap);
684         va_end(ap);
685         exit(EEXIT);
686 }