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