Whamcloud - gitweb
1d92c54c4cecf87ff77a4458b78ce5b023b70533
[tools/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                                 pass_done = 0;
612                                 break; /* Only do one filesystem at a time */
613
614                         }
615                 }
616                 inst = wait_one();
617                 if (inst) {
618                         status |= inst->exit_status;
619                         free_instance(inst);
620                 }
621                 if (pass_done) {
622                         status |= wait_all();
623                         if (verbose) 
624                                 printf("----------------------------------\n");
625                         passno++;
626                 } else
627                         not_done_yet++;
628         }
629         status |= wait_all();
630         return status;
631 }
632
633 static void usage(NOARGS)
634 {
635         fprintf(stderr,
636                 "Usage: fsck [-AV] [-t fstype] [fs-options] filesys\n");
637         exit(EXIT_USAGE);
638 }
639
640 static void PRS(int argc, char *argv[])
641 {
642         int     i, j;
643         char    *arg;
644         char    options[128];
645         int     opt = 0;
646         int     opts_for_fsck = 0;
647         
648         num_devices = 0;
649         num_args = 0;
650         instance_list = 0;
651
652         progname = argv[0];
653
654         load_fs_info();
655
656         for (i=1; i < argc; i++) {
657                 arg = argv[i];
658                 if (!arg)
659                         continue;
660                 if (arg[0] == '/') {
661                         if (num_devices >= MAX_DEVICES) {
662                                 fprintf(stderr, "%s: too many devices\n",
663                                         progname);
664                                 exit(1);
665                         }
666                         devices[num_devices++] = strdup(arg);
667                         continue;
668                 }
669                 if (arg[0] != '-') {
670                         if (num_args >= MAX_ARGS) {
671                                 fprintf(stderr, "%s: too many arguments\n",
672                                         progname);
673                                 exit(1);
674                         }
675                         args[num_args++] = strdup(arg);
676                         continue;
677                 }
678                 for (j=1; arg[j]; j++) {
679                         if (opts_for_fsck) {
680                                 options[++opt] = arg[j];
681                                 continue;
682                         }
683                         switch (arg[j]) {
684                         case 'A':
685                                 doall++;
686                                 break;
687                         case 'V':
688                                 verbose++;
689                                 break;
690                         case 'N':
691                                 noexecute++;
692                                 break;
693                         case 'R':
694                                 skip_root++;
695                                 break;
696                         case 'T':
697                                 notitle++;
698                                 break;
699                         case 'M':
700                                 like_mount++;
701                                 break;
702                         case 's':
703                                 serialize++;
704                                 break;
705                         case 't':
706                                 if (arg[j+1]) {
707                                         fstype = strdup(arg+j+1);
708                                         goto next_arg;
709                                 }
710                                 if ((i+1) < argc) {
711                                         i++;
712                                         fstype = strdup(argv[i]);
713                                         goto next_arg;
714                                 }
715                                 usage();
716                                 break;
717                         case '-':
718                                 opts_for_fsck++;
719                                 break;
720                         default:
721                                 options[++opt] = arg[j];
722                                 break;
723                         }
724                 }
725         next_arg:
726                 if (opt) {
727                         options[0] = '-';
728                         options[++opt] = '\0';
729                         if (num_args >= MAX_ARGS) {
730                                 fprintf(stderr,
731                                         "%s: too many arguments\n",
732                                         progname);
733                                 exit(1);
734                         }
735                         args[num_args++] = strdup(options);
736                         opt = 0;
737                 }
738         }
739 }
740
741 int main(int argc, char *argv[])
742 {
743         int i;
744         int status = 0;
745         char *oldpath = getenv("PATH");
746
747         PRS(argc, argv);
748
749         if (!notitle)
750                 printf("Parallelizing fsck version %s (%s)\n",
751                         E2FSPROGS_VERSION, E2FSPROGS_DATE);
752
753         /* Update our search path to include uncommon directories. */
754         if (oldpath) {
755                 fsck_path = malloc (strlen (fsck_prefix_path) + 1 +
756                                     strlen (oldpath) + 1);
757                 strcpy (fsck_path, fsck_prefix_path);
758                 strcat (fsck_path, ":");
759                 strcat (fsck_path, oldpath);
760         } else {
761                 fsck_path = strdup(oldpath);
762         }
763         
764         /* If -A was specified ("check all"), do that! */
765         if (doall)
766                 return check_all();
767
768         for (i = 0 ; i < num_devices; i++) {
769                 fsck_device(devices[i]);
770                 if (serialize) {
771                         struct fsck_instance *inst;
772
773                         inst = wait_one();
774                         if (inst) {
775                                 status |= inst->exit_status;
776                                 free_instance(inst);
777                         }
778                 }
779         }
780         status |= wait_all();
781         free(fsck_path);
782         return status;
783 }