fsck.c revision 88f8af61a48bafe35fdbfcb0d19f4a83f0ce3d66
130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng/*
230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * pfsck --- A generic, parallelizing front-end for the fsck program.
330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * It will automatically try to run fsck programs in parallel if the
430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * devices are on separate spindles.  It is based on the same ideas as
530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * the generic front end for fsck by David Engel and Fred van Kempen,
630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * but it has been completely rewritten from scratch to support
730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * parallel execution.
830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *
930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * Written by Theodore Ts'o, <tytso@mit.edu>
1030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *
1130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * Usage:	fsck [-ACVRNTM] [-s] [-t fstype] [fs-options] device
1230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *
1330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
1430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *   o Changed -t fstype to behave like with mount when -A (all file
1530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *     systems) or -M (like mount) is specified.
1630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *   o fsck looks if it can find the fsck.type program to decide
1730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *     if it should ignore the fs type. This way more fsck programs
1830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *     can be added without changing this front-end.
1930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *   o -R flag skip root file system.
2030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *
2130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o.
2230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng *
2330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * %Begin-Header%
2430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * This file may be redistributed under the terms of the GNU Public
2530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * License.
2630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * %End-Header%
2730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng */
2830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
2930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <sys/types.h>
3030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <sys/wait.h>
3130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <sys/signal.h>
3230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <sys/stat.h>
3330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <limits.h>
3430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <stdio.h>
3530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <ctype.h>
3630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <string.h>
3730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <time.h>
3830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#if HAVE_STDLIB_H
3930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <stdlib.h>
4030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#endif
4130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#if HAVE_ERRNO_H
4230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <errno.h>
4330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#endif
4430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#if HAVE_PATHS_H
4530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <paths.h>
4630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#endif
4730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#if HAVE_UNISTD_H
4830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <unistd.h>
4930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#endif
5030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#if HAVE_ERRNO_H
5130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <errno.h>
5230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#endif
5330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include <malloc.h>
5430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
5530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include "../version.h"
5630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include "nls-enable.h"
5730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include "fsck.h"
5830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#include "get_device_by_label.h"
5930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
6030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#ifndef _PATH_MNTTAB
6130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#define	_PATH_MNTTAB	"/etc/fstab"
6230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#endif
6330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
6430692c65c4174412c90e79489e98ab85c1a7412fBen Chengstatic const char *ignored_types[] = {
6530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"ignore",
6630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"iso9660",
6730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"nfs",
6830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"proc",
6930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"sw",
7030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"swap",
7130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	NULL
7230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng};
7330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
7430692c65c4174412c90e79489e98ab85c1a7412fBen Chengstatic const char *really_wanted[] = {
7530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"minix",
7630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"ext2",
7730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"ext3",
7830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	"xiafs",
7930692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	NULL
8030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng};
8130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
8230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng#define BASE_MD "/dev/md"
8330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
8430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng/*
8530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng * Global variables for options
8630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng */
8730692c65c4174412c90e79489e98ab85c1a7412fBen Chengchar *devices[MAX_DEVICES];
8830692c65c4174412c90e79489e98ab85c1a7412fBen Chengchar *args[MAX_ARGS];
8930692c65c4174412c90e79489e98ab85c1a7412fBen Chengint num_devices, num_args;
9030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
9130692c65c4174412c90e79489e98ab85c1a7412fBen Chengint verbose = 0;
9230692c65c4174412c90e79489e98ab85c1a7412fBen Chengint doall = 0;
9330692c65c4174412c90e79489e98ab85c1a7412fBen Chengint noexecute = 0;
9430692c65c4174412c90e79489e98ab85c1a7412fBen Chengint serialize = 0;
9530692c65c4174412c90e79489e98ab85c1a7412fBen Chengint skip_root = 0;
9630692c65c4174412c90e79489e98ab85c1a7412fBen Chengint like_mount = 0;
9730692c65c4174412c90e79489e98ab85c1a7412fBen Chengint notitle = 0;
9830692c65c4174412c90e79489e98ab85c1a7412fBen Chengint parallel_root = 0;
9930692c65c4174412c90e79489e98ab85c1a7412fBen Chengint progress = 0;
10030692c65c4174412c90e79489e98ab85c1a7412fBen Chengint force_all_parallel = 0;
10130692c65c4174412c90e79489e98ab85c1a7412fBen Chengchar *progname;
10230692c65c4174412c90e79489e98ab85c1a7412fBen Chengchar *fstype = NULL;
10330692c65c4174412c90e79489e98ab85c1a7412fBen Chengstruct fs_info *filesys_info;
10430692c65c4174412c90e79489e98ab85c1a7412fBen Chengstruct fsck_instance *instance_list;
10530692c65c4174412c90e79489e98ab85c1a7412fBen Chengconst char *fsck_prefix_path = "/sbin:/sbin/fs.d:/sbin/fs:/etc/fs:/etc";
10630692c65c4174412c90e79489e98ab85c1a7412fBen Chengchar *fsck_path = 0;
10730692c65c4174412c90e79489e98ab85c1a7412fBen Chengstatic int ignore(struct fs_info *);
10830692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
10930692c65c4174412c90e79489e98ab85c1a7412fBen Chengchar *string_copy(const char *s)
11030692c65c4174412c90e79489e98ab85c1a7412fBen Cheng{
11130692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	char	*ret;
11230692c65c4174412c90e79489e98ab85c1a7412fBen Cheng
11330692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	ret = malloc(strlen(s)+1);
11430692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	if (ret)
11530692c65c4174412c90e79489e98ab85c1a7412fBen Cheng		strcpy(ret, s);
11630692c65c4174412c90e79489e98ab85c1a7412fBen Cheng	return ret;
11730692c65c4174412c90e79489e98ab85c1a7412fBen Cheng}
118
119static char *skip_over_blank(char *cp)
120{
121	while (*cp && isspace(*cp))
122		cp++;
123	return cp;
124}
125
126static char *skip_over_word(char *cp)
127{
128	while (*cp && !isspace(*cp))
129		cp++;
130	return cp;
131}
132
133static void strip_line(char *line)
134{
135	char	*p;
136
137	while (*line) {
138		p = line + strlen(line) - 1;
139		if ((*p == '\n') || (*p == '\r'))
140			*p = 0;
141		else
142			break;
143	}
144}
145
146static char *parse_word(char **buf)
147{
148	char *word, *next;
149
150	word = *buf;
151	if (*word == 0)
152		return 0;
153
154	word = skip_over_blank(word);
155	next = skip_over_word(word);
156	if (*next)
157		*next++ = 0;
158	*buf = next;
159	return word;
160}
161
162static void free_instance(struct fsck_instance *i)
163{
164	if (i->prog)
165		free(i->prog);
166	if (i->device)
167		free(i->device);
168	if (i->base_device)
169		free(i->base_device);
170	free(i);
171	return;
172}
173
174static int parse_fstab_line(char *line, struct fs_info **ret_fs)
175{
176	char	*device, *mntpnt, *type, *opts, *freq, *passno, *cp;
177	struct fs_info *fs;
178
179	*ret_fs = 0;
180	strip_line(line);
181	if ((cp = strchr(line, '#')))
182		*cp = 0;	/* Ignore everything after the comment char */
183	cp = line;
184
185	device = parse_word(&cp);
186	mntpnt = parse_word(&cp);
187	type = parse_word(&cp);
188	opts = parse_word(&cp);
189	freq = parse_word(&cp);
190	passno = parse_word(&cp);
191
192	if (!device)
193		return 0;	/* Allow blank lines */
194
195	if (!mntpnt || !type)
196		return -1;
197
198	if (!(fs = malloc(sizeof(struct fs_info))))
199		return -1;
200
201	fs->device = string_copy(device);
202	fs->mountpt = string_copy(mntpnt);
203	fs->type = string_copy(type);
204	fs->opts = string_copy(opts ? opts : "");
205	fs->freq = freq ? atoi(freq) : -1;
206	fs->passno = passno ? atoi(passno) : -1;
207	fs->flags = 0;
208	fs->next = NULL;
209
210	*ret_fs = fs;
211
212	return 0;
213}
214
215/*
216 * Interpret the device name if necessary
217 */
218static char *interpret_device(char *spec)
219{
220	char *dev = NULL;
221
222	if (!strncmp(spec, "UUID=", 5))
223		dev = get_spec_by_uuid(spec+5);
224	else if (!strncmp(spec, "LABEL=", 6))
225		dev = get_spec_by_volume_label(spec+6);
226	else
227		return spec;
228	if (dev) {
229		free(spec);
230		return (dev);
231	}
232	/*
233	 * Check to see if this was because /proc/partitions isn't
234	 * found.
235	 */
236	if (access("/proc/partitions", R_OK) < 0) {
237		fprintf(stderr, "Couldn't open /proc/partitions: %s\n",
238			strerror(errno));
239		fprintf(stderr, "Is /proc mounted?\n");
240		exit(EXIT_ERROR);
241	}
242	/*
243	 * Check to see if this is because we're not running as root
244	 */
245	if (geteuid())
246		fprintf(stderr, "Must be root to scan for matching "
247			"filesystems: %s\n", spec);
248	else
249		fprintf(stderr, "Couldn't find matching filesystem: %s\n",
250			spec);
251	exit(EXIT_ERROR);
252}
253
254/*
255 * Interpret filesystem auto type if necessary
256 */
257static void interpret_type(struct fs_info *fs)
258{
259	const char	*type;
260
261	if (strcmp(fs->type, "auto") == 0) {
262		type = identify_fs(fs->device);
263		if (type) {
264			free(fs->type);
265			fs->type = string_copy(type);
266		} else
267			fprintf(stderr, _("Could not determine "
268					  "filesystem type for %s\n"),
269				fs->device);
270	}
271}
272
273
274/*
275 * Load the filesystem database from /etc/fstab
276 */
277static void load_fs_info(const char *filename)
278{
279	FILE	*f;
280	char	buf[1024];
281	int	lineno = 0;
282	int	old_fstab = 1;
283	struct fs_info *fs, *fs_last = NULL;
284
285	filesys_info = NULL;
286	if ((f = fopen(filename, "r")) == NULL) {
287		fprintf(stderr, _("WARNING: couldn't open %s: %s\n"),
288			filename, strerror(errno));
289		return;
290	}
291	while (!feof(f)) {
292		lineno++;
293		if (!fgets(buf, sizeof(buf), f))
294			break;
295		buf[sizeof(buf)-1] = 0;
296		if (parse_fstab_line(buf, &fs) < 0) {
297			fprintf(stderr, _("WARNING: bad format "
298				"on line %d of %s\n"), lineno, filename);
299			continue;
300		}
301		if (!fs)
302			continue;
303		if (!filesys_info)
304			filesys_info = fs;
305		else
306			fs_last->next = fs;
307		fs_last = fs;
308		if (fs->passno < 0)
309			fs->passno = 0;
310		else
311			old_fstab = 0;
312	}
313
314	fclose(f);
315
316	if (old_fstab) {
317		fprintf(stderr, _("\007\007\007"
318		"WARNING: Your /etc/fstab does not contain the fsck passno\n"
319		"	field.  I will kludge around things for you, but you\n"
320		"	should fix your /etc/fstab file as soon as you can.\n\n"));
321
322		for (fs = filesys_info; fs; fs = fs->next) {
323			fs->passno = 1;
324		}
325	}
326}
327
328/* Lookup filesys in /etc/fstab and return the corresponding entry. */
329static struct fs_info *lookup(char *filesys)
330{
331	struct fs_info *fs;
332	int	try_again = 0;
333
334	/* No filesys name given. */
335	if (filesys == NULL)
336		return NULL;
337
338	for (fs = filesys_info; fs; fs = fs->next) {
339		if (strchr(fs->device, '='))
340			try_again++;
341		if (!strcmp(filesys, fs->device) ||
342		    !strcmp(filesys, fs->mountpt))
343			break;
344	}
345	if (fs && strchr(fs->device, '='))
346		fs->device = interpret_device(fs->device);
347
348	if (fs || !try_again)
349		return fs;
350
351	for (fs = filesys_info; fs; fs = fs->next) {
352		fs->device = interpret_device(fs->device);
353		if (!strcmp(filesys, fs->device) ||
354		    !strcmp(filesys, fs->mountpt))
355			break;
356	}
357
358	return fs;
359}
360
361/* Find fsck program for a given fs type. */
362static char *find_fsck(char *type)
363{
364  char *s;
365  const char *tpl;
366  static char prog[256];
367  char *p = string_copy(fsck_path);
368  struct stat st;
369
370  /* Are we looking for a program or just a type? */
371  tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
372
373  for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
374	sprintf(prog, tpl, s, type);
375	if (stat(prog, &st) == 0) break;
376  }
377  free(p);
378  return(s ? prog : NULL);
379}
380
381static int progress_active(NOARGS)
382{
383	struct fsck_instance *inst;
384
385	for (inst = instance_list; inst; inst = inst->next) {
386		if (inst->flags & FLAG_DONE)
387			continue;
388		if (inst->flags & FLAG_PROGRESS)
389			return 1;
390	}
391	return 0;
392}
393
394/*
395 * Execute a particular fsck program, and link it into the list of
396 * child processes we are waiting for.
397 */
398static int execute(const char *type, char *device, char *mntpt,
399		   int interactive)
400{
401	char *s, *argv[80], prog[80];
402	int  argc, i;
403	struct fsck_instance *inst, *p;
404	pid_t	pid;
405
406	inst = malloc(sizeof(struct fsck_instance));
407	if (!inst)
408		return ENOMEM;
409	memset(inst, 0, sizeof(struct fsck_instance));
410
411	sprintf(prog, "fsck.%s", type);
412	argv[0] = string_copy(prog);
413	argc = 1;
414
415	for (i=0; i <num_args; i++)
416		argv[argc++] = string_copy(args[i]);
417
418	if (progress & !progress_active()) {
419		if ((strcmp(type, "ext2") == 0) ||
420		    (strcmp(type, "ext3") == 0)) {
421			argv[argc++] = string_copy("-C0");
422			inst->flags |= FLAG_PROGRESS;
423		}
424	}
425
426	argv[argc++] = string_copy(device);
427	argv[argc] = 0;
428
429	s = find_fsck(prog);
430	if (s == NULL) {
431		fprintf(stderr, _("fsck: %s: not found\n"), prog);
432		return ENOENT;
433	}
434
435	if (verbose || noexecute) {
436		printf("[%s -- %s] ", s, mntpt ? mntpt : device);
437		for (i=0; i < argc; i++)
438			printf("%s ", argv[i]);
439		printf("\n");
440	}
441
442	/* Fork and execute the correct program. */
443	if (noexecute)
444		pid = -1;
445	else if ((pid = fork()) < 0) {
446		perror("fork");
447		return errno;
448	} else if (pid == 0) {
449		if (!interactive)
450			close(0);
451		(void) execv(s, argv);
452		perror(argv[0]);
453		exit(EXIT_ERROR);
454	}
455
456	for (i=0; i < argc; i++)
457		free(argv[i]);
458
459	inst->pid = pid;
460	inst->prog = string_copy(prog);
461	inst->type = string_copy(type);
462	inst->device = string_copy(device);
463	inst->base_device = base_device(device);
464	inst->start_time = time(0);
465	inst->next = NULL;
466
467	/*
468	 * Find the end of the list, so we add the instance on at the end.
469	 */
470	for (p = instance_list; p && p->next; p = p->next);
471
472	if (p)
473		p->next = inst;
474	else
475		instance_list = inst;
476
477	return 0;
478}
479
480/*
481 * Wait for one child process to exit; when it does, unlink it from
482 * the list of executing child processes, and return it.
483 */
484static struct fsck_instance *wait_one(NOARGS)
485{
486	int	status;
487	int	sig;
488	struct fsck_instance *inst, *inst2, *prev;
489	pid_t	pid;
490
491	if (!instance_list)
492		return NULL;
493
494	if (noexecute) {
495		inst = instance_list;
496		instance_list = inst->next;
497		inst->exit_status = 0;
498		return(inst);
499	}
500
501	/*
502	 * gcc -Wall fails saving throw against stupidity
503	 * (inst and prev are thought to be uninitialized variables)
504	 */
505	inst = prev = NULL;
506
507	do {
508		pid = wait(&status);
509		if (pid < 0) {
510			if ((errno == EINTR) || (errno == EAGAIN))
511				continue;
512			if (errno == ECHILD) {
513				fprintf(stderr,
514					_("%s: wait: No more child process?!?\n"),
515					progname);
516				return NULL;
517			}
518			perror("wait");
519			continue;
520		}
521		for (prev = 0, inst = instance_list;
522		     inst;
523		     prev = inst, inst = inst->next) {
524			if (inst->pid == pid)
525				break;
526		}
527	} while (!inst);
528
529	if (WIFEXITED(status))
530		status = WEXITSTATUS(status);
531	else if (WIFSIGNALED(status)) {
532		sig = WTERMSIG(status);
533		if (sig == SIGINT) {
534			status = EXIT_UNCORRECTED;
535		} else {
536			printf(_("Warning... %s for device %s exited "
537			       "with signal %d.\n"),
538			       inst->prog, inst->device, sig);
539			status = EXIT_ERROR;
540		}
541	} else {
542		printf(_("%s %s: status is %x, should never happen.\n"),
543		       inst->prog, inst->device, status);
544		status = EXIT_ERROR;
545	}
546	inst->exit_status = status;
547	if (prev)
548		prev->next = inst->next;
549	else
550		instance_list = inst->next;
551	if (progress && (inst->flags & FLAG_PROGRESS) &&
552	    !progress_active()) {
553		for (inst2 = instance_list; inst2; inst2 = inst2->next) {
554			if (inst2->flags & FLAG_DONE)
555				continue;
556			if (strcmp(inst2->type, "ext2") &&
557			    strcmp(inst2->type, "ext3"))
558				continue;
559			/*
560			 * If we've just started the fsck, wait a tiny
561			 * bit before sending the kill, to give it
562			 * time to set up the signal handler
563			 */
564			if (inst2->start_time < time(0)+2) {
565				if (fork() == 0) {
566					sleep(1);
567					kill(inst2->pid, SIGUSR1);
568					exit(0);
569				}
570			} else
571				kill(inst2->pid, SIGUSR1);
572			inst2->flags |= FLAG_PROGRESS;
573			break;
574		}
575	}
576	return inst;
577}
578
579/*
580 * Wait until all executing child processes have exited; return the
581 * logical OR of all of their exit code values.
582 */
583static int wait_all(NOARGS)
584{
585	struct fsck_instance *inst;
586	int	global_status = 0;
587
588	while (instance_list) {
589		inst = wait_one();
590		if (!inst)
591			break;
592		global_status |= inst->exit_status;
593		free_instance(inst);
594	}
595	return global_status;
596}
597
598/*
599 * Run the fsck program on a particular device
600 *
601 * If the type is specified using -t, and it isn't prefixed with "no"
602 * (as in "noext2") and only one filesystem type is specified, then
603 * use that type regardless of what is specified in /etc/fstab.
604 *
605 * If the type isn't specified by the user, then use either the type
606 * specified in /etc/fstab, or DEFAULT_FSTYPE.
607 */
608static void fsck_device(char *device, int interactive)
609{
610	const char *type = 0;
611	struct fs_info *fsent;
612	int retval;
613
614	if (fstype && strncmp(fstype, "no", 2) &&
615	    strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
616	    !strchr(fstype, ','))
617		type = fstype;
618
619	if ((fsent = lookup(device))) {
620		device = fsent->device;
621		interpret_type(fsent);
622		if (!type)
623			type = fsent->type;
624	}
625	if (!type)
626		type = DEFAULT_FSTYPE;
627
628	retval = execute(type, device, fsent ? fsent->mountpt : 0,
629			 interactive);
630	if (retval) {
631		fprintf(stderr, _("%s: Error %d while executing fsck.%s "
632			"for %s\n"), progname, retval, type, device);
633	}
634}
635
636
637/*
638 * Deal with the fsck -t argument.
639 */
640struct fs_type_compile {
641	char **list;
642	int *type;
643	int  negate;
644} fs_type_compiled;
645
646#define FS_TYPE_NORMAL	0
647#define FS_TYPE_OPT	1
648#define FS_TYPE_NEGOPT	2
649
650static const char *fs_type_syntax_error =
651N_("Either all or none of the filesystem types passed to -t must be prefixed\n"
652   "with 'no' or '!'.\n");
653
654static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp)
655{
656	char 	*cp, *list, *s;
657	int	num = 2;
658	int	negate, first_negate = 1;
659
660	if (fs_type) {
661		for (cp=fs_type; *cp; cp++) {
662			if (*cp == ',')
663				num++;
664		}
665	}
666
667	cmp->list = malloc(num * sizeof(char *));
668	cmp->type = malloc(num * sizeof(int));
669	if (!cmp->list || !cmp->type) {
670		fprintf(stderr, _("Couldn't allocate memory for "
671				  "filesystem types\n"));
672		exit(EXIT_ERROR);
673	}
674	memset(cmp->list, 0, num * sizeof(char *));
675	memset(cmp->type, 0, num * sizeof(int));
676	cmp->negate = 0;
677
678	if (!fs_type)
679		return;
680
681	list = string_copy(fs_type);
682	num = 0;
683	s = strtok(list, ",");
684	while(s) {
685		negate = 0;
686		if (strncmp(s, "no", 2) == 0) {
687			s += 2;
688			negate = 1;
689		} else if (*s == '!') {
690			s++;
691			negate = 1;
692		}
693		if (strcmp(s, "loop") == 0)
694			/* loop is really short-hand for opts=loop */
695			goto loop_special_case;
696		else if (strncmp(s, "opts=", 5) == 0) {
697			s += 5;
698		loop_special_case:
699			cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT;
700		} else {
701			if (first_negate) {
702				cmp->negate = negate;
703				first_negate = 0;
704			}
705			if ((negate && !cmp->negate) ||
706			    (!negate && cmp->negate)) {
707				fprintf(stderr, _(fs_type_syntax_error));
708				exit(EXIT_USAGE);
709			}
710		}
711#if 0
712		printf("Adding %s to list (type %d).\n", s, cmp->type[num]);
713#endif
714	        cmp->list[num++] = string_copy(s);
715		s = strtok(NULL, ",");
716	}
717	free(list);
718}
719
720/*
721 * This function returns true if a particular option appears in a
722 * comma-delimited options list
723 */
724static int opt_in_list(char *opt, char *optlist)
725{
726	char	*list, *s;
727
728	if (!optlist)
729		return 0;
730	list = string_copy(optlist);
731
732	s = strtok(list, ",");
733	while(s) {
734		if (strcmp(s, opt) == 0) {
735			free(list);
736			return 1;
737		}
738		s = strtok(NULL, ",");
739	}
740        free(list);
741	return 0;
742}
743
744/* See if the filesystem matches the criteria given by the -t option */
745static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp)
746{
747	int n, ret = 0, checked_type = 0;
748	char *cp;
749
750	if (cmp->list == 0 || cmp->list[0] == 0)
751		return 1;
752
753	for (n=0; cp = cmp->list[n]; n++) {
754		switch (cmp->type[n]) {
755		case FS_TYPE_NORMAL:
756			checked_type++;
757			if (strcmp(cp, fs->type) == 0) {
758				ret = 1;
759			}
760			break;
761		case FS_TYPE_NEGOPT:
762			if (opt_in_list(cp, fs->opts))
763				return 0;
764			break;
765		case FS_TYPE_OPT:
766			if (!opt_in_list(cp, fs->opts))
767				return 0;
768			break;
769		}
770	}
771	if (checked_type == 0)
772		return 1;
773	return (cmp->negate ? !ret : ret);
774}
775
776/* Check if we should ignore this filesystem. */
777static int ignore(struct fs_info *fs)
778{
779	const char **ip;
780	int wanted = 0;
781
782	/*
783	 * If the pass number is 0, ignore it.
784	 */
785	if (fs->passno == 0)
786		return 1;
787
788	interpret_type(fs);
789
790	/*
791	 * If a specific fstype is specified, and it doesn't match,
792	 * ignore it.
793	 */
794	if (!fs_match(fs, &fs_type_compiled)) return 1;
795
796	/* Are we ignoring this type? */
797	for(ip = ignored_types; *ip; ip++)
798		if (strcmp(fs->type, *ip) == 0) return 1;
799
800	/* Do we really really want to check this fs? */
801	for(ip = really_wanted; *ip; ip++)
802		if (strcmp(fs->type, *ip) == 0) {
803			wanted = 1;
804			break;
805		}
806
807	/* See if the <fsck.fs> program is available. */
808	if (find_fsck(fs->type) == NULL) {
809		if (wanted)
810			fprintf(stderr, _("fsck: cannot check %s: fsck.%s not found\n"),
811				fs->device, fs->type);
812		return 1;
813	}
814
815	/* We can and want to check this file system type. */
816	return 0;
817}
818
819/*
820 * Returns TRUE if a partition on the same disk is already being
821 * checked.
822 */
823static int device_already_active(char *device)
824{
825	struct fsck_instance *inst;
826	char *base;
827
828	if (force_all_parallel)
829		return 0;
830
831#ifdef BASE_MD
832	/* Don't check a soft raid disk with any other disk */
833	if (instance_list &&
834	    (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) ||
835	     !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)))
836		return 1;
837#endif
838
839	base = base_device(device);
840	/*
841	 * If we don't know the base device, assume that the device is
842	 * already active if there are any fsck instances running.
843	 */
844	if (!base)
845		return (instance_list != 0);
846	for (inst = instance_list; inst; inst = inst->next) {
847		if (!inst->base_device || !strcmp(base, inst->base_device)) {
848			free(base);
849			return 1;
850		}
851	}
852	free(base);
853	return 0;
854}
855
856/* Check all file systems, using the /etc/fstab table. */
857static int check_all(NOARGS)
858{
859	struct fs_info *fs = NULL;
860	struct fsck_instance *inst;
861	int status = EXIT_OK;
862	int not_done_yet = 1;
863	int passno = 1;
864	int pass_done;
865
866	if (verbose)
867		printf(_("Checking all file systems.\n"));
868
869	/*
870	 * Do an initial scan over the filesystem; mark filesystems
871	 * which should be ignored as done, and resolve LABEL= and
872	 * UUID= specifications to the real device.
873	 */
874	for (fs = filesys_info; fs; fs = fs->next) {
875		if (ignore(fs))
876			fs->flags |= FLAG_DONE;
877		else
878			fs->device = interpret_device(fs->device);
879	}
880
881	/*
882	 * Find and check the root filesystem.
883	 */
884	if (!parallel_root) {
885		for (fs = filesys_info; fs; fs = fs->next) {
886			if (!strcmp(fs->mountpt, "/"))
887				break;
888		}
889		if (fs) {
890			if (!skip_root && !ignore(fs)) {
891				fsck_device(fs->device, 1);
892				status |= wait_all();
893				if (status > EXIT_NONDESTRUCT)
894					return status;
895			}
896			fs->flags |= FLAG_DONE;
897		}
898	}
899
900	while (not_done_yet) {
901		not_done_yet = 0;
902		pass_done = 1;
903
904		for (fs = filesys_info; fs; fs = fs->next) {
905			if (fs->flags & FLAG_DONE)
906				continue;
907			/*
908			 * If the filesystem's pass number is higher
909			 * than the current pass number, then we don't
910			 * do it yet.
911			 */
912			if (fs->passno > passno) {
913				not_done_yet++;
914				continue;
915			}
916			/*
917			 * If a filesystem on a particular device has
918			 * already been spawned, then we need to defer
919			 * this to another pass.
920			 */
921			if (device_already_active(fs->device)) {
922				pass_done = 0;
923				continue;
924			}
925			/*
926			 * Spawn off the fsck process
927			 */
928			fsck_device(fs->device, serialize);
929			fs->flags |= FLAG_DONE;
930
931			if (serialize) {
932				pass_done = 0;
933				break; /* Only do one filesystem at a time */
934			}
935		}
936		if (verbose > 1)
937			printf(_("--waiting-- (pass %d)\n"), passno);
938		inst = wait_one();
939		if (inst) {
940			status |= inst->exit_status;
941			free_instance(inst);
942		}
943		if (pass_done) {
944			status |= wait_all();
945			if (verbose > 1)
946				printf("----------------------------------\n");
947			passno++;
948		} else
949			not_done_yet++;
950	}
951	status |= wait_all();
952	return status;
953}
954
955static void usage(NOARGS)
956{
957	fprintf(stderr,
958		_("Usage: fsck [-ACNPRTV] [-t fstype] [fs-options] filesys\n"));
959	exit(EXIT_USAGE);
960}
961
962static void PRS(int argc, char *argv[])
963{
964	int	i, j;
965	char	*arg;
966	char	options[128];
967	int	opt = 0;
968	int     opts_for_fsck = 0;
969
970	num_devices = 0;
971	num_args = 0;
972	instance_list = 0;
973
974	progname = argv[0];
975
976	for (i=1; i < argc; i++) {
977		arg = argv[i];
978		if (!arg)
979			continue;
980		if ((arg[0] == '/' && !opts_for_fsck) ||
981		    (strncmp(arg, "LABEL=", 6) == 0) ||
982		    (strncmp(arg, "UUID=", 5) == 0)) {
983			if (num_devices >= MAX_DEVICES) {
984				fprintf(stderr, _("%s: too many devices\n"),
985					progname);
986				exit(EXIT_ERROR);
987			}
988			devices[num_devices++] =
989				interpret_device(string_copy(arg));
990			continue;
991		}
992		if (arg[0] != '-' || opts_for_fsck) {
993			if (num_args >= MAX_ARGS) {
994				fprintf(stderr, _("%s: too many arguments\n"),
995					progname);
996				exit(EXIT_ERROR);
997			}
998			args[num_args++] = string_copy(arg);
999			continue;
1000		}
1001		for (j=1; arg[j]; j++) {
1002			if (opts_for_fsck) {
1003				options[++opt] = arg[j];
1004				continue;
1005			}
1006			switch (arg[j]) {
1007			case 'A':
1008				doall++;
1009				break;
1010			case 'C':
1011				progress++;
1012				break;
1013			case 'V':
1014				verbose++;
1015				break;
1016			case 'N':
1017				noexecute++;
1018				break;
1019			case 'R':
1020				skip_root++;
1021				break;
1022			case 'T':
1023				notitle++;
1024				break;
1025			case 'M':
1026				like_mount++;
1027				break;
1028			case 'P':
1029				parallel_root++;
1030				break;
1031			case 's':
1032				serialize++;
1033				break;
1034			case 't':
1035				if (arg[j+1]) {
1036					fstype = string_copy(arg+j+1);
1037					compile_fs_type(fstype, &fs_type_compiled);
1038					goto next_arg;
1039				}
1040				if ((i+1) < argc) {
1041					i++;
1042					fstype = string_copy(argv[i]);
1043					compile_fs_type(fstype, &fs_type_compiled);
1044					goto next_arg;
1045				}
1046				usage();
1047				break;
1048			case '-':
1049				opts_for_fsck++;
1050				break;
1051			case '?':
1052				usage();
1053				break;
1054			default:
1055				options[++opt] = arg[j];
1056				break;
1057			}
1058		}
1059	next_arg:
1060		if (opt) {
1061			options[0] = '-';
1062			options[++opt] = '\0';
1063			if (num_args >= MAX_ARGS) {
1064				fprintf(stderr,
1065					_("%s: too many arguments\n"),
1066					progname);
1067				exit(EXIT_ERROR);
1068			}
1069			args[num_args++] = string_copy(options);
1070			opt = 0;
1071		}
1072	}
1073	if (getenv("FSCK_FORCE_ALL_PARALLEL"))
1074		force_all_parallel++;
1075}
1076
1077int main(int argc, char *argv[])
1078{
1079	int i;
1080	int status = 0;
1081	int interactive = 0;
1082	char *oldpath = getenv("PATH");
1083	const char *fstab;
1084
1085#ifdef ENABLE_NLS
1086	setlocale(LC_MESSAGES, "");
1087	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1088	textdomain(NLS_CAT_NAME);
1089#endif
1090	PRS(argc, argv);
1091
1092	if (!notitle)
1093		printf(_("Parallelizing fsck version %s (%s)\n"),
1094			E2FSPROGS_VERSION, E2FSPROGS_DATE);
1095
1096	fstab = getenv("FSTAB_FILE");
1097	if (!fstab)
1098		fstab = _PATH_MNTTAB;
1099	load_fs_info(fstab);
1100
1101	/* Update our search path to include uncommon directories. */
1102	if (oldpath) {
1103		fsck_path = malloc (strlen (fsck_prefix_path) + 1 +
1104				    strlen (oldpath) + 1);
1105		strcpy (fsck_path, fsck_prefix_path);
1106		strcat (fsck_path, ":");
1107		strcat (fsck_path, oldpath);
1108	} else {
1109		fsck_path = string_copy(fsck_prefix_path);
1110	}
1111
1112	if ((num_devices == 1) || (serialize))
1113		interactive = 1;
1114
1115	/* If -A was specified ("check all"), do that! */
1116	if (doall)
1117		return check_all();
1118
1119	for (i = 0 ; i < num_devices; i++) {
1120		fsck_device(devices[i], interactive);
1121		if (serialize) {
1122			struct fsck_instance *inst;
1123
1124			inst = wait_one();
1125			if (inst) {
1126				status |= inst->exit_status;
1127				free_instance(inst);
1128			}
1129		}
1130	}
1131	status |= wait_all();
1132	free(fsck_path);
1133	return status;
1134}
1135