1/*	$NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Copyright (c) 1990, 1993
7 *	The Regents of the University of California.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/* $NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $ */
35
36#include <sys/param.h>
37#include <sys/stat.h>
38#include <sys/wait.h>
39#include <sys/queue.h>
40
41#include <err.h>
42#include <ctype.h>
43#include <fstab.h>
44#include <string.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <unistd.h>
48
49#include "fsutil.h"
50
51struct partentry {
52	TAILQ_ENTRY(partentry)	 p_entries;
53	char		  	*p_devname;	/* device name */
54	char			*p_mntpt;	/* mount point */
55	char		  	*p_type;	/* file system type */
56	int			 p_failok;	/* failok option set */
57};
58
59static TAILQ_HEAD(part, partentry) badh;
60
61struct diskentry {
62	TAILQ_ENTRY(diskentry) 	    d_entries;
63	char		       	   *d_name;	/* disk base name */
64	TAILQ_HEAD(prt, partentry)  d_part;	/* list of partitions on disk */
65	int			    d_pid;	/* 0 or pid of fsck proc */
66};
67
68static TAILQ_HEAD(disk, diskentry) diskh;
69
70static int nrun = 0, ndisks = 0;
71
72static struct diskentry *finddisk(const char *);
73static void addpart(const char *, const char *, const char *, const int);
74static int startdisk(struct diskentry *,
75    int (*)(const char *, const char *, const char *, const char *, pid_t *));
76static void printpart(void);
77
78int
79checkfstab(int flags, int (*docheck)(struct fstab *),
80    int (*checkit)(const char *, const char *, const char *, const char *, pid_t *))
81{
82	struct fstab *fs;
83	struct diskentry *d, *nextdisk;
84	struct partentry *p;
85	int ret, pid, retcode, passno, sumstatus, status, nextpass;
86	const char *name;
87
88	TAILQ_INIT(&badh);
89	TAILQ_INIT(&diskh);
90
91	sumstatus = 0;
92
93	nextpass = 0;
94	for (passno = 1; nextpass != INT_MAX; passno = nextpass) {
95		if (flags & CHECK_DEBUG)
96			printf("pass %d\n", passno);
97
98		nextpass = INT_MAX;
99		if (setfsent() == 0) {
100			warnx("Can't open checklist file: %s\n", _PATH_FSTAB);
101			return (8);
102		}
103		while ((fs = getfsent()) != NULL) {
104			name = fs->fs_spec;
105			if (fs->fs_passno > passno && fs->fs_passno < nextpass)
106				nextpass = fs->fs_passno;
107
108			if (passno != fs->fs_passno)
109				continue;
110
111			if ((*docheck)(fs) == 0)
112				continue;
113
114			if (flags & CHECK_DEBUG)
115				printf("pass %d, name %s\n", passno, name);
116
117			if ((flags & CHECK_PREEN) == 0 || passno == 1 ||
118			    (flags & DO_BACKGRD) != 0) {
119				if (name == NULL) {
120					if (flags & CHECK_PREEN)
121						return 8;
122					else
123						continue;
124				}
125				sumstatus = (*checkit)(fs->fs_vfstype,
126				    name, fs->fs_file, NULL, NULL);
127				if (sumstatus)
128					return (sumstatus);
129				continue;
130			}
131			if (name == NULL) {
132				(void) fprintf(stderr,
133				    "BAD DISK NAME %s\n", fs->fs_spec);
134				sumstatus |= 8;
135				continue;
136			}
137			addpart(fs->fs_vfstype, name, fs->fs_file,
138			    getfsopt(fs, "failok"));
139		}
140
141		if ((flags & CHECK_PREEN) == 0 || passno == 1 ||
142		    (flags & DO_BACKGRD) != 0)
143			continue;
144
145		if (flags & CHECK_DEBUG) {
146			printf("Parallel start\n");
147			printpart();
148		}
149
150		TAILQ_FOREACH(nextdisk, &diskh, d_entries) {
151			if ((ret = startdisk(nextdisk, checkit)) != 0)
152				return ret;
153		}
154
155		if (flags & CHECK_DEBUG)
156			printf("Parallel wait\n");
157		while ((pid = wait(&status)) != -1) {
158			TAILQ_FOREACH(d, &diskh, d_entries)
159				if (d->d_pid == pid)
160					break;
161
162			if (d == NULL) {
163				warnx("Unknown pid %d\n", pid);
164				continue;
165			}
166
167			p = TAILQ_FIRST(&d->d_part);
168
169			if (WIFEXITED(status) == 0) {
170				retcode = 0;
171			} else if (p->p_failok == 0) {
172				retcode = WEXITSTATUS(status);
173			} else {
174				retcode = 0;
175				fprintf(stderr, "%s: failok SPECIFIED, FSCK "
176				    "ERROR(S) IGNORED\n", p->p_devname);
177			}
178
179			if (flags & (CHECK_DEBUG|CHECK_VERBOSE))
180				(void) printf("done %s: %s (%s) = 0x%x\n",
181				    p->p_type, p->p_devname, p->p_mntpt,
182				    status);
183
184			if (WIFSIGNALED(status)) {
185				(void) fprintf(stderr,
186				    "%s: %s (%s): EXITED WITH SIGNAL %d\n",
187				    p->p_type, p->p_devname, p->p_mntpt,
188				    WTERMSIG(status));
189				retcode = 8;
190			}
191
192			TAILQ_REMOVE(&d->d_part, p, p_entries);
193
194			if (retcode != 0) {
195				TAILQ_INSERT_TAIL(&badh, p, p_entries);
196				sumstatus |= retcode;
197			} else {
198				free(p->p_type);
199				free(p->p_devname);
200				free(p);
201			}
202			d->d_pid = 0;
203			nrun--;
204
205			if (TAILQ_EMPTY(&d->d_part)) {
206				TAILQ_REMOVE(&diskh, d, d_entries);
207				ndisks--;
208			} else {
209				if ((ret = startdisk(d, checkit)) != 0)
210					return ret;
211			}
212		}
213		if (flags & CHECK_DEBUG) {
214			printf("Parallel end\n");
215			printpart();
216		}
217	}
218
219	if (!(flags & CHECK_PREEN))
220			return 0;
221
222	if (sumstatus) {
223		p = TAILQ_FIRST(&badh);
224		if (p == NULL)
225			return (sumstatus);
226
227		(void) fprintf(stderr,
228			"THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
229			TAILQ_NEXT(p, p_entries) ? "S" : "",
230			"UNEXPECTED INCONSISTENCY:");
231
232		for (; p; p = TAILQ_NEXT(p, p_entries))
233			(void) fprintf(stderr,
234			    "%s: %s (%s)%s", p->p_type, p->p_devname,
235			    p->p_mntpt, TAILQ_NEXT(p, p_entries) ? ", " : "\n");
236
237		return sumstatus;
238	}
239	(void) endfsent();
240	return (0);
241}
242
243static struct diskentry *
244finddisk(const char *name)
245{
246	const char *p;
247	size_t len = 0;
248	struct diskentry *d;
249
250	p = strrchr(name, '/');
251	if (p == NULL)
252		p = name;
253	else
254		p++;
255	for (; *p && !isdigit(*p); p++)
256		continue;
257	for (; *p && isdigit(*p); p++)
258		continue;
259	len = p - name;
260	if (len == 0)
261		len = strlen(name);
262
263	TAILQ_FOREACH(d, &diskh, d_entries)
264		if (strncmp(d->d_name, name, len) == 0 && d->d_name[len] == 0)
265			return d;
266
267	d = emalloc(sizeof(*d));
268	d->d_name = estrdup(name);
269	d->d_name[len] = '\0';
270	TAILQ_INIT(&d->d_part);
271	d->d_pid = 0;
272
273	TAILQ_INSERT_TAIL(&diskh, d, d_entries);
274	ndisks++;
275
276	return d;
277}
278
279
280static void
281printpart(void)
282{
283	struct diskentry *d;
284	struct partentry *p;
285
286	TAILQ_FOREACH(d, &diskh, d_entries) {
287		(void) printf("disk %s: ", d->d_name);
288		TAILQ_FOREACH(p, &d->d_part, p_entries)
289			(void) printf("%s ", p->p_devname);
290		(void) printf("\n");
291	}
292}
293
294
295static void
296addpart(const char *type, const char *dev, const char *mntpt, const int failok)
297{
298	struct diskentry *d = finddisk(dev);
299	struct partentry *p;
300
301	TAILQ_FOREACH(p, &d->d_part, p_entries)
302		if (strcmp(p->p_devname, dev) == 0) {
303			warnx("%s in fstab more than once!\n", dev);
304			return;
305		}
306
307	p = emalloc(sizeof(*p));
308	p->p_devname = estrdup(dev);
309	p->p_mntpt = estrdup(mntpt);
310	p->p_type = estrdup(type);
311	p->p_failok = failok;
312
313	TAILQ_INSERT_TAIL(&d->d_part, p, p_entries);
314}
315
316
317static int
318startdisk(struct diskentry *d, int (*checkit)(const char *, const char *,
319    const char *, const char *, pid_t *))
320{
321	struct partentry *p = TAILQ_FIRST(&d->d_part);
322	int rv;
323
324	while ((rv = (*checkit)(p->p_type, p->p_devname, p->p_mntpt,
325	    NULL, &d->d_pid)) != 0 && nrun > 0)
326		sleep(10);
327
328	if (rv == 0)
329		nrun++;
330
331	return rv;
332}
333