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