]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - misc/fsck.c
Many files:
[thirdparty/e2fsprogs.git] / misc / fsck.c
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
55 static const char *ignored_types[] = {
56 "ignore",
57 "iso9660",
58 "nfs",
59 "proc",
60 "sw",
61 "swap",
62 NULL
63 };
64
65 static const char *really_wanted[] = {
66 "minix",
67 "ext2",
68 "xiafs",
69 NULL
70 };
71
72 #ifdef DEV_DSK_DEVICES
73 static 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
92 static 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 */
115 char *devices[MAX_DEVICES];
116 char *args[MAX_ARGS];
117 int num_devices, num_args;
118
119 int verbose = 0;
120 int doall = 0;
121 int noexecute = 0;
122 int serialize = 0;
123 int skip_root = 0;
124 int like_mount = 0;
125 int notitle = 0;
126 char *progname;
127 char *fstype = NULL;
128 struct fs_info *filesys_info;
129 struct fsck_instance *instance_list;
130 const char *fsck_prefix_path = "/sbin:/sbin/fs.d:/sbin/fs:/etc/fs:/etc";
131 char *fsck_path = 0;
132 static int ignore(struct fs_info *);
133
134 #ifdef HAVE_STRDUP
135 #ifdef _POSIX_SOURCE
136 extern char *strdup(const char *s);
137 #endif
138 #else
139 static 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
150 static 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 */
163 static 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. */
219 static 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. */
237 static 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 */
260 static 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 */
317 static 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
327 retry:
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 */
381 static 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 */
406 static 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(fstype, "no", 2) && !strchr(fstype, ','))
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. */
433 static 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. */
459 static 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 */
512 static 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 */
527 static 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. */
543 static 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
630 static void usage(NOARGS)
631 {
632 fprintf(stderr,
633 "Usage: fsck [-AV] [-t fstype] [fs-options] filesys\n");
634 exit(EXIT_USAGE);
635 }
636
637 static 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
738 int 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 }