]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/fsck_ffs/fsutil.c
Update fsck_ffs buffer cache manager to use TAILQ macros.
[FreeBSD/FreeBSD.git] / sbin / fsck_ffs / fsutil.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  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #if 0
31 #ifndef lint
32 static const char sccsid[] = "@(#)utilities.c   8.6 (Berkeley) 5/19/95";
33 #endif /* not lint */
34 #endif
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <sys/sysctl.h>
42 #include <sys/disk.h>
43 #include <sys/disklabel.h>
44 #include <sys/ioctl.h>
45 #include <sys/stat.h>
46
47 #include <ufs/ufs/dinode.h>
48 #include <ufs/ufs/dir.h>
49 #include <ufs/ffs/fs.h>
50
51 #include <err.h>
52 #include <errno.h>
53 #include <string.h>
54 #include <ctype.h>
55 #include <fstab.h>
56 #include <stdint.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60
61 #include "fsck.h"
62
63 static void slowio_start(void);
64 static void slowio_end(void);
65
66 long    diskreads, totalreads;  /* Disk cache statistics */
67 struct timeval slowio_starttime;
68 int slowio_delay_usec = 10000;  /* Initial IO delay for background fsck */
69 int slowio_pollcnt;
70 static TAILQ_HEAD(buflist, bufarea) bufhead;    /* head of buffer cache list */
71 static int numbufs;                             /* size of buffer cache */
72
73 int
74 ftypeok(union dinode *dp)
75 {
76         switch (DIP(dp, di_mode) & IFMT) {
77
78         case IFDIR:
79         case IFREG:
80         case IFBLK:
81         case IFCHR:
82         case IFLNK:
83         case IFSOCK:
84         case IFIFO:
85                 return (1);
86
87         default:
88                 if (debug)
89                         printf("bad file type 0%o\n", DIP(dp, di_mode));
90                 return (0);
91         }
92 }
93
94 int
95 reply(const char *question)
96 {
97         int persevere;
98         char c;
99
100         if (preen)
101                 pfatal("INTERNAL ERROR: GOT TO reply()");
102         persevere = !strcmp(question, "CONTINUE");
103         printf("\n");
104         if (!persevere && (nflag || (fswritefd < 0 && bkgrdflag == 0))) {
105                 printf("%s? no\n\n", question);
106                 resolved = 0;
107                 return (0);
108         }
109         if (yflag || (persevere && nflag)) {
110                 printf("%s? yes\n\n", question);
111                 return (1);
112         }
113         do      {
114                 printf("%s? [yn] ", question);
115                 (void) fflush(stdout);
116                 c = getc(stdin);
117                 while (c != '\n' && getc(stdin) != '\n') {
118                         if (feof(stdin)) {
119                                 resolved = 0;
120                                 return (0);
121                         }
122                 }
123         } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
124         printf("\n");
125         if (c == 'y' || c == 'Y')
126                 return (1);
127         resolved = 0;
128         return (0);
129 }
130
131 /*
132  * Look up state information for an inode.
133  */
134 struct inostat *
135 inoinfo(ino_t inum)
136 {
137         static struct inostat unallocated = { USTATE, 0, 0 };
138         struct inostatlist *ilp;
139         int iloff;
140
141         if (inum > maxino)
142                 errx(EEXIT, "inoinfo: inumber %ju out of range",
143                     (uintmax_t)inum);
144         ilp = &inostathead[inum / sblock.fs_ipg];
145         iloff = inum % sblock.fs_ipg;
146         if (iloff >= ilp->il_numalloced)
147                 return (&unallocated);
148         return (&ilp->il_stat[iloff]);
149 }
150
151 /*
152  * Malloc buffers and set up cache.
153  */
154 void
155 bufinit(void)
156 {
157         struct bufarea *bp;
158         long bufcnt, i;
159         char *bufp;
160
161         pbp = pdirbp = (struct bufarea *)0;
162         bufp = malloc((unsigned int)sblock.fs_bsize);
163         if (bufp == 0)
164                 errx(EEXIT, "cannot allocate buffer pool");
165         cgblk.b_un.b_buf = bufp;
166         initbarea(&cgblk);
167         TAILQ_INIT(&bufhead);
168         bufcnt = MAXBUFS;
169         if (bufcnt < MINBUFS)
170                 bufcnt = MINBUFS;
171         for (i = 0; i < bufcnt; i++) {
172                 bp = (struct bufarea *)malloc(sizeof(struct bufarea));
173                 bufp = malloc((unsigned int)sblock.fs_bsize);
174                 if (bp == NULL || bufp == NULL) {
175                         if (i >= MINBUFS)
176                                 break;
177                         errx(EEXIT, "cannot allocate buffer pool");
178                 }
179                 bp->b_un.b_buf = bufp;
180                 TAILQ_INSERT_HEAD(&bufhead, bp, b_list);
181                 initbarea(bp);
182         }
183         numbufs = i;    /* save number of buffers */
184 }
185
186 /*
187  * Manage a cache of directory blocks.
188  */
189 struct bufarea *
190 getdatablk(ufs2_daddr_t blkno, long size)
191 {
192         struct bufarea *bp;
193
194         TAILQ_FOREACH(bp, &bufhead, b_list)
195                 if (bp->b_bno == fsbtodb(&sblock, blkno))
196                         goto foundit;
197         TAILQ_FOREACH_REVERSE(bp, &bufhead, buflist, b_list)
198                 if ((bp->b_flags & B_INUSE) == 0)
199                         break;
200         if (bp == NULL)
201                 errx(EEXIT, "deadlocked buffer pool");
202         getblk(bp, blkno, size);
203         /* fall through */
204 foundit:
205         TAILQ_REMOVE(&bufhead, bp, b_list);
206         TAILQ_INSERT_HEAD(&bufhead, bp, b_list);
207         bp->b_flags |= B_INUSE;
208         return (bp);
209 }
210
211 void
212 getblk(struct bufarea *bp, ufs2_daddr_t blk, long size)
213 {
214         ufs2_daddr_t dblk;
215
216         totalreads++;
217         dblk = fsbtodb(&sblock, blk);
218         if (bp->b_bno != dblk) {
219                 flush(fswritefd, bp);
220                 diskreads++;
221                 bp->b_errs = blread(fsreadfd, bp->b_un.b_buf, dblk, size);
222                 bp->b_bno = dblk;
223                 bp->b_size = size;
224         }
225 }
226
227 void
228 flush(int fd, struct bufarea *bp)
229 {
230         int i, j;
231
232         if (!bp->b_dirty)
233                 return;
234         bp->b_dirty = 0;
235         if (fswritefd < 0) {
236                 pfatal("WRITING IN READ_ONLY MODE.\n");
237                 return;
238         }
239         if (bp->b_errs != 0)
240                 pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
241                     (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
242                     (long long)bp->b_bno);
243         bp->b_errs = 0;
244         blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size);
245         if (bp != &sblk)
246                 return;
247         for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
248                 blwrite(fswritefd, (char *)sblock.fs_csp + i,
249                     fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
250                     sblock.fs_cssize - i < sblock.fs_bsize ?
251                     sblock.fs_cssize - i : sblock.fs_bsize);
252         }
253 }
254
255 void
256 rwerror(const char *mesg, ufs2_daddr_t blk)
257 {
258
259         if (bkgrdcheck)
260                 exit(EEXIT);
261         if (preen == 0)
262                 printf("\n");
263         pfatal("CANNOT %s: %ld", mesg, (long)blk);
264         if (reply("CONTINUE") == 0)
265                 exit(EEXIT);
266 }
267
268 void
269 ckfini(int markclean)
270 {
271         struct bufarea *bp, *nbp;
272         int ofsmodified, cnt;
273
274         if (bkgrdflag) {
275                 unlink(snapname);
276                 if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) {
277                         cmd.value = FS_UNCLEAN;
278                         cmd.size = markclean ? -1 : 1;
279                         if (sysctlbyname("vfs.ffs.setflags", 0, 0,
280                             &cmd, sizeof cmd) == -1)
281                                 rwerror("SET FILE SYSTEM FLAGS", FS_UNCLEAN);
282                         if (!preen) {
283                                 printf("\n***** FILE SYSTEM MARKED %s *****\n",
284                                     markclean ? "CLEAN" : "DIRTY");
285                                 if (!markclean)
286                                         rerun = 1;
287                         }
288                 } else if (!preen && !markclean) {
289                         printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
290                         rerun = 1;
291                 }
292         }
293         if (debug && totalreads > 0)
294                 printf("cache with %d buffers missed %ld of %ld (%d%%)\n",
295                     numbufs, diskreads, totalreads,
296                     (int)(diskreads * 100 / totalreads));
297         if (fswritefd < 0) {
298                 (void)close(fsreadfd);
299                 return;
300         }
301         flush(fswritefd, &sblk);
302         if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
303             sblk.b_bno != sblock.fs_sblockloc / dev_bsize &&
304             !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
305                 sblk.b_bno = sblock.fs_sblockloc / dev_bsize;
306                 sbdirty();
307                 flush(fswritefd, &sblk);
308         }
309         flush(fswritefd, &cgblk);
310         free(cgblk.b_un.b_buf);
311         cnt = 0;
312         TAILQ_FOREACH_REVERSE_SAFE(bp, &bufhead, buflist, b_list, nbp) {
313                 TAILQ_REMOVE(&bufhead, bp, b_list);
314                 cnt++;
315                 flush(fswritefd, bp);
316                 free(bp->b_un.b_buf);
317                 free((char *)bp);
318         }
319         if (numbufs != cnt)
320                 errx(EEXIT, "panic: lost %d buffers", numbufs - cnt);
321         pbp = pdirbp = (struct bufarea *)0;
322         if (cursnapshot == 0 && sblock.fs_clean != markclean) {
323                 if ((sblock.fs_clean = markclean) != 0) {
324                         sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK);
325                         sblock.fs_pendingblocks = 0;
326                         sblock.fs_pendinginodes = 0;
327                 }
328                 sbdirty();
329                 ofsmodified = fsmodified;
330                 flush(fswritefd, &sblk);
331                 fsmodified = ofsmodified;
332                 if (!preen) {
333                         printf("\n***** FILE SYSTEM MARKED %s *****\n",
334                             markclean ? "CLEAN" : "DIRTY");
335                         if (!markclean)
336                                 rerun = 1;
337                 }
338         } else if (!preen) {
339                 if (markclean) {
340                         printf("\n***** FILE SYSTEM IS CLEAN *****\n");
341                 } else {
342                         printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
343                         rerun = 1;
344                 }
345         }
346         (void)close(fsreadfd);
347         (void)close(fswritefd);
348 }
349
350 int
351 blread(int fd, char *buf, ufs2_daddr_t blk, long size)
352 {
353         char *cp;
354         int i, errs;
355         off_t offset;
356
357         offset = blk;
358         offset *= dev_bsize;
359         if (bkgrdflag)
360                 slowio_start();
361         if (lseek(fd, offset, 0) < 0)
362                 rwerror("SEEK BLK", blk);
363         else if (read(fd, buf, (int)size) == size) {
364                 if (bkgrdflag)
365                         slowio_end();
366                 return (0);
367         }
368         rwerror("READ BLK", blk);
369         if (lseek(fd, offset, 0) < 0)
370                 rwerror("SEEK BLK", blk);
371         errs = 0;
372         memset(buf, 0, (size_t)size);
373         printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
374         for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
375                 if (read(fd, cp, (int)secsize) != secsize) {
376                         (void)lseek(fd, offset + i + secsize, 0);
377                         if (secsize != dev_bsize && dev_bsize != 1)
378                                 printf(" %jd (%jd),",
379                                     (intmax_t)(blk * dev_bsize + i) / secsize,
380                                     (intmax_t)blk + i / dev_bsize);
381                         else
382                                 printf(" %jd,", (intmax_t)blk + i / dev_bsize);
383                         errs++;
384                 }
385         }
386         printf("\n");
387         if (errs)
388                 resolved = 0;
389         return (errs);
390 }
391
392 void
393 blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size)
394 {
395         int i;
396         char *cp;
397         off_t offset;
398
399         if (fd < 0)
400                 return;
401         offset = blk;
402         offset *= dev_bsize;
403         if (lseek(fd, offset, 0) < 0)
404                 rwerror("SEEK BLK", blk);
405         else if (write(fd, buf, size) == size) {
406                 fsmodified = 1;
407                 return;
408         }
409         resolved = 0;
410         rwerror("WRITE BLK", blk);
411         if (lseek(fd, offset, 0) < 0)
412                 rwerror("SEEK BLK", blk);
413         printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
414         for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
415                 if (write(fd, cp, dev_bsize) != dev_bsize) {
416                         (void)lseek(fd, offset + i + dev_bsize, 0);
417                         printf(" %jd,", (intmax_t)blk + i / dev_bsize);
418                 }
419         printf("\n");
420         return;
421 }
422
423 void
424 blerase(int fd, ufs2_daddr_t blk, long size)
425 {
426         off_t ioarg[2];
427
428         if (fd < 0)
429                 return;
430         ioarg[0] = blk * dev_bsize;
431         ioarg[1] = size;
432         ioctl(fd, DIOCGDELETE, ioarg);
433         /* we don't really care if we succeed or not */
434         return;
435 }
436
437 /*
438  * Verify cylinder group's magic number and other parameters.  If the
439  * test fails, offer an option to rebuild the whole cylinder group.
440  */
441 int
442 check_cgmagic(int cg, struct cg *cgp)
443 {
444
445         /*
446          * Extended cylinder group checks.
447          */
448         if (cg_chkmagic(cgp) &&
449             ((sblock.fs_magic == FS_UFS1_MAGIC &&
450               cgp->cg_old_niblk == sblock.fs_ipg &&
451               cgp->cg_ndblk <= sblock.fs_fpg &&
452               cgp->cg_old_ncyl <= sblock.fs_old_cpg) ||
453              (sblock.fs_magic == FS_UFS2_MAGIC &&
454               cgp->cg_niblk == sblock.fs_ipg &&
455               cgp->cg_ndblk <= sblock.fs_fpg &&
456               cgp->cg_initediblk <= sblock.fs_ipg))) {
457                 return (1);
458         }
459         pfatal("CYLINDER GROUP %d: BAD MAGIC NUMBER", cg);
460         if (!reply("REBUILD CYLINDER GROUP")) {
461                 printf("YOU WILL NEED TO RERUN FSCK.\n");
462                 rerun = 1;
463                 return (1);
464         }
465         /*
466          * Zero out the cylinder group and then initialize critical fields.
467          * Bit maps and summaries will be recalculated by later passes.
468          */
469         memset(cgp, 0, (size_t)sblock.fs_cgsize);
470         cgp->cg_magic = CG_MAGIC;
471         cgp->cg_cgx = cg;
472         cgp->cg_niblk = sblock.fs_ipg;
473         cgp->cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ?
474             sblock.fs_ipg : 2 * INOPB(&sblock);
475         if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size)
476                 cgp->cg_ndblk = sblock.fs_fpg;
477         else
478                 cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg);
479         cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield);
480         if (sblock.fs_magic == FS_UFS1_MAGIC) {
481                 cgp->cg_niblk = 0;
482                 cgp->cg_initediblk = 0;
483                 cgp->cg_old_ncyl = sblock.fs_old_cpg;
484                 cgp->cg_old_niblk = sblock.fs_ipg;
485                 cgp->cg_old_btotoff = cgp->cg_iusedoff;
486                 cgp->cg_old_boff = cgp->cg_old_btotoff +
487                     sblock.fs_old_cpg * sizeof(int32_t);
488                 cgp->cg_iusedoff = cgp->cg_old_boff +
489                     sblock.fs_old_cpg * sizeof(u_int16_t);
490         }
491         cgp->cg_freeoff = cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
492         cgp->cg_nextfreeoff = cgp->cg_freeoff + howmany(sblock.fs_fpg,CHAR_BIT);
493         if (sblock.fs_contigsumsize > 0) {
494                 cgp->cg_nclusterblks = cgp->cg_ndblk / sblock.fs_frag;
495                 cgp->cg_clustersumoff =
496                     roundup(cgp->cg_nextfreeoff, sizeof(u_int32_t));
497                 cgp->cg_clustersumoff -= sizeof(u_int32_t);
498                 cgp->cg_clusteroff = cgp->cg_clustersumoff +
499                     (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
500                 cgp->cg_nextfreeoff = cgp->cg_clusteroff +
501                     howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
502         }
503         cgdirty();
504         return (0);
505 }
506
507 /*
508  * allocate a data block with the specified number of fragments
509  */
510 ufs2_daddr_t
511 allocblk(long frags)
512 {
513         int i, j, k, cg, baseblk;
514         struct cg *cgp = &cgrp;
515
516         if (frags <= 0 || frags > sblock.fs_frag)
517                 return (0);
518         for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
519                 for (j = 0; j <= sblock.fs_frag - frags; j++) {
520                         if (testbmap(i + j))
521                                 continue;
522                         for (k = 1; k < frags; k++)
523                                 if (testbmap(i + j + k))
524                                         break;
525                         if (k < frags) {
526                                 j += k;
527                                 continue;
528                         }
529                         cg = dtog(&sblock, i + j);
530                         getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
531                         if (!check_cgmagic(cg, cgp))
532                                 return (0);
533                         baseblk = dtogd(&sblock, i + j);
534                         for (k = 0; k < frags; k++) {
535                                 setbmap(i + j + k);
536                                 clrbit(cg_blksfree(cgp), baseblk + k);
537                         }
538                         n_blks += frags;
539                         if (frags == sblock.fs_frag)
540                                 cgp->cg_cs.cs_nbfree--;
541                         else
542                                 cgp->cg_cs.cs_nffree -= frags;
543                         cgdirty();
544                         return (i + j);
545                 }
546         }
547         return (0);
548 }
549
550 /*
551  * Free a previously allocated block
552  */
553 void
554 freeblk(ufs2_daddr_t blkno, long frags)
555 {
556         struct inodesc idesc;
557
558         idesc.id_blkno = blkno;
559         idesc.id_numfrags = frags;
560         (void)pass4check(&idesc);
561 }
562
563 /* Slow down IO so as to leave some disk bandwidth for other processes */
564 void
565 slowio_start()
566 {
567
568         /* Delay one in every 8 operations */
569         slowio_pollcnt = (slowio_pollcnt + 1) & 7;
570         if (slowio_pollcnt == 0) {
571                 gettimeofday(&slowio_starttime, NULL);
572         }
573 }
574
575 void
576 slowio_end()
577 {
578         struct timeval tv;
579         int delay_usec;
580
581         if (slowio_pollcnt != 0)
582                 return;
583
584         /* Update the slowdown interval. */
585         gettimeofday(&tv, NULL);
586         delay_usec = (tv.tv_sec - slowio_starttime.tv_sec) * 1000000 +
587             (tv.tv_usec - slowio_starttime.tv_usec);
588         if (delay_usec < 64)
589                 delay_usec = 64;
590         if (delay_usec > 2500000)
591                 delay_usec = 2500000;
592         slowio_delay_usec = (slowio_delay_usec * 63 + delay_usec) >> 6;
593         /* delay by 8 times the average IO delay */
594         if (slowio_delay_usec > 64)
595                 usleep(slowio_delay_usec * 8);
596 }
597
598 /*
599  * Find a pathname
600  */
601 void
602 getpathname(char *namebuf, ino_t curdir, ino_t ino)
603 {
604         int len;
605         char *cp;
606         struct inodesc idesc;
607         static int busy = 0;
608
609         if (curdir == ino && ino == ROOTINO) {
610                 (void)strcpy(namebuf, "/");
611                 return;
612         }
613         if (busy || !INO_IS_DVALID(curdir)) {
614                 (void)strcpy(namebuf, "?");
615                 return;
616         }
617         busy = 1;
618         memset(&idesc, 0, sizeof(struct inodesc));
619         idesc.id_type = DATA;
620         idesc.id_fix = IGNORE;
621         cp = &namebuf[MAXPATHLEN - 1];
622         *cp = '\0';
623         if (curdir != ino) {
624                 idesc.id_parent = curdir;
625                 goto namelookup;
626         }
627         while (ino != ROOTINO) {
628                 idesc.id_number = ino;
629                 idesc.id_func = findino;
630                 idesc.id_name = strdup("..");
631                 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
632                         break;
633         namelookup:
634                 idesc.id_number = idesc.id_parent;
635                 idesc.id_parent = ino;
636                 idesc.id_func = findname;
637                 idesc.id_name = namebuf;
638                 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
639                         break;
640                 len = strlen(namebuf);
641                 cp -= len;
642                 memmove(cp, namebuf, (size_t)len);
643                 *--cp = '/';
644                 if (cp < &namebuf[MAXNAMLEN])
645                         break;
646                 ino = idesc.id_number;
647         }
648         busy = 0;
649         if (ino != ROOTINO)
650                 *--cp = '?';
651         memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
652 }
653
654 void
655 catch(int sig __unused)
656 {
657
658         ckfini(0);
659         exit(12);
660 }
661
662 /*
663  * When preening, allow a single quit to signal
664  * a special exit after file system checks complete
665  * so that reboot sequence may be interrupted.
666  */
667 void
668 catchquit(int sig __unused)
669 {
670         printf("returning to single-user after file system check\n");
671         returntosingle = 1;
672         (void)signal(SIGQUIT, SIG_DFL);
673 }
674
675 /*
676  * determine whether an inode should be fixed.
677  */
678 int
679 dofix(struct inodesc *idesc, const char *msg)
680 {
681
682         switch (idesc->id_fix) {
683
684         case DONTKNOW:
685                 if (idesc->id_type == DATA)
686                         direrror(idesc->id_number, msg);
687                 else
688                         pwarn("%s", msg);
689                 if (preen) {
690                         printf(" (SALVAGED)\n");
691                         idesc->id_fix = FIX;
692                         return (ALTERED);
693                 }
694                 if (reply("SALVAGE") == 0) {
695                         idesc->id_fix = NOFIX;
696                         return (0);
697                 }
698                 idesc->id_fix = FIX;
699                 return (ALTERED);
700
701         case FIX:
702                 return (ALTERED);
703
704         case NOFIX:
705         case IGNORE:
706                 return (0);
707
708         default:
709                 errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
710         }
711         /* NOTREACHED */
712         return (0);
713 }
714
715 #include <stdarg.h>
716
717 /*
718  * An unexpected inconsistency occurred.
719  * Die if preening or file system is running with soft dependency protocol,
720  * otherwise just print message and continue.
721  */
722 void
723 pfatal(const char *fmt, ...)
724 {
725         va_list ap;
726         va_start(ap, fmt);
727         if (!preen) {
728                 (void)vfprintf(stdout, fmt, ap);
729                 va_end(ap);
730                 if (usedsoftdep)
731                         (void)fprintf(stdout,
732                             "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n");
733                 /*
734                  * Force foreground fsck to clean up inconsistency.
735                  */
736                 if (bkgrdflag) {
737                         cmd.value = FS_NEEDSFSCK;
738                         cmd.size = 1;
739                         if (sysctlbyname("vfs.ffs.setflags", 0, 0,
740                             &cmd, sizeof cmd) == -1)
741                                 pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
742                         fprintf(stdout, "CANNOT RUN IN BACKGROUND\n");
743                         ckfini(0);
744                         exit(EEXIT);
745                 }
746                 return;
747         }
748         if (cdevname == NULL)
749                 cdevname = strdup("fsck");
750         (void)fprintf(stdout, "%s: ", cdevname);
751         (void)vfprintf(stdout, fmt, ap);
752         (void)fprintf(stdout,
753             "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n",
754             cdevname, usedsoftdep ? " SOFT UPDATE " : " ");
755         /*
756          * Force foreground fsck to clean up inconsistency.
757          */
758         if (bkgrdflag) {
759                 cmd.value = FS_NEEDSFSCK;
760                 cmd.size = 1;
761                 if (sysctlbyname("vfs.ffs.setflags", 0, 0,
762                     &cmd, sizeof cmd) == -1)
763                         pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
764         }
765         ckfini(0);
766         exit(EEXIT);
767 }
768
769 /*
770  * Pwarn just prints a message when not preening or running soft dependency
771  * protocol, or a warning (preceded by filename) when preening.
772  */
773 void
774 pwarn(const char *fmt, ...)
775 {
776         va_list ap;
777         va_start(ap, fmt);
778         if (preen)
779                 (void)fprintf(stdout, "%s: ", cdevname);
780         (void)vfprintf(stdout, fmt, ap);
781         va_end(ap);
782 }
783
784 /*
785  * Stub for routines from kernel.
786  */
787 void
788 panic(const char *fmt, ...)
789 {
790         va_list ap;
791         va_start(ap, fmt);
792         pfatal("INTERNAL INCONSISTENCY:");
793         (void)vfprintf(stdout, fmt, ap);
794         va_end(ap);
795         exit(EEXIT);
796 }