fsck.c revision a418d3ad819323f871005d253f7f9ac378e78ba5
1/*
2 * pfsck --- A generic, parallelizing front-end for the fsck program.
3 * It will automatically try to run fsck programs in parallel if the
4 * devices are on separate spindles.  It is based on the same ideas as
5 * the generic front end for fsck by David Engel and Fred van Kempen,
6 * but it has been completely rewritten from scratch to support
7 * parallel execution.
8 *
9 * Written by Theodore Ts'o, <tytso@mit.edu>
10 *
11 * Usage:	fsck [-AVRNTM] [-s] [-t fstype] [fs-options] device
12 *
13 * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
14 *   o Changed -t fstype to behave like with mount when -A (all file
15 *     systems) or -M (like mount) is specified.
16 *   o fsck looks if it can find the fsck.type program to decide
17 *     if it should ignore the fs type. This way more fsck programs
18 *     can be added without changing this front-end.
19 *   o -R flag skip root file system.
20 *
21 * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
22 * redistributed under the terms of the GNU Public License.
23 */
24
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <sys/signal.h>
28#include <sys/stat.h>
29#include <limits.h>
30#include <stdio.h>
31#include <string.h>
32#if HAVE_STDLIB_H
33#include <stdlib.h>
34#endif
35#if HAVE_ERRNO_H
36#include <errno.h>
37#endif
38#if HAVE_MNTENT_H
39#include <mntent.h>
40#endif
41#if HAVE_UNISTD_H
42#include <unistd.h>
43#endif
44#if HAVE_ERRNO_H
45#include <errno.h>
46#endif
47#include <malloc.h>
48#ifdef HAVE_GETOPT_H
49#include <getopt.h>
50#endif
51
52#include "../version.h"
53#include "fsck.h"
54
55static const char *ignored_types[] = {
56	"ignore",
57	"iso9660",
58	"nfs",
59	"proc",
60	"sw",
61	"swap",
62	NULL
63};
64
65static const char *really_wanted[] = {
66	"minix",
67	"ext2",
68	"xiafs",
69	NULL
70};
71
72#ifdef DEV_DSK_DEVICES
73static const char *base_devices[] = {
74	"/dev/dsk/hda",
75	"/dev/dsk/hdb",
76	"/dev/dsk/hdc",
77	"/dev/dsk/hdd",
78	"/dev/dsk/hd1a",
79	"/dev/dsk/hd1b",
80	"/dev/dsk/hd1c",
81	"/dev/dsk/hd1d",
82	"/dev/dsk/sda",
83	"/dev/dsk/sdb",
84	"/dev/dsk/sdc",
85	"/dev/dsk/sdd",
86	"/dev/dsk/sde",
87	"/dev/dsk/sdf",
88	"/dev/dsk/sdg",
89	NULL
90};
91#else
92static const char *base_devices[] = {
93	"/dev/hda",
94	"/dev/hdb",
95	"/dev/hdc",
96	"/dev/hdd",
97	"/dev/hd1a",
98	"/dev/hd1b",
99	"/dev/hd1c",
100	"/dev/hd1d",
101	"/dev/sda",
102	"/dev/sdb",
103	"/dev/sdc",
104	"/dev/sdd",
105	"/dev/sde",
106	"/dev/sdf",
107	"/dev/sdg",
108	NULL
109};
110#endif
111
112/*
113 * Global variables for options
114 */
115char *devices[MAX_DEVICES];
116char *args[MAX_ARGS];
117int num_devices, num_args;
118
119int verbose = 0;
120int doall = 0;
121int noexecute = 0;
122int serialize = 0;
123int skip_root = 0;
124int like_mount = 0;
125int notitle = 0;
126char *progname;
127char *fstype = NULL;
128struct fs_info *filesys_info;
129struct fsck_instance *instance_list;
130const char *fsck_prefix_path = "/sbin:/sbin/fs.d:/sbin/fs:/etc/fs:/etc";
131char *fsck_path = 0;
132static int ignore(struct fs_info *);
133
134#ifdef HAVE_STRDUP
135#ifdef _POSIX_SOURCE
136extern char *strdup(const char *s);
137#endif
138#else
139static char *strdup(const char *s)
140{
141	char	*ret;
142
143	ret = malloc(strlen(s)+1);
144	if (ret)
145		strcpy(ret, s);
146	return ret;
147}
148#endif
149
150static void free_instance(struct fsck_instance *i)
151{
152	if (i->prog)
153		free(i->prog);
154	if (i->device)
155		free(i->device);
156	free(i);
157	return;
158}
159
160/*
161 * Load the filesystem database from /etc/fstab
162 */
163static void load_fs_info(NOARGS)
164{
165#if HAVE_MNTENT_H
166	FILE *mntfile;
167	struct mntent *mp;
168	struct fs_info *fs;
169	struct fs_info *fs_last = NULL;
170	int	old_fstab = 1;
171
172	filesys_info = NULL;
173
174	/* Open the mount table. */
175	if ((mntfile = setmntent(MNTTAB, "r")) == NULL) {
176		perror(MNTTAB);
177		exit(EXIT_ERROR);
178	}
179
180	while ((mp = getmntent(mntfile)) != NULL) {
181		fs = malloc(sizeof(struct fs_info));
182		memset(fs, 0, sizeof(struct fs_info));
183		fs->device = strdup(mp->mnt_fsname);
184		fs->mountpt = strdup(mp->mnt_dir);
185		fs->type = strdup(mp->mnt_type);
186		fs->opts = strdup(mp->mnt_opts);
187		fs->freq = mp->mnt_freq;
188		fs->passno = mp->mnt_passno;
189		fs->next = NULL;
190		if (!filesys_info)
191			filesys_info = fs;
192		else
193			fs_last->next = fs;
194		fs_last = fs;
195		if (fs->passno)
196			old_fstab = 0;
197	}
198
199	(void) endmntent(mntfile);
200
201	if (old_fstab) {
202		fprintf(stderr, "\007\007\007"
203	"WARNING: Your /etc/fstab does not contain the fsck passno\n");
204		fprintf(stderr,
205	"	field.  I will kludge around things for you, but you\n");
206		fprintf(stderr,
207	"	should fix your /etc/fstab file as soon as you can.\n\n");
208
209		for (fs = filesys_info; fs; fs = fs->next) {
210			fs->passno = 1;
211		}
212	}
213#else
214	filesys_info = NULL;
215#endif /* HAVE_MNTENT_H */
216}
217
218/* Lookup filesys in /etc/fstab and return the corresponding entry. */
219static struct fs_info *lookup(char *filesys)
220{
221	struct fs_info *fs;
222
223	/* No filesys name given. */
224	if (filesys == NULL)
225		return NULL;
226
227	for (fs = filesys_info; fs; fs = fs->next) {
228		if (!strcmp(filesys, fs->device) ||
229		    !strcmp(filesys, fs->mountpt))
230			break;
231	}
232
233	return fs;
234}
235
236/* Find fsck program for a given fs type. */
237static char *find_fsck(char *type)
238{
239  char *s;
240  const char *tpl;
241  static char prog[256];
242  char *p = strdup(fsck_path);
243  struct stat st;
244
245  /* Are we looking for a program or just a type? */
246  tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
247
248  for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
249	sprintf(prog, tpl, s, type);
250	if (stat(prog, &st) == 0) break;
251  }
252  free(p);
253  return(s ? prog : NULL);
254}
255
256/*
257 * Execute a particular fsck program, and link it into the list of
258 * child processes we are waiting for.
259 */
260static int execute(char *prog, char *device)
261{
262	char *s, *argv[80];
263	int  argc, i;
264	struct fsck_instance *inst;
265	pid_t	pid;
266
267	argv[0] = strdup(prog);
268	argc = 1;
269
270	for (i=0; i <num_args; i++)
271		argv[argc++] = strdup(args[i]);
272
273	argv[argc++] = strdup(device);
274	argv[argc] = 0;
275
276	s = find_fsck(prog);
277	if (s == NULL) {
278		fprintf(stderr, "fsck: %s: not found\n", prog);
279		return ENOENT;
280	}
281
282	if (verbose || noexecute) {
283		printf("[%s] ", s);
284		for (i=0; i < argc; i++)
285			printf("%s ", argv[i]);
286		printf("\n");
287	}
288	if (noexecute)
289		return 0;
290
291	/* Fork and execute the correct program. */
292	if ((pid = fork()) < 0) {
293		perror("fork");
294		return errno;
295	} else if (pid == 0) {
296		(void) execv(s, argv);
297		perror(argv[0]);
298		exit(EXIT_ERROR);
299	}
300	inst = malloc(sizeof(struct fsck_instance));
301	if (!inst)
302		return ENOMEM;
303	memset(inst, 0, sizeof(struct fsck_instance));
304	inst->pid = pid;
305	inst->prog = strdup(prog);
306	inst->device = strdup(device);
307	inst->next = instance_list;
308	instance_list = inst;
309
310	return 0;
311}
312
313/*
314 * Wait for one child process to exit; when it does, unlink it from
315 * the list of executing child processes, and return it.
316 */
317static struct fsck_instance *wait_one(NOARGS)
318{
319	int	status;
320	int	sig;
321	struct fsck_instance *inst, *prev;
322	pid_t	pid;
323
324	if (!instance_list)
325		return NULL;
326
327retry:
328	pid = wait(&status);
329	if (pid < 0) {
330		if ((errno == EINTR) || (errno == EAGAIN))
331			goto retry;
332		if (errno == ECHILD) {
333			fprintf(stderr,
334				"%s: wait: No more child process?!?\n",
335				progname);
336			return NULL;
337		}
338		perror("wait");
339		goto retry;
340	}
341	for (prev = 0, inst = instance_list;
342	     inst;
343	     prev = inst, inst = inst->next) {
344		if (inst->pid == pid)
345			break;
346	}
347	if (!inst) {
348		printf("Unexpected child process %d, status = 0x%x\n",
349		       pid, status);
350		goto retry;
351	}
352	if (WIFEXITED(status))
353		status = WEXITSTATUS(status);
354	else if (WIFSIGNALED(status)) {
355		sig = WTERMSIG(status);
356		if (sig == SIGINT) {
357			status = EXIT_UNCORRECTED;
358		} else {
359			printf("Warning... %s for device %s exited "
360			       "with signal %d.\n",
361			       inst->prog, inst->device, sig);
362			status = EXIT_ERROR;
363		}
364	} else {
365		printf("%s %s: status is %x, should never happen.\n",
366		       inst->prog, inst->device, status);
367		status = EXIT_ERROR;
368	}
369	inst->exit_status = status;
370	if (prev)
371		prev->next = inst->next;
372	else
373		instance_list = inst->next;
374	return inst;
375}
376
377/*
378 * Wait until all executing child processes have exited; return the
379 * logical OR of all of their exit code values.
380 */
381static int wait_all(NOARGS)
382{
383	struct fsck_instance *inst;
384	int	global_status = 0;
385
386	while (instance_list) {
387		inst = wait_one();
388		if (!inst)
389			break;
390		global_status |= inst->exit_status;
391		free_instance(inst);
392	}
393	return global_status;
394}
395
396/*
397 * Run the fsck program on a particular device
398 *
399 * If the type is specified using -t, and it isn't prefixed with "no"
400 * (as in "noext2") and only one filesystem type is specified, then
401 * use that type regardless of what is specified in /etc/fstab.
402 *
403 * If the type isn't specified by the user, then use either the type
404 * specified in /etc/fstab, or DEFAULT_FSTYPE.
405 */
406static void fsck_device(char *device)
407{
408	const char	*type = 0;
409	struct fs_info *fsent;
410	int retval;
411	char prog[80];
412
413	if (fstype && strncmp(type, "no", 2) && !strchr(type, ','))
414		type = fstype;
415
416	if ((fsent = lookup(device))) {
417		device = fsent->device;
418		if (!type)
419			type = fsent->type;
420	}
421	if (!type)
422		type = DEFAULT_FSTYPE;
423
424	sprintf(prog, "fsck.%s", type);
425	retval = execute(prog, device);
426	if (retval) {
427		fprintf(stderr, "%s: Error %d while executing %s for %s\n",
428			progname, retval, prog, device);
429	}
430}
431
432/* See if filesystem type matches the list. */
433static int fs_match(char *type, char *fs_type)
434{
435  int ret = 0, negate = 0;
436  char list[128];
437  char *s;
438
439  if (!fs_type) return(1);
440
441  if (strncmp(fs_type, "no", 2) == 0) {
442	fs_type += 2;
443	negate = 1;
444  }
445  strcpy(list, fs_type);
446  s = strtok(list, ",");
447  while(s) {
448	if (strcmp(s, type) == 0) {
449		ret = 1;
450		break;
451	}
452	s = strtok(NULL, ",");
453  }
454  return(negate ? !ret : ret);
455}
456
457
458/* Check if we should ignore this filesystem. */
459static int ignore(struct fs_info *fs)
460{
461	const char *cp;
462	const char **ip;
463	int wanted = 0;
464
465	/*
466	 * If the pass number is 0, ignore it.
467	 */
468	if (fs->passno == 0)
469		return 1;
470
471	/*
472	 * If a specific fstype is specified, and it doesn't match,
473	 * ignore it.
474	 */
475	if (!fs_match(fs->type, fstype)) return 1;
476
477	/* Noauto never matches. */
478	for (cp = strtok(fs->opts, ","); cp != NULL; cp = strtok(NULL, ",")) {
479		if (!strcmp(cp, "noauto"))
480			return 1;
481	}
482
483	/* Are we ignoring this type? */
484	for(ip = ignored_types; *ip; ip++)
485		if (strcmp(fs->type, *ip) == 0) return(1);
486
487	/* Do we really really want to check this fs? */
488	for(ip = really_wanted; *ip; ip++)
489		if (strcmp(fs->type, *ip) == 0) {
490			wanted = 1;
491			break;
492		}
493
494	/* See if the <fsck.fs> program is available. */
495	if (find_fsck(fs->type) == NULL) {
496		if (wanted)
497			fprintf(stderr, "fsck: cannot check %s: fsck.%s not found\n",
498				fs->device, fs->type);
499		return(1);
500	}
501
502	/* We can and want to check this file system type. */
503	return 0;
504}
505
506/*
507 * Return the "base device" given a particular device; this is used to
508 * assure that we only fsck one partition on a particular drive at any
509 * one time.  Otherwise, the disk heads will be seeking all over the
510 * place.
511 */
512static const char *base_device(char *device)
513{
514	const char **base;
515
516	for (base = base_devices; *base; base++) {
517		if (!strncmp(*base, device, strlen(*base)))
518			return *base;
519	}
520	return device;
521}
522
523/*
524 * Returns TRUE if a partition on the same disk is already being
525 * checked.
526 */
527static int device_already_active(char *device)
528{
529	struct fsck_instance *inst;
530	const char *base;
531
532	base = base_device(device);
533
534	for (inst = instance_list; inst; inst = inst->next) {
535		if (!strcmp(base, base_device(inst->device)))
536			return 1;
537	}
538
539	return 0;
540}
541
542/* Check all file systems, using the /etc/fstab table. */
543static int check_all(NOARGS)
544{
545	struct fs_info *fs;
546	struct fsck_instance *inst;
547	int status = EXIT_OK;
548	int not_done_yet = 1;
549	int passno = 0;
550	int pass_done;
551
552	if (verbose)
553		printf("Checking all file systems.\n");
554
555	/*
556	 * Find and check the root filesystem first.
557	 */
558	for (fs = filesys_info; fs; fs = fs->next) {
559		if (!strcmp(fs->mountpt, "/"))
560			break;
561	}
562	if (fs && !skip_root && !ignore(fs)) {
563		fsck_device(fs->device);
564		fs->flags |= FLAG_DONE;
565		status |= wait_all();
566		if (status > EXIT_NONDESTRUCT)
567			return status;
568	}
569	if (fs) fs->flags |= FLAG_DONE;
570
571	/*
572	 * Mark filesystems that should be ignored as done.
573	 */
574	for (fs = filesys_info; fs; fs = fs->next) {
575		if (ignore(fs))
576			fs->flags |= FLAG_DONE;
577	}
578
579	while (not_done_yet) {
580		not_done_yet = 0;
581		pass_done = 1;
582
583		for (fs = filesys_info; fs; fs = fs->next) {
584			if (fs->flags & FLAG_DONE)
585				continue;
586			/*
587			 * If the filesystem's pass number is higher
588			 * than the current pass number, then we don't
589			 * do it yet.
590			 */
591			if (fs->passno > passno) {
592				not_done_yet++;
593				continue;
594			}
595			/*
596			 * If a filesystem on a particular device has
597			 * already been spawned, then we need to defer
598			 * this to another pass.
599			 */
600			if (device_already_active(fs->device)) {
601				pass_done = 0;
602				continue;
603			}
604			/*
605			 * Spawn off the fsck process
606			 */
607			fsck_device(fs->device);
608			fs->flags |= FLAG_DONE;
609
610			if (serialize)
611				break; /* Only do one filesystem at a time */
612		}
613		inst = wait_one();
614		if (inst) {
615			status |= inst->exit_status;
616			free_instance(inst);
617		}
618		if (pass_done) {
619			status |= wait_all();
620			if (verbose)
621				printf("----------------------------------\n");
622			passno++;
623		} else
624			not_done_yet++;
625	}
626	status |= wait_all();
627	return status;
628}
629
630static void usage(NOARGS)
631{
632	fprintf(stderr,
633		"Usage: fsck [-AV] [-t fstype] [fs-options] filesys\n");
634	exit(EXIT_USAGE);
635}
636
637static void PRS(int argc, char *argv[])
638{
639	int	i, j;
640	char	*arg;
641	char	options[128];
642	int	opt = 0;
643	int     opts_for_fsck = 0;
644
645	num_devices = 0;
646	num_args = 0;
647	instance_list = 0;
648
649	progname = argv[0];
650
651	load_fs_info();
652
653	for (i=1; i < argc; i++) {
654		arg = argv[i];
655		if (!arg)
656			continue;
657		if (arg[0] == '/') {
658			if (num_devices >= MAX_DEVICES) {
659				fprintf(stderr, "%s: too many devices\n",
660					progname);
661				exit(1);
662			}
663			devices[num_devices++] = strdup(arg);
664			continue;
665		}
666		if (arg[0] != '-') {
667			if (num_args >= MAX_ARGS) {
668				fprintf(stderr, "%s: too many arguments\n",
669					progname);
670				exit(1);
671			}
672			args[num_args++] = strdup(arg);
673			continue;
674		}
675		for (j=1; arg[j]; j++) {
676			if (opts_for_fsck) {
677				options[++opt] = arg[j];
678				continue;
679			}
680			switch (arg[j]) {
681			case 'A':
682				doall++;
683				break;
684			case 'V':
685				verbose++;
686				break;
687			case 'N':
688				noexecute++;
689				break;
690			case 'R':
691				skip_root++;
692				break;
693			case 'T':
694				notitle++;
695				break;
696			case 'M':
697				like_mount++;
698				break;
699			case 's':
700				serialize++;
701				break;
702			case 't':
703				if (arg[j+1]) {
704					fstype = strdup(arg+j+1);
705					goto next_arg;
706				}
707				if ((i+1) < argc) {
708					i++;
709					fstype = strdup(argv[i]);
710					goto next_arg;
711				}
712				usage();
713				break;
714			case '-':
715				opts_for_fsck++;
716				break;
717			default:
718				options[++opt] = arg[j];
719				break;
720			}
721		}
722	next_arg:
723		if (opt) {
724			options[0] = '-';
725			options[++opt] = '\0';
726			if (num_args >= MAX_ARGS) {
727				fprintf(stderr,
728					"%s: too many arguments\n",
729					progname);
730				exit(1);
731			}
732			args[num_args++] = strdup(options);
733			opt = 0;
734		}
735	}
736}
737
738int main(int argc, char *argv[])
739{
740	int i;
741	int status = 0;
742	char *oldpath = getenv("PATH");
743
744	PRS(argc, argv);
745
746	if (!notitle)
747		printf("Parallelizing fsck version %s (%s)\n",
748			E2FSPROGS_VERSION, E2FSPROGS_DATE);
749
750	/* Update our search path to include uncommon directories. */
751	if (oldpath) {
752		fsck_path = malloc (strlen (fsck_prefix_path) + 1 +
753				    strlen (oldpath) + 1);
754		strcpy (fsck_path, fsck_prefix_path);
755		strcat (fsck_path, ":");
756		strcat (fsck_path, oldpath);
757	} else {
758		fsck_path = strdup(oldpath);
759	}
760
761	/* If -A was specified ("check all"), do that! */
762	if (doall)
763		return check_all();
764
765	for (i = 0 ; i < num_devices; i++) {
766		fsck_device(devices[i]);
767		if (serialize) {
768			struct fsck_instance *inst;
769
770			inst = wait_one();
771			if (inst) {
772				status |= inst->exit_status;
773				free_instance(inst);
774			}
775		}
776	}
777	status |= wait_all();
778	free(fsck_path);
779	return status;
780}
781