Whamcloud - gitweb
Many files:
[tools/e2fsprogs.git] / e2fsck / e2fsck.c
1 /*
2  * e2fsck.c - a consistency checker for the new extended file system.
3  * 
4  * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
5  * redistributed under the terms of the GNU Public License.
6  */
7
8 /* Usage: e2fsck [-dfpnsvy] device
9  *      -d -- debugging this program
10  *      -f -- check the fs even if it is marked valid
11  *      -p -- "preen" the filesystem
12  *      -n -- open the filesystem r/o mode; never try to fix problems
13  *      -v -- verbose (tells how many files)
14  *      -y -- always answer yes to questions
15  *
16  * The device may be a block device or a image of one, but this isn't
17  * enforced (but it's not much fun on a character device :-). 
18  */
19
20 #include <string.h>
21 #include <fcntl.h>
22 #include <ctype.h>
23 #include <termios.h>
24 #include <time.h>
25 #include <getopt.h>
26 #include <unistd.h>
27 #include <mntent.h>
28 #include <sys/ioctl.h>
29 #include <malloc.h>
30
31 #include "et/com_err.h"
32 #include "e2fsck.h"
33 #include "../version.h"
34
35 extern int isatty(int);
36
37 const char * program_name = "e2fsck";
38 const char * device_name = NULL;
39
40 /* Command line options */
41 int nflag = 0;
42 int yflag = 0;
43 int tflag = 0;                  /* Do timing */
44 int cflag = 0;                  /* check disk */
45 int preen = 0;
46 int rwflag = 1;
47 int inode_buffer_blocks = 0;
48 blk_t superblock;
49 int blocksize = 0;
50 int verbose = 0;
51 int list = 0;
52 int debug = 0;
53 int force = 0;
54 static int show_version_only = 0;
55
56 static int replace_bad_blocks = 0;
57 static char *bad_blocks_file = 0;
58
59 static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
60
61 struct resource_track   global_rtrack;
62
63 static int root_filesystem = 0;
64 static int read_only_root = 0;
65
66 static void usage(NOARGS)
67 {
68         fprintf(stderr,
69                 "Usage: %s [-panyrdfvtFV] [-b superblock] [-B blocksize]\n"
70                 "\t\tdevice\n", program_name);
71         exit(FSCK_USAGE);
72 }
73
74 static void show_stats(ext2_filsys fs)
75 {
76         int inodes, inodes_used, blocks, blocks_used;
77         int dir_links;
78         int num_files, num_links;
79
80         dir_links = 2 * fs_directory_count - 1;
81         num_files = fs_total_count - dir_links;
82         num_links = fs_links_count - dir_links;
83         inodes = fs->super->s_inodes_count;
84         inodes_used = (fs->super->s_inodes_count -
85                        fs->super->s_free_inodes_count);
86         blocks = fs->super->s_blocks_count;
87         blocks_used = (fs->super->s_blocks_count -
88                        fs->super->s_free_blocks_count);
89         
90         if (!verbose) {
91                 printf("%s: %d/%d files, %d/%d blocks\n", device_name,
92                        inodes_used, inodes, blocks_used, blocks);
93                 return;
94         }
95         printf ("\n%6d inode%s used (%d%%)\n", inodes_used,
96                 (inodes_used != 1) ? "s" : "",
97                 100 * inodes_used / inodes);
98         printf ("%6d block%s used (%d%%)\n"
99                 "%6d bad block%s\n", blocks_used,
100                 (blocks_used != 1) ? "s" : "",
101                 100 * blocks_used / blocks, fs_badblocks_count,
102                 fs_badblocks_count != 1 ? "s" : "");
103         printf ("\n%6d regular file%s\n"
104                 "%6d director%s\n"
105                 "%6d character device file%s\n"
106                 "%6d block device file%s\n"
107                 "%6d fifo%s\n"
108                 "%6d link%s\n"
109                 "%6d symbolic link%s (%d fast symbolic link%s)\n"
110                 "%6d socket%s\n"
111                 "------\n"
112                 "%6d file%s\n",
113                 fs_regular_count, (fs_regular_count != 1) ? "s" : "",
114                 fs_directory_count, (fs_directory_count != 1) ? "ies" : "y",
115                 fs_chardev_count, (fs_chardev_count != 1) ? "s" : "",
116                 fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "",
117                 fs_fifo_count, (fs_fifo_count != 1) ? "s" : "",
118                 fs_links_count - dir_links,
119                 ((fs_links_count - dir_links) != 1) ? "s" : "",
120                 fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "",
121                 fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "",
122                 fs_sockets_count, (fs_sockets_count != 1) ? "s" : "",
123                 fs_total_count - dir_links,
124                 ((fs_total_count - dir_links) != 1) ? "s" : "");
125 }
126
127 static void check_mount(NOARGS)
128 {
129         FILE * f;
130         struct mntent * mnt;
131         int cont;
132         int fd;
133
134         if ((f = setmntent (MOUNTED, "r")) == NULL)
135                 return;
136         while ((mnt = getmntent (f)) != NULL)
137                 if (strcmp (device_name, mnt->mnt_fsname) == 0)
138                         break;
139         endmntent (f);
140         if (!mnt)
141                 return;
142
143         if  (!strcmp(mnt->mnt_dir, "/"))
144                 root_filesystem = 1;
145
146         /*
147          * If the root is mounted read-only, then /etc/mtab is
148          * probably not correct; so we won't issue a warning based on
149          * it.
150          */
151         fd = open(MOUNTED, O_RDWR);
152         if (fd < 0) {
153                 if (errno == EROFS) {
154                         read_only_root = 1;
155                         return;
156                 }
157         } else
158                 close(fd);
159         
160         if (!rwflag) {
161                 printf("Warning!  %s is mounted.\n", device_name);
162                 return;
163         }
164
165         printf ("%s is mounted.  ", device_name);
166         if (isatty (0) && isatty (1))
167                 cont = ask_yn("Do you really want to continue", -1);
168         else
169                 cont = 0;
170         if (!cont) {
171                 printf ("check aborted.\n");
172                 exit (0);
173         }
174         return;
175 }
176
177 static void sync_disks(NOARGS)
178 {
179         sync();
180         sync();
181         sleep(1);
182         sync();
183 }
184
185 static void check_super_block(ext2_filsys fs)
186 {
187         blk_t   first_block, last_block;
188         int     blocks_per_group = fs->super->s_blocks_per_group;
189         int     i;
190
191         first_block =  fs->super->s_first_data_block;
192         last_block = first_block + blocks_per_group;
193
194         for (i = 0; i < fs->group_desc_count; i++) {
195                 if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
196                     (fs->group_desc[i].bg_block_bitmap >= last_block)) {
197                         printf("Block bitmap %ld for group %d not in group.\n",
198                                fs->group_desc[i].bg_block_bitmap, i);
199                         fatal_error(0);
200                 }
201                 if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
202                     (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
203                         printf("Inode bitmap %ld for group %d not in group.\n",
204                                fs->group_desc[i].bg_inode_bitmap, i);
205                         fatal_error(0);
206                 }
207                 if ((fs->group_desc[i].bg_inode_table < first_block) ||
208                     ((fs->group_desc[i].bg_inode_table +
209                       fs->inode_blocks_per_group - 1) >= last_block)) {
210                         printf("Inode table %ld for group %d not in group.\n",
211                                fs->group_desc[i].bg_inode_table, i);
212                         fatal_error(0);
213                 }
214                 first_block += fs->super->s_blocks_per_group;
215                 last_block += fs->super->s_blocks_per_group;
216         }
217         return;
218 }
219
220 /*
221  * This routine checks to see if a filesystem can be skipped; if so,
222  * it will exit with E2FSCK_OK.  Under some conditions it will print a
223  * message explaining why a check is being forced.
224  */
225 static void check_if_skip(ext2_filsys fs)
226 {
227         const char *reason = NULL;
228         
229         if (force || bad_blocks_file || cflag)
230                 return;
231         
232         if (fs->super->s_state & EXT2_ERROR_FS)
233                 reason = "contains a file system with errors";
234         else if (fs->super->s_mnt_count >=
235                  (unsigned) fs->super->s_max_mnt_count)
236                 reason = "has reached maximal mount count";
237         else if (fs->super->s_checkinterval &&
238                  time(0) >= (fs->super->s_lastcheck +
239                              fs->super->s_checkinterval))
240                 reason = "has gone too long without being checked";
241         if (reason) {
242                 printf("%s %s, check forced.\n", device_name, reason);
243                 return;
244         }
245         if (fs->super->s_state & EXT2_VALID_FS) {
246                 printf("%s is clean, no check.\n", device_name);
247                 exit(FSCK_OK);
248         }
249 }       
250
251 static void PRS(int argc, char *argv[])
252 {
253         int flush = 0;
254         char c;
255 #ifdef MTRACE
256         extern void *mallwatch;
257 #endif
258         char *oldpath, newpath[PATH_MAX];
259
260         /* Update our PATH to include /sbin  */
261         strcpy(newpath, "PATH=/sbin:");
262         if ((oldpath = getenv("PATH")) != NULL)
263                 strcat(newpath, oldpath);
264         putenv(newpath);
265
266         setbuf(stdout, NULL);
267         setbuf(stderr, NULL);
268         initialize_ext2_error_table();
269         
270         if (argc && *argv)
271                 program_name = *argv;
272         while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:")) != EOF)
273                 switch (c) {
274                 case 'p':
275                 case 'a':
276                         preen = 1;
277                         yflag = nflag = 0;
278                         break;
279                 case 'n':
280                         nflag = 1;
281                         preen = yflag = 0;
282                         break;
283                 case 'y':
284                         yflag = 1;
285                         preen = nflag = 0;
286                         break;
287                 case 't':
288                         tflag++;
289                         break;
290                 case 'c':
291                         cflag++;
292                         break;
293                 case 'r':
294                         /* What we do by default, anyway! */
295                         break;
296                 case 'b':
297                         superblock = atoi(optarg);
298                         break;
299                 case 'B':
300                         blocksize = atoi(optarg);
301                         break;
302                 case 'I':
303                         inode_buffer_blocks = atoi(optarg);
304                         break;
305                 case 'P':
306                         process_inode_size = atoi(optarg);
307                         break;
308                 case 'L':
309                         replace_bad_blocks++;
310                 case 'l':
311                         bad_blocks_file = malloc(strlen(optarg)+1);
312                         if (!bad_blocks_file)
313                                 fatal_error("Couldn't malloc bad_blocks_file");
314                         strcpy(bad_blocks_file, optarg);
315                         break;
316                 case 'd':
317                         debug = 1;
318                         break;
319                 case 'f':
320                         force = 1;
321                         break;
322                 case 'F':
323                         flush = 1;
324                         break;
325                 case 'v':
326                         verbose = 1;
327                         break;
328                 case 'V':
329                         show_version_only = 1;
330                         break;
331 #ifdef MTRACE
332                 case 'M':
333                         mallwatch = (void *) strtol(optarg, NULL, 0);
334                         break;
335 #endif
336                 default:
337                         usage ();
338                 }
339         if (show_version_only)
340                 return;
341         if (optind != argc - 1)
342                 usage ();
343         if (nflag && !bad_blocks_file && !cflag)
344                 rwflag = 0;
345         device_name = argv[optind];
346         if (flush) {
347                 int     fd = open(device_name, O_RDONLY, 0);
348
349                 if (fd < 0) {
350                         com_err("open", errno, "while opening %s for flushing",
351                                 device_name);
352                         exit(FSCK_ERROR);
353                 }
354                 if (ioctl(fd, BLKFLSBUF, 0) < 0) {
355                         com_err("BLKFLSBUF", errno, "while trying to flush %s",
356                                 device_name);
357                         exit(FSCK_ERROR);
358                 }
359                 close(fd);
360         }
361 }
362                                         
363 int main (int argc, char *argv[])
364 {
365         errcode_t       retval = 0;
366         int             exit_value = FSCK_OK;
367         int             i;
368         ext2_filsys     fs;
369         
370 #ifdef MTRACE
371         mtrace();
372 #endif
373 #ifdef MCHECK
374         mcheck(0);
375 #endif
376         
377         init_resource_track(&global_rtrack);
378
379         PRS(argc, argv);
380
381         if (!preen)
382                 fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
383                          E2FSPROGS_VERSION, E2FSPROGS_DATE,
384                          EXT2FS_VERSION, EXT2FS_DATE);
385
386         if (show_version_only)
387                 exit(0);
388         
389         check_mount();
390         
391         if (!preen && !nflag && !yflag) {
392                 if (!isatty (0) || !isatty (1))
393                         die ("need terminal for interactive repairs");
394         }
395         sync_disks();
396         if (superblock && blocksize) {
397                 retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0,
398                                      superblock, blocksize, unix_io_manager,
399                                      &fs);
400         } else if (superblock) {
401                 for (i=0; possible_block_sizes[i]; i++) {
402                         retval = ext2fs_open(device_name,
403                                              rwflag ? EXT2_FLAG_RW : 0,
404                                              superblock,
405                                              possible_block_sizes[i],
406                                              unix_io_manager, &fs);
407                         if (!retval)
408                                 break;
409                 }
410         } else 
411                 retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0,
412                                      0, 0, unix_io_manager, &fs);
413         if (retval) {
414                 com_err(program_name, retval, "while trying to open %s",
415                         device_name);
416                 printf("Couldn't find valid filesystem superblock.\n");
417                 fatal_error(0);
418         }
419         /*
420          * If the user specified a specific superblock, presumably the
421          * master superblock has been trashed.  So we mark the
422          * superblock as dirty, so it can be written out.
423          */
424         if (superblock && rwflag)
425                 ext2fs_mark_super_dirty(fs);
426
427         ehandler_init(fs->io);
428
429         check_super_block(fs);
430         check_if_skip(fs);
431         if (bad_blocks_file)
432                 read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks);
433         else if (cflag)
434                 test_disk(fs);
435
436         /*
437          * Mark the system as valid, 'til proven otherwise
438          */
439         ext2fs_mark_valid(fs);
440         
441         pass1(fs);
442         pass2(fs);
443         pass3(fs);
444         pass4(fs);
445         pass5(fs);
446
447 #ifdef MTRACE
448         mtrace_print("Cleanup");
449 #endif
450         if (ext2fs_test_changed(fs)) {
451                 exit_value = FSCK_NONDESTRUCT;
452                 if (!preen)
453                         printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
454                                device_name);
455                 if (root_filesystem && !read_only_root) {
456                         printf("%s: ***** REBOOT LINUX *****\n", device_name);
457                         exit_value = FSCK_REBOOT;
458                 }
459         }
460         if (!ext2fs_test_valid(fs))
461                 exit_value = FSCK_UNCORRECTED;
462         if (rwflag) {
463                 if (ext2fs_test_valid(fs))
464                         fs->super->s_state = EXT2_VALID_FS;
465                 else
466                         fs->super->s_state &= ~EXT2_VALID_FS;
467                 fs->super->s_mnt_count = 0;
468                 fs->super->s_lastcheck = time(NULL);
469                 ext2fs_mark_super_dirty(fs);
470         }
471         show_stats(fs);
472
473         write_bitmaps(fs);
474         ext2fs_close(fs);
475         sync_disks();
476         
477         if (tflag)
478                 print_resource_track(&global_rtrack);
479         
480         return exit_value;
481 }