1254721Semaste/*	$NetBSD: preen.c,v 1.31 2012/04/07 04:52:20 christos Exp $	*/
2254721Semaste
3254721Semaste/*
4254721Semaste * Copyright (c) 1990, 1993
5254721Semaste *	The Regents of the University of California.  All rights reserved.
6254721Semaste *
7254721Semaste * Redistribution and use in source and binary forms, with or without
8254721Semaste * modification, are permitted provided that the following conditions
9254721Semaste * are met:
10254721Semaste * 1. Redistributions of source code must retain the above copyright
11254721Semaste *    notice, this list of conditions and the following disclaimer.
12254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
13254721Semaste *    notice, this list of conditions and the following disclaimer in the
14254721Semaste *    documentation and/or other materials provided with the distribution.
15254721Semaste * 3. Neither the name of the University nor the names of its contributors
16254721Semaste *    may be used to endorse or promote products derived from this software
17254721Semaste *    without specific prior written permission.
18254721Semaste *
19254721Semaste * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20254721Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21254721Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22254721Semaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23254721Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24254721Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25254721Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26254721Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27254721Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29254721Semaste * SUCH DAMAGE.
30254721Semaste */
31254721Semaste
32254721Semaste#include <sys/cdefs.h>
33254721Semaste#ifndef lint
34254721Semaste#if 0
35254721Semastestatic char sccsid[] = "@(#)preen.c	8.5 (Berkeley) 4/28/95";
36254721Semaste#else
37254721Semaste__RCSID("$NetBSD: preen.c,v 1.31 2012/04/07 04:52:20 christos Exp $");
38254721Semaste#endif
39254721Semaste#endif /* not lint */
40254721Semaste
41254721Semaste/*
42254721Semaste * used by sbin/fsck
43254721Semaste * used by usr.sbin/quotacheck
44254721Semaste */
45254721Semaste
46254721Semaste#include <sys/param.h>
47254721Semaste#include <sys/stat.h>
48254721Semaste#include <sys/wait.h>
49254721Semaste#include <sys/queue.h>
50254721Semaste#include <sys/disk.h>
51254721Semaste#include <sys/ioctl.h>
52254721Semaste
53254721Semaste#include <err.h>
54254721Semaste#include <ctype.h>
55254721Semaste#include <fstab.h>
56254721Semaste#include <fcntl.h>
57254721Semaste#include <string.h>
58254721Semaste#include <stdio.h>
59254721Semaste#include <stdlib.h>
60254721Semaste#include <unistd.h>
61254721Semaste#include <util.h>
62254721Semaste
63254721Semaste#include "fsutil.h"
64254721Semaste#include "exitvalues.h"
65254721Semaste
66254721Semastestruct partentry {
67254721Semaste	TAILQ_ENTRY(partentry)	 p_entries;
68254721Semaste	char		  	*p_devname;	/* device name */
69254721Semaste	char			*p_mntpt;	/* mount point */
70254721Semaste	char		  	*p_type;	/* file system type */
71254721Semaste	void			*p_auxarg;	/* auxiliary argument */
72254721Semaste};
73254721Semaste
74254721SemasteTAILQ_HEAD(part, partentry) badh;
75254721Semaste
76254721Semastestruct diskentry {
77254721Semaste	TAILQ_ENTRY(diskentry) 	    d_entries;
78254721Semaste	char			   *d_name;	/* disk base name */
79254721Semaste	TAILQ_HEAD(prt, partentry)  d_part;	/* list of partitions on disk */
80254721Semaste	int			    d_pid;	/* 0 or pid of fsck proc */
81254721Semaste};
82254721Semaste
83254721SemasteTAILQ_HEAD(diskinfo, diskentry) diskh;
84254721Semaste
85254721Semastestatic int nrun = 0, ndisks = 0;
86254721Semaste
87254721Semastestatic struct diskentry *finddisk(const char *);
88254721Semastestatic void addpart(const char *, const char *, const char *, void *);
89254721Semastestatic int startdisk(struct diskentry *,
90254721Semaste    int (*)(const char *, const char *, const char *, void *, pid_t *));
91254721Semastestatic void printpart(void);
92254721Semaste
93254721Semasteint
94254721Semastecheckfstab(int flags, int maxrun, void *(*docheck)(struct fstab *),
95254721Semaste    int (*checkit)(const char *, const char *, const char *, void *, pid_t *))
96254721Semaste{
97254721Semaste	struct fstab *fs;
98254721Semaste	struct diskentry *d, *nextdisk;
99254721Semaste	struct partentry *p;
100254721Semaste	int ret, pid, retcode, passno, sumstatus, status;
101254721Semaste	void *auxarg;
102254721Semaste	const char *name;
103254721Semaste	int error = FSCK_EXIT_OK;
104254721Semaste
105254721Semaste	TAILQ_INIT(&badh);
106254721Semaste	TAILQ_INIT(&diskh);
107254721Semaste
108254721Semaste	sumstatus = FSCK_EXIT_OK;
109254721Semaste
110254721Semaste	for (passno = 1; passno <= 2; passno++) {
111254721Semaste		if (setfsent() == 0) {
112254721Semaste			warnx("Can't open checklist file: %s", _PATH_FSTAB);
113254721Semaste			return FSCK_EXIT_CHECK_FAILED;
114254721Semaste		}
115254721Semaste		while ((fs = getfsent()) != 0) {
116254721Semaste			char buf[MAXPATHLEN];
117254721Semaste			const char *fsspec;
118254721Semaste			if ((auxarg = (*docheck)(fs)) == NULL)
119254721Semaste				continue;
120254721Semaste			fsspec = getfsspecname(buf, sizeof(buf), fs->fs_spec);
121254721Semaste			if (fsspec == NULL) {
122254721Semaste				warn("%s", buf);
123254721Semaste				return FSCK_EXIT_CHECK_FAILED;
124254721Semaste			}
125254721Semaste			name = blockcheck(fsspec);
126254721Semaste			if (flags & CHECK_DEBUG)
127254721Semaste				printf("pass %d, name %s\n", passno, name);
128254721Semaste
129254721Semaste			if ((flags & CHECK_PREEN) == 0 ||
130254721Semaste			    (passno == 1 && fs->fs_passno == 1)) {
131254721Semaste				if (name == NULL) {
132254721Semaste					if (flags & CHECK_PREEN)
133254721Semaste						return FSCK_EXIT_CHECK_FAILED;
134254721Semaste					else
135254721Semaste						continue;
136254721Semaste				}
137254721Semaste				sumstatus = (*checkit)(fs->fs_vfstype,
138254721Semaste				    name, fs->fs_file, auxarg, NULL);
139254721Semaste
140254721Semaste				if (sumstatus) {
141254721Semaste					if ((flags & CHECK_NOFIX) == 0)
142254721Semaste						return sumstatus;
143254721Semaste					else if (error < sumstatus)
144254721Semaste						error = sumstatus;
145254721Semaste				}
146254721Semaste			} else if (passno == 2 && fs->fs_passno > 1) {
147254721Semaste				if (name == NULL) {
148254721Semaste					(void) fprintf(stderr,
149254721Semaste					    "BAD DISK NAME %s\n", fsspec);
150254721Semaste					sumstatus = FSCK_EXIT_CHECK_FAILED;
151254721Semaste					continue;
152254721Semaste				}
153254721Semaste				addpart(fs->fs_vfstype, name, fs->fs_file,
154254721Semaste				    auxarg);
155254721Semaste			}
156254721Semaste		}
157254721Semaste		if ((flags & CHECK_PREEN) == 0)
158254721Semaste			return error;
159254721Semaste	}
160254721Semaste
161254721Semaste	if (flags & CHECK_DEBUG)
162254721Semaste		printpart();
163254721Semaste
164254721Semaste	if (flags & CHECK_PREEN) {
165254721Semaste		if (maxrun == 0)
166254721Semaste			maxrun = ndisks;
167254721Semaste		if (maxrun > ndisks)
168254721Semaste			maxrun = ndisks;
169254721Semaste		nextdisk = TAILQ_FIRST(&diskh);
170254721Semaste		for (passno = 0; passno < maxrun; ++passno) {
171254721Semaste			if ((ret = startdisk(nextdisk, checkit)) != 0) {
172254721Semaste				if ((flags & CHECK_NOFIX) == 0)
173254721Semaste					return ret;
174254721Semaste				else if (error < ret)
175254721Semaste					error = ret;
176254721Semaste			}
177254721Semaste			nextdisk = TAILQ_NEXT(nextdisk, d_entries);
178254721Semaste		}
179254721Semaste
180254721Semaste		while ((pid = wait(&status)) != -1) {
181254721Semaste			TAILQ_FOREACH(d, &diskh, d_entries)
182254721Semaste				if (d->d_pid == pid)
183254721Semaste					break;
184254721Semaste
185254721Semaste			if (d == NULL) {
186254721Semaste				warnx("Unknown pid %d", pid);
187254721Semaste				continue;
188254721Semaste			}
189254721Semaste
190254721Semaste
191254721Semaste			if (WIFEXITED(status))
192254721Semaste				retcode = WEXITSTATUS(status);
193254721Semaste			else
194254721Semaste				retcode = 0;
195254721Semaste
196254721Semaste			p = TAILQ_FIRST(&d->d_part);
197254721Semaste
198254721Semaste			if (flags & (CHECK_DEBUG|CHECK_VERBOSE))
199254721Semaste				(void) printf("done %s: %s (%s) = 0x%x\n",
200254721Semaste				    p->p_type, p->p_devname, p->p_mntpt,
201254721Semaste				    status);
202254721Semaste
203254721Semaste			if (WIFSIGNALED(status)) {
204254721Semaste				(void) fprintf(stderr,
205254721Semaste				    "%s: %s (%s): EXITED WITH SIGNAL %d\n",
206254721Semaste				    p->p_type, p->p_devname, p->p_mntpt,
207254721Semaste				    WTERMSIG(status));
208254721Semaste				retcode = FSCK_EXIT_SIGNALLED;
209254721Semaste			}
210254721Semaste
211254721Semaste			TAILQ_REMOVE(&d->d_part, p, p_entries);
212254721Semaste
213254721Semaste			if (retcode != 0) {
214254721Semaste				TAILQ_INSERT_TAIL(&badh, p, p_entries);
215254721Semaste				sumstatus |= retcode;
216254721Semaste			} else {
217254721Semaste				free(p->p_type);
218254721Semaste				free(p->p_devname);
219254721Semaste				free(p);
220254721Semaste			}
221254721Semaste			d->d_pid = 0;
222254721Semaste			nrun--;
223254721Semaste
224254721Semaste			if (TAILQ_FIRST(&d->d_part) == NULL)
225254721Semaste				ndisks--;
226254721Semaste
227254721Semaste			if (nextdisk == NULL) {
228254721Semaste				if (TAILQ_FIRST(&d->d_part) != NULL) {
229254721Semaste					if ((ret = startdisk(d, checkit)) != 0)
230254721Semaste					{
231254721Semaste						if ((flags & CHECK_NOFIX) == 0)
232254721Semaste							return ret;
233254721Semaste						else if (error < ret)
234254721Semaste							error = ret;
235254721Semaste					}
236254721Semaste				}
237254721Semaste			} else if (nrun < maxrun && nrun < ndisks) {
238254721Semaste				for ( ;; ) {
239254721Semaste					nextdisk = TAILQ_NEXT(nextdisk,
240254721Semaste					    d_entries);
241254721Semaste					if (nextdisk == NULL)
242254721Semaste						nextdisk = TAILQ_FIRST(&diskh);
243254721Semaste					if (TAILQ_FIRST(&nextdisk->d_part)
244254721Semaste					    != NULL && nextdisk->d_pid == 0)
245254721Semaste						break;
246254721Semaste				}
247254721Semaste				if ((ret = startdisk(nextdisk, checkit)) != 0)
248254721Semaste				{
249254721Semaste					if ((flags & CHECK_NOFIX) == 0)
250254721Semaste						return ret;
251254721Semaste					else if (error < ret)
252254721Semaste						error = ret;
253254721Semaste				}
254254721Semaste			}
255254721Semaste		}
256254721Semaste	}
257254721Semaste	if (sumstatus) {
258254721Semaste		p = TAILQ_FIRST(&badh);
259254721Semaste		if (p == NULL)
260254721Semaste			return sumstatus;
261254721Semaste
262254721Semaste		(void) fprintf(stderr,
263254721Semaste			"THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
264254721Semaste			TAILQ_NEXT(p, p_entries) ? "S" : "",
265254721Semaste			"UNEXPECTED INCONSISTENCY:");
266254721Semaste
267254721Semaste		TAILQ_FOREACH(p, &badh, p_entries)
268254721Semaste			(void) fprintf(stderr,
269254721Semaste			    "%s: %s (%s)%s", p->p_type, p->p_devname,
270254721Semaste			    p->p_mntpt, TAILQ_NEXT(p, p_entries) ? ", " : "\n");
271254721Semaste
272254721Semaste		return sumstatus;
273254721Semaste	}
274254721Semaste	(void) endfsent();
275254721Semaste	return error;
276254721Semaste}
277254721Semaste
278254721Semaste
279254721Semastestatic struct diskentry *
280254721Semastefinddisk(const char *name)
281254721Semaste{
282254721Semaste	const char *p;
283254721Semaste	size_t len, dlen;
284254721Semaste	struct diskentry *d;
285254721Semaste	char buf[MAXPATHLEN];
286254721Semaste	struct dkwedge_info dkw;
287254721Semaste	int fd;
288254721Semaste
289254721Semaste	if ((fd = opendisk(name, O_RDONLY, buf, sizeof(buf), 0)) != -1) {
290254721Semaste		if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1)
291254721Semaste			name = dkw.dkw_parent;
292254721Semaste		(void)close(fd);
293254721Semaste	}
294254721Semaste
295254721Semaste	for (dlen = len = strlen(name), p = name + len - 1; p >= name; --p)
296254721Semaste		if (isdigit((unsigned char)*p)) {
297254721Semaste			len = p - name + 1;
298254721Semaste			break;
299254721Semaste		}
300254721Semaste	if (p < name)
301254721Semaste		len = dlen;
302254721Semaste
303254721Semaste	TAILQ_FOREACH(d, &diskh, d_entries)
304254721Semaste		if (strncmp(d->d_name, name, len) == 0 && d->d_name[len] == 0)
305254721Semaste			return d;
306254721Semaste
307254721Semaste	d = emalloc(sizeof(*d));
308254721Semaste	d->d_name = estrdup(name);
309254721Semaste	d->d_name[len] = '\0';
310254721Semaste	TAILQ_INIT(&d->d_part);
311254721Semaste	d->d_pid = 0;
312254721Semaste
313254721Semaste	TAILQ_INSERT_TAIL(&diskh, d, d_entries);
314254721Semaste	ndisks++;
315254721Semaste
316254721Semaste	return d;
317254721Semaste}
318254721Semaste
319254721Semaste
320254721Semastestatic void
321254721Semasteprintpart(void)
322254721Semaste{
323254721Semaste	struct diskentry *d;
324254721Semaste	struct partentry *p;
325254721Semaste
326254721Semaste	TAILQ_FOREACH(d, &diskh, d_entries) {
327254721Semaste		(void) printf("disk %s:", d->d_name);
328254721Semaste		TAILQ_FOREACH(p, &d->d_part, p_entries)
329254721Semaste			(void) printf(" %s", p->p_devname);
330254721Semaste		(void) printf("\n");
331254721Semaste	}
332254721Semaste}
333254721Semaste
334254721Semaste
335254721Semastestatic void
336254721Semasteaddpart(const char *type, const char *dev, const char *mntpt, void *auxarg)
337254721Semaste{
338254721Semaste	struct diskentry *d = finddisk(dev);
339254721Semaste	struct partentry *p;
340254721Semaste
341254721Semaste	TAILQ_FOREACH(p, &d->d_part, p_entries)
342254721Semaste		if (strcmp(p->p_devname, dev) == 0) {
343254721Semaste			warnx("%s in fstab more than once!", dev);
344254721Semaste			return;
345254721Semaste		}
346254721Semaste
347254721Semaste	p = emalloc(sizeof(*p));
348254721Semaste	p->p_devname = estrdup(dev);
349254721Semaste	p->p_mntpt = estrdup(mntpt);
350254721Semaste	p->p_type = estrdup(type);
351254721Semaste	p->p_auxarg = auxarg;
352254721Semaste
353254721Semaste	TAILQ_INSERT_TAIL(&d->d_part, p, p_entries);
354254721Semaste}
355254721Semaste
356254721Semaste
357254721Semastestatic int
358254721Semastestartdisk(struct diskentry *d,
359254721Semaste    int (*checkit)(const char *, const char *, const char *, void *, pid_t *))
360254721Semaste{
361254721Semaste	struct partentry *p = TAILQ_FIRST(&d->d_part);
362254721Semaste	int rv;
363254721Semaste
364254721Semaste	while ((rv = (*checkit)(p->p_type, p->p_devname, p->p_mntpt,
365254721Semaste	    p->p_auxarg, &d->d_pid)) != 0 && nrun > 0)
366254721Semaste		sleep(10);
367254721Semaste
368254721Semaste	if (rv == 0)
369254721Semaste		nrun++;
370254721Semaste
371254721Semaste	return rv;
372254721Semaste}
373254721Semaste