1/*	$NetBSD: fsutil.c,v 1.19 2010/02/04 23:55:42 christos Exp $	*/
2
3/*
4 * Copyright (c) 1990, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__RCSID("$NetBSD: fsutil.c,v 1.19 2010/02/04 23:55:42 christos Exp $");
35#endif /* not lint */
36
37#include <sys/param.h>
38
39#include <stdio.h>
40#include <string.h>
41#include <stdlib.h>
42#include <stdarg.h>
43#include <errno.h>
44#include <fstab.h>
45#include <fcntl.h>
46#include <unistd.h>
47#include <err.h>
48
49#include <sys/types.h>
50#include <sys/stat.h>
51
52#include "fsutil.h"
53#include "exitvalues.h"
54
55static const char *dev = NULL;
56static int hot = 0;
57static int preen = 0;
58int quiet;
59#define F_ERROR	0x80000000
60
61void
62setcdevname(const char *cd, int pr)
63{
64
65	dev = cd;
66	preen = pr;
67}
68
69const char *
70cdevname(void)
71{
72
73	return dev;
74}
75
76int
77hotroot(void)
78{
79
80	return hot;
81}
82
83/*VARARGS*/
84void
85errexit(const char *fmt, ...)
86{
87	va_list ap;
88
89	va_start(ap, fmt);
90	(void) vfprintf(stderr, fmt, ap);
91	va_end(ap);
92	(void)fprintf(stderr, "\n");
93	exit(FSCK_EXIT_CHECK_FAILED);
94}
95
96void
97vmsg(int fatal, const char *fmt, va_list ap)
98{
99	int serr = fatal & F_ERROR;
100	int serrno = errno;
101	fatal &= ~F_ERROR;
102
103	if (!fatal && preen)
104		(void)printf("%s: ", dev);
105	if (quiet && !preen) {
106		(void)printf("** %s (vmsg)\n", dev);
107		quiet = 0;
108	}
109
110	(void) vprintf(fmt, ap);
111	if (serr)
112		printf(" (%s)", strerror(serrno));
113
114	if (fatal && preen)
115		(void) printf("\n");
116
117	if (fatal && preen) {
118		(void) printf(
119		    "%s: UNEXPECTED INCONSISTENCY; RUN %s MANUALLY.\n",
120		    dev, getprogname());
121		exit(FSCK_EXIT_CHECK_FAILED);
122	}
123}
124
125/*VARARGS*/
126void
127pfatal(const char *fmt, ...)
128{
129	va_list ap;
130
131	va_start(ap, fmt);
132	vmsg(1, fmt, ap);
133	va_end(ap);
134}
135
136/*VARARGS*/
137void
138pwarn(const char *fmt, ...)
139{
140	va_list ap;
141
142	va_start(ap, fmt);
143	vmsg(0, fmt, ap);
144	va_end(ap);
145}
146
147void
148perr(const char *fmt, ...)
149{
150	va_list ap;
151
152	va_start(ap, fmt);
153	vmsg(1 | F_ERROR, fmt, ap);
154	va_end(ap);
155}
156
157void
158panic(const char *fmt, ...)
159{
160	va_list ap;
161
162	va_start(ap, fmt);
163	vmsg(1, fmt, ap);
164	va_end(ap);
165	exit(FSCK_EXIT_CHECK_FAILED);
166}
167
168const char *
169unrawname(const char *name)
170{
171	static char unrawbuf[MAXPATHLEN];
172	const char *dp;
173	struct stat stb;
174
175	if ((dp = strrchr(name, '/')) == 0)
176		return (name);
177	if (stat(name, &stb) < 0)
178		return (name);
179	if (!S_ISCHR(stb.st_mode))
180		return (name);
181	if (dp[1] != 'r')
182		return (name);
183	(void)snprintf(unrawbuf, sizeof(unrawbuf), "%.*s/%s",
184	    (int)(dp - name), name, dp + 2);
185	return (unrawbuf);
186}
187
188const char *
189rawname(const char *name)
190{
191	static char rawbuf[MAXPATHLEN];
192	const char *dp;
193
194	if ((dp = strrchr(name, '/')) == 0)
195		return (0);
196	(void)snprintf(rawbuf, sizeof(rawbuf), "%.*s/r%s",
197	    (int)(dp - name), name, dp + 1);
198	return (rawbuf);
199}
200
201const char *
202blockcheck(const char *origname)
203{
204	struct stat stslash, stblock, stchar;
205	const char *newname, *raw;
206	struct fstab *fsp;
207	int retried = 0;
208
209	hot = 0;
210	if (stat("/", &stslash) < 0) {
211		perr("Can't stat `/'");
212		return (origname);
213	}
214	newname = origname;
215retry:
216	if (stat(newname, &stblock) < 0) {
217		perr("Can't stat `%s'", newname);
218		return (origname);
219	}
220	if (S_ISBLK(stblock.st_mode)) {
221		if (stslash.st_dev == stblock.st_rdev)
222			hot++;
223		raw = rawname(newname);
224		if (stat(raw, &stchar) < 0) {
225			perr("Can't stat `%s'", raw);
226			return (origname);
227		}
228		if (S_ISCHR(stchar.st_mode)) {
229			return (raw);
230		} else {
231			printf("%s is not a character device\n", raw);
232			return (origname);
233		}
234	} else if (S_ISCHR(stblock.st_mode) && !retried) {
235		newname = unrawname(newname);
236		retried++;
237		goto retry;
238	} else if ((fsp = getfsfile(newname)) != 0 && !retried) {
239		newname = fsp->fs_spec;
240		retried++;
241		goto retry;
242	}
243	/*
244	 * Not a block or character device, just return name and
245	 * let the user decide whether to use it.
246	 */
247	return (origname);
248}
249
250const char *
251print_mtime(time_t t)
252{
253	static char b[128];
254	char *p = ctime(&t);
255	if (p != NULL)
256		(void)snprintf(b, sizeof(b), "%12.12s %4.4s ", &p[4], &p[20]);
257	else
258		(void)snprintf(b, sizeof(b), "%lld ", (long long)t);
259	return b;
260}
261
262
263void
264catch(int n)
265{
266	if (ckfinish) (*ckfinish)(0);
267	_exit(FSCK_EXIT_SIGNALLED);
268}
269
270/*
271 * When preening, allow a single quit to signal
272 * a special exit after filesystem checks complete
273 * so that reboot sequence may be interrupted.
274 */
275void
276catchquit(int n)
277{
278	static const char msg[] =
279	    "returning to single-user after filesystem check\n";
280	int serrno = errno;
281
282	(void)write(STDOUT_FILENO, msg, sizeof(msg) - 1);
283	returntosingle = 1;
284	(void)signal(SIGQUIT, SIG_DFL);
285	errno = serrno;
286}
287
288/*
289 * Ignore a single quit signal; wait and flush just in case.
290 * Used by child processes in preen.
291 */
292void
293voidquit(int n)
294{
295	int serrno = errno;
296
297	sleep(1);
298	(void)signal(SIGQUIT, SIG_IGN);
299	(void)signal(SIGQUIT, SIG_DFL);
300	errno = serrno;
301}
302