166864Sadrian/*
266864Sadrian * Copyright (c) 1980, 1986, 1993
366864Sadrian *	The Regents of the University of California.  All rights reserved.
466864Sadrian *
566864Sadrian * Redistribution and use in source and binary forms, with or without
666864Sadrian * modification, are permitted provided that the following conditions
766864Sadrian * are met:
866864Sadrian * 1. Redistributions of source code must retain the above copyright
966864Sadrian *    notice, this list of conditions and the following disclaimer.
1066864Sadrian * 2. Redistributions in binary form must reproduce the above copyright
1166864Sadrian *    notice, this list of conditions and the following disclaimer in the
1266864Sadrian *    documentation and/or other materials provided with the distribution.
1366864Sadrian * 4. Neither the name of the University nor the names of its contributors
1466864Sadrian *    may be used to endorse or promote products derived from this software
1566864Sadrian *    without specific prior written permission.
1666864Sadrian *
1766864Sadrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1866864Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1966864Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2066864Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2166864Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2266864Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2366864Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2466864Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2566864Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2666864Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2766864Sadrian * SUCH DAMAGE.
2866864Sadrian */
2966864Sadrian
30114589Sobrien#if 0
3166864Sadrian#ifndef lint
3266864Sadrianstatic const char sccsid[] = "@(#)utilities.c	8.6 (Berkeley) 5/19/95";
33114589Sobrien#endif /* not lint */
3466864Sadrian#endif
35114589Sobrien#include <sys/cdefs.h>
36114589Sobrien__FBSDID("$FreeBSD$");
3766864Sadrian
3866864Sadrian#include <sys/param.h>
39107671Siedowse#include <sys/time.h>
4066864Sadrian#include <sys/types.h>
4186514Siedowse#include <sys/sysctl.h>
42221233Sdes#include <sys/disk.h>
4398542Smckusick#include <sys/disklabel.h>
44221233Sdes#include <sys/ioctl.h>
4566864Sadrian#include <sys/stat.h>
4666864Sadrian
4766864Sadrian#include <ufs/ufs/dinode.h>
4866864Sadrian#include <ufs/ufs/dir.h>
4966864Sadrian#include <ufs/ffs/fs.h>
5066864Sadrian
5166864Sadrian#include <err.h>
5266864Sadrian#include <errno.h>
5366864Sadrian#include <string.h>
5466864Sadrian#include <ctype.h>
5566864Sadrian#include <fstab.h>
56101037Smux#include <stdint.h>
5766864Sadrian#include <stdio.h>
5866864Sadrian#include <stdlib.h>
59247212Smckusick#include <time.h>
6066864Sadrian#include <unistd.h>
6166864Sadrian
6266864Sadrian#include "fsck.h"
6366864Sadrian
64107671Siedowsestatic void slowio_start(void);
65107671Siedowsestatic void slowio_end(void);
66247212Smckusickstatic void printIOstats(void);
67107671Siedowse
68247212Smckusickstatic long diskreads, totaldiskreads, totalreads; /* Disk cache statistics */
69247212Smckusickstatic struct timespec startpass, finishpass;
70107671Siedowsestruct timeval slowio_starttime;
71107671Siedowseint slowio_delay_usec = 10000;	/* Initial IO delay for background fsck */
72107671Siedowseint slowio_pollcnt;
73248658Smckusickstatic struct bufarea cgblk;	/* backup buffer for cylinder group blocks */
74246812Smckusickstatic TAILQ_HEAD(buflist, bufarea) bufhead;	/* head of buffer cache list */
75246812Smckusickstatic int numbufs;				/* size of buffer cache */
76247212Smckusickstatic char *buftype[BT_NUMBUFTYPES] = BT_NAMES;
77260178Sscottlstatic struct bufarea *cgbufs;	/* header for cylinder group cache */
78260178Sscottlstatic int flushtries;		/* number of tries to reclaim memory */
7966864Sadrian
80260178Sscottlvoid
81260178Sscottlfsutilinit(void)
82260178Sscottl{
83260178Sscottl	diskreads = totaldiskreads = totalreads = 0;
84260178Sscottl	bzero(&startpass, sizeof(struct timespec));
85260178Sscottl	bzero(&finishpass, sizeof(struct timespec));
86260178Sscottl	bzero(&slowio_starttime, sizeof(struct timeval));
87260178Sscottl	slowio_delay_usec = 10000;
88260178Sscottl	slowio_pollcnt = 0;
89260178Sscottl	bzero(&cgblk, sizeof(struct bufarea));
90260178Sscottl	TAILQ_INIT(&bufhead);
91260178Sscottl	numbufs = 0;
92260178Sscottl	/* buftype ? */
93260178Sscottl	cgbufs = NULL;
94260178Sscottl	flushtries = 0;
95260178Sscottl}
96260178Sscottl
9766864Sadrianint
9898542Smckusickftypeok(union dinode *dp)
9966864Sadrian{
10098542Smckusick	switch (DIP(dp, di_mode) & IFMT) {
10166864Sadrian
10266864Sadrian	case IFDIR:
10366864Sadrian	case IFREG:
10466864Sadrian	case IFBLK:
10566864Sadrian	case IFCHR:
10666864Sadrian	case IFLNK:
10766864Sadrian	case IFSOCK:
10866864Sadrian	case IFIFO:
10966864Sadrian		return (1);
11066864Sadrian
11166864Sadrian	default:
11266864Sadrian		if (debug)
11398542Smckusick			printf("bad file type 0%o\n", DIP(dp, di_mode));
11466864Sadrian		return (0);
11566864Sadrian	}
11666864Sadrian}
11766864Sadrian
11866864Sadrianint
119100935Sphkreply(const char *question)
12066864Sadrian{
12166864Sadrian	int persevere;
12266864Sadrian	char c;
12366864Sadrian
12466864Sadrian	if (preen)
12566864Sadrian		pfatal("INTERNAL ERROR: GOT TO reply()");
12666864Sadrian	persevere = !strcmp(question, "CONTINUE");
12766864Sadrian	printf("\n");
12874556Smckusick	if (!persevere && (nflag || (fswritefd < 0 && bkgrdflag == 0))) {
12966864Sadrian		printf("%s? no\n\n", question);
13066864Sadrian		resolved = 0;
13166864Sadrian		return (0);
13266864Sadrian	}
13366864Sadrian	if (yflag || (persevere && nflag)) {
13466864Sadrian		printf("%s? yes\n\n", question);
13566864Sadrian		return (1);
13666864Sadrian	}
13766864Sadrian	do	{
13866864Sadrian		printf("%s? [yn] ", question);
13966864Sadrian		(void) fflush(stdout);
14066864Sadrian		c = getc(stdin);
14166864Sadrian		while (c != '\n' && getc(stdin) != '\n') {
14266864Sadrian			if (feof(stdin)) {
14366864Sadrian				resolved = 0;
14466864Sadrian				return (0);
14566864Sadrian			}
14666864Sadrian		}
14766864Sadrian	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
14866864Sadrian	printf("\n");
14966864Sadrian	if (c == 'y' || c == 'Y')
15066864Sadrian		return (1);
15166864Sadrian	resolved = 0;
15266864Sadrian	return (0);
15366864Sadrian}
15466864Sadrian
15566864Sadrian/*
15666864Sadrian * Look up state information for an inode.
15766864Sadrian */
15866864Sadrianstruct inostat *
15992839Simpinoinfo(ino_t inum)
16066864Sadrian{
16166864Sadrian	static struct inostat unallocated = { USTATE, 0, 0 };
16266864Sadrian	struct inostatlist *ilp;
16366864Sadrian	int iloff;
16466864Sadrian
16566864Sadrian	if (inum > maxino)
166241012Smdf		errx(EEXIT, "inoinfo: inumber %ju out of range",
167241012Smdf		    (uintmax_t)inum);
16866864Sadrian	ilp = &inostathead[inum / sblock.fs_ipg];
16966864Sadrian	iloff = inum % sblock.fs_ipg;
17066864Sadrian	if (iloff >= ilp->il_numalloced)
17166864Sadrian		return (&unallocated);
17266864Sadrian	return (&ilp->il_stat[iloff]);
17366864Sadrian}
17466864Sadrian
17566864Sadrian/*
17666864Sadrian * Malloc buffers and set up cache.
17766864Sadrian */
17866864Sadrianvoid
17992839Simpbufinit(void)
18066864Sadrian{
18192806Sobrien	struct bufarea *bp;
18266864Sadrian	long bufcnt, i;
18366864Sadrian	char *bufp;
18466864Sadrian
18566864Sadrian	pbp = pdirbp = (struct bufarea *)0;
186248658Smckusick	bufp = Malloc((unsigned int)sblock.fs_bsize);
18766864Sadrian	if (bufp == 0)
18866864Sadrian		errx(EEXIT, "cannot allocate buffer pool");
18966864Sadrian	cgblk.b_un.b_buf = bufp;
190247212Smckusick	initbarea(&cgblk, BT_CYLGRP);
191246812Smckusick	TAILQ_INIT(&bufhead);
192246812Smckusick	bufcnt = MAXBUFS;
19366864Sadrian	if (bufcnt < MINBUFS)
19466864Sadrian		bufcnt = MINBUFS;
19566864Sadrian	for (i = 0; i < bufcnt; i++) {
196248658Smckusick		bp = (struct bufarea *)Malloc(sizeof(struct bufarea));
197248658Smckusick		bufp = Malloc((unsigned int)sblock.fs_bsize);
19866864Sadrian		if (bp == NULL || bufp == NULL) {
19966864Sadrian			if (i >= MINBUFS)
20066864Sadrian				break;
20166864Sadrian			errx(EEXIT, "cannot allocate buffer pool");
20266864Sadrian		}
20366864Sadrian		bp->b_un.b_buf = bufp;
204246812Smckusick		TAILQ_INSERT_HEAD(&bufhead, bp, b_list);
205247212Smckusick		initbarea(bp, BT_UNKNOWN);
20666864Sadrian	}
207246812Smckusick	numbufs = i;	/* save number of buffers */
208247212Smckusick	for (i = 0; i < BT_NUMBUFTYPES; i++) {
209247212Smckusick		readtime[i].tv_sec = totalreadtime[i].tv_sec = 0;
210247212Smckusick		readtime[i].tv_nsec = totalreadtime[i].tv_nsec = 0;
211247212Smckusick		readcnt[i] = totalreadcnt[i] = 0;
212247212Smckusick	}
21366864Sadrian}
21466864Sadrian
21566864Sadrian/*
216248658Smckusick * Manage cylinder group buffers.
217248658Smckusick */
218248658Smckusickstatic struct bufarea *cgbufs;	/* header for cylinder group cache */
219248658Smckusickstatic int flushtries;		/* number of tries to reclaim memory */
220248658Smckusick
221248658Smckusickstruct bufarea *
222248658Smckusickcgget(int cg)
223248658Smckusick{
224248658Smckusick	struct bufarea *cgbp;
225248658Smckusick	struct cg *cgp;
226248658Smckusick
227248658Smckusick	if (cgbufs == NULL) {
228263629Smckusick		cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea));
229248658Smckusick		if (cgbufs == NULL)
230248658Smckusick			errx(EEXIT, "cannot allocate cylinder group buffers");
231248658Smckusick	}
232248658Smckusick	cgbp = &cgbufs[cg];
233248658Smckusick	if (cgbp->b_un.b_cg != NULL)
234248658Smckusick		return (cgbp);
235248658Smckusick	cgp = NULL;
236248658Smckusick	if (flushtries == 0)
237248658Smckusick		cgp = malloc((unsigned int)sblock.fs_cgsize);
238248658Smckusick	if (cgp == NULL) {
239248658Smckusick		getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
240248658Smckusick		return (&cgblk);
241248658Smckusick	}
242248658Smckusick	cgbp->b_un.b_cg = cgp;
243248658Smckusick	initbarea(cgbp, BT_CYLGRP);
244248658Smckusick	getblk(cgbp, cgtod(&sblock, cg), sblock.fs_cgsize);
245248658Smckusick	return (cgbp);
246248658Smckusick}
247248658Smckusick
248248658Smckusick/*
249248658Smckusick * Attempt to flush a cylinder group cache entry.
250248658Smckusick * Return whether the flush was successful.
251248658Smckusick */
252248658Smckusickint
253248658Smckusickflushentry(void)
254248658Smckusick{
255248658Smckusick	struct bufarea *cgbp;
256248658Smckusick
257263629Smckusick	if (flushtries == sblock.fs_ncg || cgbufs == NULL)
258263629Smckusick		return (0);
259248658Smckusick	cgbp = &cgbufs[flushtries++];
260248658Smckusick	if (cgbp->b_un.b_cg == NULL)
261248658Smckusick		return (0);
262248658Smckusick	flush(fswritefd, cgbp);
263248658Smckusick	free(cgbp->b_un.b_buf);
264248658Smckusick	cgbp->b_un.b_buf = NULL;
265248658Smckusick	return (1);
266248658Smckusick}
267248658Smckusick
268248658Smckusick/*
26966864Sadrian * Manage a cache of directory blocks.
27066864Sadrian */
27166864Sadrianstruct bufarea *
272247212Smckusickgetdatablk(ufs2_daddr_t blkno, long size, int type)
27366864Sadrian{
27492806Sobrien	struct bufarea *bp;
27566864Sadrian
276246812Smckusick	TAILQ_FOREACH(bp, &bufhead, b_list)
27766864Sadrian		if (bp->b_bno == fsbtodb(&sblock, blkno))
27866864Sadrian			goto foundit;
279246812Smckusick	TAILQ_FOREACH_REVERSE(bp, &bufhead, buflist, b_list)
28066864Sadrian		if ((bp->b_flags & B_INUSE) == 0)
28166864Sadrian			break;
282246812Smckusick	if (bp == NULL)
28366864Sadrian		errx(EEXIT, "deadlocked buffer pool");
284247212Smckusick	bp->b_type = type;
28566864Sadrian	getblk(bp, blkno, size);
28666864Sadrian	/* fall through */
28766864Sadrianfoundit:
288247212Smckusick	if (debug && bp->b_type != type)
289247212Smckusick		printf("Buffer type changed from %s to %s\n",
290247212Smckusick		    buftype[bp->b_type], buftype[type]);
291246812Smckusick	TAILQ_REMOVE(&bufhead, bp, b_list);
292246812Smckusick	TAILQ_INSERT_HEAD(&bufhead, bp, b_list);
29366864Sadrian	bp->b_flags |= B_INUSE;
29466864Sadrian	return (bp);
29566864Sadrian}
29666864Sadrian
297247212Smckusick/*
298247212Smckusick * Timespec operations (from <sys/time.h>).
299247212Smckusick */
300247212Smckusick#define	timespecsub(vvp, uvp)						\
301247212Smckusick	do {								\
302247212Smckusick		(vvp)->tv_sec -= (uvp)->tv_sec;				\
303247212Smckusick		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
304247212Smckusick		if ((vvp)->tv_nsec < 0) {				\
305247212Smckusick			(vvp)->tv_sec--;				\
306247212Smckusick			(vvp)->tv_nsec += 1000000000;			\
307247212Smckusick		}							\
308247212Smckusick	} while (0)
309247212Smckusick#define	timespecadd(vvp, uvp)						\
310247212Smckusick	do {								\
311247212Smckusick		(vvp)->tv_sec += (uvp)->tv_sec;				\
312247212Smckusick		(vvp)->tv_nsec += (uvp)->tv_nsec;			\
313247212Smckusick		if ((vvp)->tv_nsec >= 1000000000) {			\
314247212Smckusick			(vvp)->tv_sec++;				\
315247212Smckusick			(vvp)->tv_nsec -= 1000000000;			\
316247212Smckusick		}							\
317247212Smckusick	} while (0)
318247212Smckusick
31966864Sadrianvoid
32098542Smckusickgetblk(struct bufarea *bp, ufs2_daddr_t blk, long size)
32166864Sadrian{
32298542Smckusick	ufs2_daddr_t dblk;
323247212Smckusick	struct timespec start, finish;
32466864Sadrian
32566864Sadrian	dblk = fsbtodb(&sblock, blk);
326247212Smckusick	if (bp->b_bno == dblk) {
327247212Smckusick		totalreads++;
328247212Smckusick	} else {
32966864Sadrian		flush(fswritefd, bp);
330247212Smckusick		if (debug) {
331247212Smckusick			readcnt[bp->b_type]++;
332247212Smckusick			clock_gettime(CLOCK_REALTIME_PRECISE, &start);
333247212Smckusick		}
334163845Spjd		bp->b_errs = blread(fsreadfd, bp->b_un.b_buf, dblk, size);
335247212Smckusick		if (debug) {
336247212Smckusick			clock_gettime(CLOCK_REALTIME_PRECISE, &finish);
337247212Smckusick			timespecsub(&finish, &start);
338247212Smckusick			timespecadd(&readtime[bp->b_type], &finish);
339247212Smckusick		}
34066864Sadrian		bp->b_bno = dblk;
34166864Sadrian		bp->b_size = size;
34266864Sadrian	}
34366864Sadrian}
34466864Sadrian
34566864Sadrianvoid
34692839Simpflush(int fd, struct bufarea *bp)
34766864Sadrian{
34892806Sobrien	int i, j;
34966864Sadrian
35066864Sadrian	if (!bp->b_dirty)
35166864Sadrian		return;
35274556Smckusick	bp->b_dirty = 0;
35374556Smckusick	if (fswritefd < 0) {
35474556Smckusick		pfatal("WRITING IN READ_ONLY MODE.\n");
35574556Smckusick		return;
35674556Smckusick	}
35766864Sadrian	if (bp->b_errs != 0)
358100935Sphk		pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
35966864Sadrian		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
360100935Sphk		    (long long)bp->b_bno);
36166864Sadrian	bp->b_errs = 0;
362240406Sobrien	blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size);
36366864Sadrian	if (bp != &sblk)
36466864Sadrian		return;
36566864Sadrian	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
366163845Spjd		blwrite(fswritefd, (char *)sblock.fs_csp + i,
36766864Sadrian		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
36866864Sadrian		    sblock.fs_cssize - i < sblock.fs_bsize ?
36966864Sadrian		    sblock.fs_cssize - i : sblock.fs_bsize);
37066864Sadrian	}
37166864Sadrian}
37266864Sadrian
37374556Smckusickvoid
374100935Sphkrwerror(const char *mesg, ufs2_daddr_t blk)
37566864Sadrian{
37666864Sadrian
37775927Smckusick	if (bkgrdcheck)
37875927Smckusick		exit(EEXIT);
37966864Sadrian	if (preen == 0)
38066864Sadrian		printf("\n");
38186514Siedowse	pfatal("CANNOT %s: %ld", mesg, (long)blk);
38266864Sadrian	if (reply("CONTINUE") == 0)
38366864Sadrian		exit(EEXIT);
38466864Sadrian}
38566864Sadrian
38666864Sadrianvoid
38792839Simpckfini(int markclean)
38866864Sadrian{
38992806Sobrien	struct bufarea *bp, *nbp;
390246812Smckusick	int ofsmodified, cnt;
39166864Sadrian
39274556Smckusick	if (bkgrdflag) {
39374556Smckusick		unlink(snapname);
39474556Smckusick		if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) {
39574556Smckusick			cmd.value = FS_UNCLEAN;
39674556Smckusick			cmd.size = markclean ? -1 : 1;
39774556Smckusick			if (sysctlbyname("vfs.ffs.setflags", 0, 0,
39874556Smckusick			    &cmd, sizeof cmd) == -1)
399118302Sru				rwerror("SET FILE SYSTEM FLAGS", FS_UNCLEAN);
40074556Smckusick			if (!preen) {
40174556Smckusick				printf("\n***** FILE SYSTEM MARKED %s *****\n",
40274556Smckusick				    markclean ? "CLEAN" : "DIRTY");
40374556Smckusick				if (!markclean)
40474556Smckusick					rerun = 1;
40574556Smckusick			}
40674556Smckusick		} else if (!preen && !markclean) {
40774556Smckusick			printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
40874556Smckusick			rerun = 1;
40974556Smckusick		}
41074556Smckusick	}
411246812Smckusick	if (debug && totalreads > 0)
412246812Smckusick		printf("cache with %d buffers missed %ld of %ld (%d%%)\n",
413247212Smckusick		    numbufs, totaldiskreads, totalreads,
414247212Smckusick		    (int)(totaldiskreads * 100 / totalreads));
41566864Sadrian	if (fswritefd < 0) {
41666864Sadrian		(void)close(fsreadfd);
41766864Sadrian		return;
41866864Sadrian	}
41966864Sadrian	flush(fswritefd, &sblk);
42098542Smckusick	if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
421107294Smckusick	    sblk.b_bno != sblock.fs_sblockloc / dev_bsize &&
42266864Sadrian	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
423107294Smckusick		sblk.b_bno = sblock.fs_sblockloc / dev_bsize;
42466864Sadrian		sbdirty();
42566864Sadrian		flush(fswritefd, &sblk);
42666864Sadrian	}
42766864Sadrian	flush(fswritefd, &cgblk);
42866864Sadrian	free(cgblk.b_un.b_buf);
429246812Smckusick	cnt = 0;
430246812Smckusick	TAILQ_FOREACH_REVERSE_SAFE(bp, &bufhead, buflist, b_list, nbp) {
431246812Smckusick		TAILQ_REMOVE(&bufhead, bp, b_list);
43266864Sadrian		cnt++;
43366864Sadrian		flush(fswritefd, bp);
43466864Sadrian		free(bp->b_un.b_buf);
43566864Sadrian		free((char *)bp);
43666864Sadrian	}
437246812Smckusick	if (numbufs != cnt)
438246812Smckusick		errx(EEXIT, "panic: lost %d buffers", numbufs - cnt);
439263629Smckusick	if (cgbufs != NULL) {
440263629Smckusick		for (cnt = 0; cnt < sblock.fs_ncg; cnt++) {
441263629Smckusick			if (cgbufs[cnt].b_un.b_cg == NULL)
442263629Smckusick				continue;
443263629Smckusick			flush(fswritefd, &cgbufs[cnt]);
444263629Smckusick			free(cgbufs[cnt].b_un.b_cg);
445263629Smckusick		}
446263629Smckusick		free(cgbufs);
447248658Smckusick	}
44866864Sadrian	pbp = pdirbp = (struct bufarea *)0;
44974556Smckusick	if (cursnapshot == 0 && sblock.fs_clean != markclean) {
450105436Smckusick		if ((sblock.fs_clean = markclean) != 0) {
45175557Smckusick			sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK);
452105436Smckusick			sblock.fs_pendingblocks = 0;
453105436Smckusick			sblock.fs_pendinginodes = 0;
454105436Smckusick		}
45566864Sadrian		sbdirty();
45666864Sadrian		ofsmodified = fsmodified;
45766864Sadrian		flush(fswritefd, &sblk);
45866864Sadrian		fsmodified = ofsmodified;
45966864Sadrian		if (!preen) {
46066864Sadrian			printf("\n***** FILE SYSTEM MARKED %s *****\n",
46166864Sadrian			    markclean ? "CLEAN" : "DIRTY");
46266864Sadrian			if (!markclean)
46366864Sadrian				rerun = 1;
46466864Sadrian		}
465188110Smckusick	} else if (!preen) {
466188110Smckusick		if (markclean) {
467188110Smckusick			printf("\n***** FILE SYSTEM IS CLEAN *****\n");
468188110Smckusick		} else {
469188110Smckusick			printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
470188110Smckusick			rerun = 1;
471188110Smckusick		}
47266864Sadrian	}
47366864Sadrian	(void)close(fsreadfd);
47466864Sadrian	(void)close(fswritefd);
47566864Sadrian}
47666864Sadrian
477247212Smckusick/*
478247212Smckusick * Print out I/O statistics.
479247212Smckusick */
480247212Smckusickvoid
481247212SmckusickIOstats(char *what)
482247212Smckusick{
483247212Smckusick	int i;
484247212Smckusick
485247212Smckusick	if (debug == 0)
486247212Smckusick		return;
487247212Smckusick	if (diskreads == 0) {
488247212Smckusick		printf("%s: no I/O\n\n", what);
489247212Smckusick		return;
490247212Smckusick	}
491247212Smckusick	if (startpass.tv_sec == 0)
492247212Smckusick		startpass = startprog;
493247212Smckusick	printf("%s: I/O statistics\n", what);
494247212Smckusick	printIOstats();
495247212Smckusick	totaldiskreads += diskreads;
496247212Smckusick	diskreads = 0;
497247212Smckusick	for (i = 0; i < BT_NUMBUFTYPES; i++) {
498247212Smckusick		timespecadd(&totalreadtime[i], &readtime[i]);
499247212Smckusick		totalreadcnt[i] += readcnt[i];
500247212Smckusick		readtime[i].tv_sec = readtime[i].tv_nsec = 0;
501247212Smckusick		readcnt[i] = 0;
502247212Smckusick	}
503247212Smckusick	clock_gettime(CLOCK_REALTIME_PRECISE, &startpass);
504247212Smckusick}
505247212Smckusick
506247212Smckusickvoid
507247212SmckusickfinalIOstats(void)
508247212Smckusick{
509247212Smckusick	int i;
510247212Smckusick
511247212Smckusick	if (debug == 0)
512247212Smckusick		return;
513247212Smckusick	printf("Final I/O statistics\n");
514247212Smckusick	totaldiskreads += diskreads;
515247212Smckusick	diskreads = totaldiskreads;
516247212Smckusick	startpass = startprog;
517247212Smckusick	for (i = 0; i < BT_NUMBUFTYPES; i++) {
518247212Smckusick		timespecadd(&totalreadtime[i], &readtime[i]);
519247212Smckusick		totalreadcnt[i] += readcnt[i];
520247212Smckusick		readtime[i] = totalreadtime[i];
521247212Smckusick		readcnt[i] = totalreadcnt[i];
522247212Smckusick	}
523247212Smckusick	printIOstats();
524247212Smckusick}
525247212Smckusick
526247212Smckusickstatic void printIOstats(void)
527247212Smckusick{
528247212Smckusick	long long msec, totalmsec;
529247212Smckusick	int i;
530247212Smckusick
531247212Smckusick	clock_gettime(CLOCK_REALTIME_PRECISE, &finishpass);
532247212Smckusick	timespecsub(&finishpass, &startpass);
533248691Smckusick	printf("Running time: %jd.%03ld sec\n",
534248680Ssbruno		(intmax_t)finishpass.tv_sec, finishpass.tv_nsec / 1000000);
535247212Smckusick	printf("buffer reads by type:\n");
536247212Smckusick	for (totalmsec = 0, i = 0; i < BT_NUMBUFTYPES; i++)
537247212Smckusick		totalmsec += readtime[i].tv_sec * 1000 +
538247212Smckusick		    readtime[i].tv_nsec / 1000000;
539247212Smckusick	if (totalmsec == 0)
540247212Smckusick		totalmsec = 1;
541247212Smckusick	for (i = 0; i < BT_NUMBUFTYPES; i++) {
542247212Smckusick		if (readcnt[i] == 0)
543247212Smckusick			continue;
544248691Smckusick		msec =
545248691Smckusick		    readtime[i].tv_sec * 1000 + readtime[i].tv_nsec / 1000000;
546248680Ssbruno		printf("%21s:%8ld %2ld.%ld%% %4jd.%03ld sec %2lld.%lld%%\n",
547247212Smckusick		    buftype[i], readcnt[i], readcnt[i] * 100 / diskreads,
548248658Smckusick		    (readcnt[i] * 1000 / diskreads) % 10,
549248680Ssbruno		    (intmax_t)readtime[i].tv_sec, readtime[i].tv_nsec / 1000000,
550247212Smckusick		    msec * 100 / totalmsec, (msec * 1000 / totalmsec) % 10);
551247212Smckusick	}
552247212Smckusick	printf("\n");
553247212Smckusick}
554247212Smckusick
55566864Sadrianint
556163845Spjdblread(int fd, char *buf, ufs2_daddr_t blk, long size)
55766864Sadrian{
55866864Sadrian	char *cp;
55966864Sadrian	int i, errs;
56066864Sadrian	off_t offset;
56166864Sadrian
56266864Sadrian	offset = blk;
56366864Sadrian	offset *= dev_bsize;
564107671Siedowse	if (bkgrdflag)
565107671Siedowse		slowio_start();
566247212Smckusick	totalreads++;
567247212Smckusick	diskreads++;
56866864Sadrian	if (lseek(fd, offset, 0) < 0)
56974556Smckusick		rwerror("SEEK BLK", blk);
570107671Siedowse	else if (read(fd, buf, (int)size) == size) {
571107671Siedowse		if (bkgrdflag)
572107671Siedowse			slowio_end();
57366864Sadrian		return (0);
574107671Siedowse	}
575253822Sscottl
576253822Sscottl	/*
577253822Sscottl	 * This is handled specially here instead of in rwerror because
578253822Sscottl	 * rwerror is used for all sorts of errors, not just true read/write
579253822Sscottl	 * errors.  It should be refactored and fixed.
580253822Sscottl	 */
581253822Sscottl	if (surrender) {
582253822Sscottl		pfatal("CANNOT READ_BLK: %ld", (long)blk);
583253822Sscottl		errx(EEXIT, "ABORTING DUE TO READ ERRORS");
584253822Sscottl	} else
585253822Sscottl		rwerror("READ BLK", blk);
586253822Sscottl
58766864Sadrian	if (lseek(fd, offset, 0) < 0)
58874556Smckusick		rwerror("SEEK BLK", blk);
58966864Sadrian	errs = 0;
59066864Sadrian	memset(buf, 0, (size_t)size);
59166864Sadrian	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
59266864Sadrian	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
59366864Sadrian		if (read(fd, cp, (int)secsize) != secsize) {
59466864Sadrian			(void)lseek(fd, offset + i + secsize, 0);
59566864Sadrian			if (secsize != dev_bsize && dev_bsize != 1)
596101037Smux				printf(" %jd (%jd),",
597101037Smux				    (intmax_t)(blk * dev_bsize + i) / secsize,
598101037Smux				    (intmax_t)blk + i / dev_bsize);
59966864Sadrian			else
600101037Smux				printf(" %jd,", (intmax_t)blk + i / dev_bsize);
60166864Sadrian			errs++;
60266864Sadrian		}
60366864Sadrian	}
60466864Sadrian	printf("\n");
60566864Sadrian	if (errs)
60666864Sadrian		resolved = 0;
60766864Sadrian	return (errs);
60866864Sadrian}
60966864Sadrian
61066864Sadrianvoid
611240406Sobrienblwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size)
61266864Sadrian{
61366864Sadrian	int i;
61466864Sadrian	char *cp;
61566864Sadrian	off_t offset;
61666864Sadrian
61766864Sadrian	if (fd < 0)
61866864Sadrian		return;
61966864Sadrian	offset = blk;
62066864Sadrian	offset *= dev_bsize;
62166864Sadrian	if (lseek(fd, offset, 0) < 0)
62274556Smckusick		rwerror("SEEK BLK", blk);
623240406Sobrien	else if (write(fd, buf, size) == size) {
62466864Sadrian		fsmodified = 1;
62566864Sadrian		return;
62666864Sadrian	}
62766864Sadrian	resolved = 0;
62874556Smckusick	rwerror("WRITE BLK", blk);
62966864Sadrian	if (lseek(fd, offset, 0) < 0)
63074556Smckusick		rwerror("SEEK BLK", blk);
63166864Sadrian	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
63266864Sadrian	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
633240406Sobrien		if (write(fd, cp, dev_bsize) != dev_bsize) {
63466864Sadrian			(void)lseek(fd, offset + i + dev_bsize, 0);
635101037Smux			printf(" %jd,", (intmax_t)blk + i / dev_bsize);
63666864Sadrian		}
63766864Sadrian	printf("\n");
63866864Sadrian	return;
63966864Sadrian}
64066864Sadrian
641221233Sdesvoid
642221233Sdesblerase(int fd, ufs2_daddr_t blk, long size)
643221233Sdes{
644221233Sdes	off_t ioarg[2];
645221233Sdes
646221233Sdes	if (fd < 0)
647221233Sdes		return;
648221233Sdes	ioarg[0] = blk * dev_bsize;
649221233Sdes	ioarg[1] = size;
650221233Sdes	ioctl(fd, DIOCGDELETE, ioarg);
651221233Sdes	/* we don't really care if we succeed or not */
652221233Sdes	return;
653221233Sdes}
654221233Sdes
655254553Sdes/*
656254553Sdes * Fill a contiguous region with all-zeroes.  Note ZEROBUFSIZE is by
657254553Sdes * definition a multiple of dev_bsize.
658254553Sdes */
659250056Sdesvoid
660250056Sdesblzero(int fd, ufs2_daddr_t blk, long size)
661250056Sdes{
662250056Sdes	static char *zero;
663250056Sdes	off_t offset, len;
664250056Sdes
665250056Sdes	if (fd < 0)
666250056Sdes		return;
667250056Sdes	if (zero == NULL) {
668254553Sdes		zero = calloc(ZEROBUFSIZE, 1);
669250056Sdes		if (zero == NULL)
670250056Sdes			errx(EEXIT, "cannot allocate buffer pool");
671250056Sdes	}
672250056Sdes	offset = blk * dev_bsize;
673250056Sdes	if (lseek(fd, offset, 0) < 0)
674250056Sdes		rwerror("SEEK BLK", blk);
675250056Sdes	while (size > 0) {
676254553Sdes		len = size > ZEROBUFSIZE ? ZEROBUFSIZE : size;
677250056Sdes		if (write(fd, zero, len) != len)
678250056Sdes			rwerror("WRITE BLK", blk);
679250056Sdes		blk += len / dev_bsize;
680250056Sdes		size -= len;
681250056Sdes	}
682250056Sdes}
683250056Sdes
68466864Sadrian/*
685188110Smckusick * Verify cylinder group's magic number and other parameters.  If the
686188110Smckusick * test fails, offer an option to rebuild the whole cylinder group.
687178088Sdelphij */
688188110Smckusickint
689248658Smckusickcheck_cgmagic(int cg, struct bufarea *cgbp)
690178088Sdelphij{
691248658Smckusick	struct cg *cgp = cgbp->b_un.b_cg;
692178088Sdelphij
693188110Smckusick	/*
694188110Smckusick	 * Extended cylinder group checks.
695188110Smckusick	 */
696188110Smckusick	if (cg_chkmagic(cgp) &&
697188110Smckusick	    ((sblock.fs_magic == FS_UFS1_MAGIC &&
698188110Smckusick	      cgp->cg_old_niblk == sblock.fs_ipg &&
699188110Smckusick	      cgp->cg_ndblk <= sblock.fs_fpg &&
700201700Smckusick	      cgp->cg_old_ncyl <= sblock.fs_old_cpg) ||
701188110Smckusick	     (sblock.fs_magic == FS_UFS2_MAGIC &&
702188110Smckusick	      cgp->cg_niblk == sblock.fs_ipg &&
703188110Smckusick	      cgp->cg_ndblk <= sblock.fs_fpg &&
704188110Smckusick	      cgp->cg_initediblk <= sblock.fs_ipg))) {
705188110Smckusick		return (1);
706178088Sdelphij	}
707188110Smckusick	pfatal("CYLINDER GROUP %d: BAD MAGIC NUMBER", cg);
708188110Smckusick	if (!reply("REBUILD CYLINDER GROUP")) {
709188110Smckusick		printf("YOU WILL NEED TO RERUN FSCK.\n");
710188110Smckusick		rerun = 1;
711188110Smckusick		return (1);
712188110Smckusick	}
713188110Smckusick	/*
714188110Smckusick	 * Zero out the cylinder group and then initialize critical fields.
715188110Smckusick	 * Bit maps and summaries will be recalculated by later passes.
716188110Smckusick	 */
717188110Smckusick	memset(cgp, 0, (size_t)sblock.fs_cgsize);
718188110Smckusick	cgp->cg_magic = CG_MAGIC;
719188110Smckusick	cgp->cg_cgx = cg;
720188110Smckusick	cgp->cg_niblk = sblock.fs_ipg;
721188110Smckusick	cgp->cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ?
722188110Smckusick	    sblock.fs_ipg : 2 * INOPB(&sblock);
723188110Smckusick	if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size)
724188110Smckusick		cgp->cg_ndblk = sblock.fs_fpg;
725188110Smckusick	else
726188110Smckusick		cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg);
727188110Smckusick	cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield);
728188110Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC) {
729188110Smckusick		cgp->cg_niblk = 0;
730188110Smckusick		cgp->cg_initediblk = 0;
731188110Smckusick		cgp->cg_old_ncyl = sblock.fs_old_cpg;
732188110Smckusick		cgp->cg_old_niblk = sblock.fs_ipg;
733188110Smckusick		cgp->cg_old_btotoff = cgp->cg_iusedoff;
734188110Smckusick		cgp->cg_old_boff = cgp->cg_old_btotoff +
735188110Smckusick		    sblock.fs_old_cpg * sizeof(int32_t);
736188110Smckusick		cgp->cg_iusedoff = cgp->cg_old_boff +
737188110Smckusick		    sblock.fs_old_cpg * sizeof(u_int16_t);
738188110Smckusick	}
739188110Smckusick	cgp->cg_freeoff = cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
740188110Smckusick	cgp->cg_nextfreeoff = cgp->cg_freeoff + howmany(sblock.fs_fpg,CHAR_BIT);
741188110Smckusick	if (sblock.fs_contigsumsize > 0) {
742188110Smckusick		cgp->cg_nclusterblks = cgp->cg_ndblk / sblock.fs_frag;
743188110Smckusick		cgp->cg_clustersumoff =
744188110Smckusick		    roundup(cgp->cg_nextfreeoff, sizeof(u_int32_t));
745188110Smckusick		cgp->cg_clustersumoff -= sizeof(u_int32_t);
746188110Smckusick		cgp->cg_clusteroff = cgp->cg_clustersumoff +
747188110Smckusick		    (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
748188110Smckusick		cgp->cg_nextfreeoff = cgp->cg_clusteroff +
749188110Smckusick		    howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
750188110Smckusick	}
751248658Smckusick	dirty(cgbp);
752188110Smckusick	return (0);
753178088Sdelphij}
754178088Sdelphij
755178088Sdelphij/*
75666864Sadrian * allocate a data block with the specified number of fragments
75766864Sadrian */
75898542Smckusickufs2_daddr_t
75992839Simpallocblk(long frags)
76066864Sadrian{
76166864Sadrian	int i, j, k, cg, baseblk;
762248658Smckusick	struct bufarea *cgbp;
763248658Smckusick	struct cg *cgp;
76466864Sadrian
76566864Sadrian	if (frags <= 0 || frags > sblock.fs_frag)
76666864Sadrian		return (0);
76766864Sadrian	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
76866864Sadrian		for (j = 0; j <= sblock.fs_frag - frags; j++) {
76966864Sadrian			if (testbmap(i + j))
77066864Sadrian				continue;
77166864Sadrian			for (k = 1; k < frags; k++)
77266864Sadrian				if (testbmap(i + j + k))
77366864Sadrian					break;
77466864Sadrian			if (k < frags) {
77566864Sadrian				j += k;
77666864Sadrian				continue;
77766864Sadrian			}
77866864Sadrian			cg = dtog(&sblock, i + j);
779248658Smckusick			cgbp = cgget(cg);
780248658Smckusick			cgp = cgbp->b_un.b_cg;
781248658Smckusick			if (!check_cgmagic(cg, cgbp))
782188110Smckusick				return (0);
78366864Sadrian			baseblk = dtogd(&sblock, i + j);
78466864Sadrian			for (k = 0; k < frags; k++) {
78566864Sadrian				setbmap(i + j + k);
78666864Sadrian				clrbit(cg_blksfree(cgp), baseblk + k);
78766864Sadrian			}
78866864Sadrian			n_blks += frags;
78966864Sadrian			if (frags == sblock.fs_frag)
79066864Sadrian				cgp->cg_cs.cs_nbfree--;
79166864Sadrian			else
79266864Sadrian				cgp->cg_cs.cs_nffree -= frags;
793248658Smckusick			dirty(cgbp);
79466864Sadrian			return (i + j);
79566864Sadrian		}
79666864Sadrian	}
79766864Sadrian	return (0);
79866864Sadrian}
79966864Sadrian
80066864Sadrian/*
80166864Sadrian * Free a previously allocated block
80266864Sadrian */
80366864Sadrianvoid
80498542Smckusickfreeblk(ufs2_daddr_t blkno, long frags)
80566864Sadrian{
80666864Sadrian	struct inodesc idesc;
80766864Sadrian
80866864Sadrian	idesc.id_blkno = blkno;
80966864Sadrian	idesc.id_numfrags = frags;
81066864Sadrian	(void)pass4check(&idesc);
81166864Sadrian}
81266864Sadrian
813107671Siedowse/* Slow down IO so as to leave some disk bandwidth for other processes */
814107671Siedowsevoid
815107671Siedowseslowio_start()
816107671Siedowse{
817107671Siedowse
818129401Sscottl	/* Delay one in every 8 operations */
819107671Siedowse	slowio_pollcnt = (slowio_pollcnt + 1) & 7;
820107671Siedowse	if (slowio_pollcnt == 0) {
821107671Siedowse		gettimeofday(&slowio_starttime, NULL);
822107671Siedowse	}
823107671Siedowse}
824107671Siedowse
825107671Siedowsevoid
826107671Siedowseslowio_end()
827107671Siedowse{
828107671Siedowse	struct timeval tv;
829107671Siedowse	int delay_usec;
830107671Siedowse
831107671Siedowse	if (slowio_pollcnt != 0)
832107671Siedowse		return;
833107671Siedowse
834107671Siedowse	/* Update the slowdown interval. */
835107671Siedowse	gettimeofday(&tv, NULL);
836107671Siedowse	delay_usec = (tv.tv_sec - slowio_starttime.tv_sec) * 1000000 +
837107671Siedowse	    (tv.tv_usec - slowio_starttime.tv_usec);
838107671Siedowse	if (delay_usec < 64)
839107671Siedowse		delay_usec = 64;
840129401Sscottl	if (delay_usec > 2500000)
841129401Sscottl		delay_usec = 2500000;
842107671Siedowse	slowio_delay_usec = (slowio_delay_usec * 63 + delay_usec) >> 6;
843129401Sscottl	/* delay by 8 times the average IO delay */
844129401Sscottl	if (slowio_delay_usec > 64)
845129401Sscottl		usleep(slowio_delay_usec * 8);
846107671Siedowse}
847107671Siedowse
84866864Sadrian/*
84966864Sadrian * Find a pathname
85066864Sadrian */
85166864Sadrianvoid
85292839Simpgetpathname(char *namebuf, ino_t curdir, ino_t ino)
85366864Sadrian{
85466864Sadrian	int len;
85592806Sobrien	char *cp;
85666864Sadrian	struct inodesc idesc;
85766864Sadrian	static int busy = 0;
85866864Sadrian
85966864Sadrian	if (curdir == ino && ino == ROOTINO) {
86066864Sadrian		(void)strcpy(namebuf, "/");
86166864Sadrian		return;
86266864Sadrian	}
863136281Struckman	if (busy || !INO_IS_DVALID(curdir)) {
86466864Sadrian		(void)strcpy(namebuf, "?");
86566864Sadrian		return;
86666864Sadrian	}
86766864Sadrian	busy = 1;
86866864Sadrian	memset(&idesc, 0, sizeof(struct inodesc));
86966864Sadrian	idesc.id_type = DATA;
87066864Sadrian	idesc.id_fix = IGNORE;
87166864Sadrian	cp = &namebuf[MAXPATHLEN - 1];
87266864Sadrian	*cp = '\0';
87366864Sadrian	if (curdir != ino) {
87466864Sadrian		idesc.id_parent = curdir;
87566864Sadrian		goto namelookup;
87666864Sadrian	}
87766864Sadrian	while (ino != ROOTINO) {
87866864Sadrian		idesc.id_number = ino;
87966864Sadrian		idesc.id_func = findino;
880100935Sphk		idesc.id_name = strdup("..");
88166864Sadrian		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
88266864Sadrian			break;
88366864Sadrian	namelookup:
88466864Sadrian		idesc.id_number = idesc.id_parent;
88566864Sadrian		idesc.id_parent = ino;
88666864Sadrian		idesc.id_func = findname;
88766864Sadrian		idesc.id_name = namebuf;
88866864Sadrian		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
88966864Sadrian			break;
89066864Sadrian		len = strlen(namebuf);
89166864Sadrian		cp -= len;
89266864Sadrian		memmove(cp, namebuf, (size_t)len);
89366864Sadrian		*--cp = '/';
89466864Sadrian		if (cp < &namebuf[MAXNAMLEN])
89566864Sadrian			break;
89666864Sadrian		ino = idesc.id_number;
89766864Sadrian	}
89866864Sadrian	busy = 0;
89966864Sadrian	if (ino != ROOTINO)
90066864Sadrian		*--cp = '?';
90166864Sadrian	memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
90266864Sadrian}
90366864Sadrian
90466864Sadrianvoid
905100935Sphkcatch(int sig __unused)
90666864Sadrian{
90796483Sphk
90896483Sphk	ckfini(0);
90966864Sadrian	exit(12);
91066864Sadrian}
91166864Sadrian
91266864Sadrian/*
91366864Sadrian * When preening, allow a single quit to signal
914102231Strhodes * a special exit after file system checks complete
91566864Sadrian * so that reboot sequence may be interrupted.
91666864Sadrian */
91766864Sadrianvoid
918100935Sphkcatchquit(int sig __unused)
91966864Sadrian{
920102231Strhodes	printf("returning to single-user after file system check\n");
92166864Sadrian	returntosingle = 1;
92266864Sadrian	(void)signal(SIGQUIT, SIG_DFL);
92366864Sadrian}
92466864Sadrian
92566864Sadrian/*
92666864Sadrian * determine whether an inode should be fixed.
92766864Sadrian */
92866864Sadrianint
929100935Sphkdofix(struct inodesc *idesc, const char *msg)
93066864Sadrian{
93166864Sadrian
93266864Sadrian	switch (idesc->id_fix) {
93366864Sadrian
93466864Sadrian	case DONTKNOW:
93566864Sadrian		if (idesc->id_type == DATA)
93666864Sadrian			direrror(idesc->id_number, msg);
93766864Sadrian		else
93881911Skris			pwarn("%s", msg);
93966864Sadrian		if (preen) {
94066864Sadrian			printf(" (SALVAGED)\n");
94166864Sadrian			idesc->id_fix = FIX;
94266864Sadrian			return (ALTERED);
94366864Sadrian		}
94466864Sadrian		if (reply("SALVAGE") == 0) {
94566864Sadrian			idesc->id_fix = NOFIX;
94666864Sadrian			return (0);
94766864Sadrian		}
94866864Sadrian		idesc->id_fix = FIX;
94966864Sadrian		return (ALTERED);
95066864Sadrian
95166864Sadrian	case FIX:
95266864Sadrian		return (ALTERED);
95366864Sadrian
95466864Sadrian	case NOFIX:
95566864Sadrian	case IGNORE:
95666864Sadrian		return (0);
95766864Sadrian
95866864Sadrian	default:
95966864Sadrian		errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
96066864Sadrian	}
96166864Sadrian	/* NOTREACHED */
96266864Sadrian	return (0);
96366864Sadrian}
96466864Sadrian
96566864Sadrian#include <stdarg.h>
96666864Sadrian
96766864Sadrian/*
968229778Suqs * An unexpected inconsistency occurred.
969102231Strhodes * Die if preening or file system is running with soft dependency protocol,
97066864Sadrian * otherwise just print message and continue.
97166864Sadrian */
97266864Sadrianvoid
97366864Sadrianpfatal(const char *fmt, ...)
97466864Sadrian{
97566864Sadrian	va_list ap;
97666864Sadrian	va_start(ap, fmt);
97766864Sadrian	if (!preen) {
97875927Smckusick		(void)vfprintf(stdout, fmt, ap);
97966864Sadrian		va_end(ap);
98066864Sadrian		if (usedsoftdep)
98175927Smckusick			(void)fprintf(stdout,
98266864Sadrian			    "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n");
98375557Smckusick		/*
98475557Smckusick		 * Force foreground fsck to clean up inconsistency.
98575557Smckusick		 */
98675557Smckusick		if (bkgrdflag) {
98775557Smckusick			cmd.value = FS_NEEDSFSCK;
98875557Smckusick			cmd.size = 1;
98975557Smckusick			if (sysctlbyname("vfs.ffs.setflags", 0, 0,
99075557Smckusick			    &cmd, sizeof cmd) == -1)
99175557Smckusick				pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
99275927Smckusick			fprintf(stdout, "CANNOT RUN IN BACKGROUND\n");
99375557Smckusick			ckfini(0);
99475557Smckusick			exit(EEXIT);
99575557Smckusick		}
99666864Sadrian		return;
99766864Sadrian	}
99866864Sadrian	if (cdevname == NULL)
999100935Sphk		cdevname = strdup("fsck");
100075927Smckusick	(void)fprintf(stdout, "%s: ", cdevname);
100175927Smckusick	(void)vfprintf(stdout, fmt, ap);
100275927Smckusick	(void)fprintf(stdout,
100366864Sadrian	    "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n",
100466864Sadrian	    cdevname, usedsoftdep ? " SOFT UPDATE " : " ");
100575557Smckusick	/*
100675557Smckusick	 * Force foreground fsck to clean up inconsistency.
100775557Smckusick	 */
100875557Smckusick	if (bkgrdflag) {
100975557Smckusick		cmd.value = FS_NEEDSFSCK;
101075557Smckusick		cmd.size = 1;
101175557Smckusick		if (sysctlbyname("vfs.ffs.setflags", 0, 0,
101275557Smckusick		    &cmd, sizeof cmd) == -1)
101375557Smckusick			pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
101475557Smckusick	}
101566864Sadrian	ckfini(0);
101666864Sadrian	exit(EEXIT);
101766864Sadrian}
101866864Sadrian
101966864Sadrian/*
102066864Sadrian * Pwarn just prints a message when not preening or running soft dependency
102166864Sadrian * protocol, or a warning (preceded by filename) when preening.
102266864Sadrian */
102366864Sadrianvoid
102466864Sadrianpwarn(const char *fmt, ...)
102566864Sadrian{
102666864Sadrian	va_list ap;
102766864Sadrian	va_start(ap, fmt);
102866864Sadrian	if (preen)
102975927Smckusick		(void)fprintf(stdout, "%s: ", cdevname);
103075927Smckusick	(void)vfprintf(stdout, fmt, ap);
103166864Sadrian	va_end(ap);
103266864Sadrian}
103366864Sadrian
103466864Sadrian/*
103566864Sadrian * Stub for routines from kernel.
103666864Sadrian */
103766864Sadrianvoid
103866864Sadrianpanic(const char *fmt, ...)
103966864Sadrian{
104066864Sadrian	va_list ap;
104166864Sadrian	va_start(ap, fmt);
104266864Sadrian	pfatal("INTERNAL INCONSISTENCY:");
104375927Smckusick	(void)vfprintf(stdout, fmt, ap);
104466864Sadrian	va_end(ap);
104566864Sadrian	exit(EEXIT);
104666864Sadrian}
1047