blob: c40a4426c706858fe3f0749d1d667bd023deb278 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * e2fsck.c - a consistency checker for the new extended file system.
3 *
Theodore Ts'o21c84b71997-04-29 16:15:03 +00004 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
Theodore Ts'o3839e651997-04-26 13:21:57 +000010 */
11
12/* Usage: e2fsck [-dfpnsvy] device
13 * -d -- debugging this program
14 * -f -- check the fs even if it is marked valid
15 * -p -- "preen" the filesystem
16 * -n -- open the filesystem r/o mode; never try to fix problems
17 * -v -- verbose (tells how many files)
18 * -y -- always answer yes to questions
19 *
20 * The device may be a block device or a image of one, but this isn't
21 * enforced (but it's not much fun on a character device :-).
22 */
23
Theodore Ts'o1e3472c1997-04-29 14:53:37 +000024#include <stdio.h>
25#ifdef HAVE_STDLIB_H
26#include <stdlib.h>
27#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include <string.h>
29#include <fcntl.h>
30#include <ctype.h>
31#include <termios.h>
32#include <time.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000033#ifdef HAVE_GETOPT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000034#include <getopt.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000035#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000036#include <unistd.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000037#ifdef HAVE_ERRNO_H
38#include <errno.h>
39#endif
40#ifdef HAVE_MNTENT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000041#include <mntent.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000042#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000043#include <sys/ioctl.h>
44#include <malloc.h>
45
46#include "et/com_err.h"
Theodore Ts'o1e3472c1997-04-29 14:53:37 +000047#include "uuid/uuid.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000048#include "e2fsck.h"
Theodore Ts'o21c84b71997-04-29 16:15:03 +000049#include "problem.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000050#include "../version.h"
51
52extern int isatty(int);
53
54const char * program_name = "e2fsck";
55const char * device_name = NULL;
Theodore Ts'o50e1e101997-04-26 13:58:21 +000056const char * filesystem_name = NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000057
58/* Command line options */
59int nflag = 0;
60int yflag = 0;
61int tflag = 0; /* Do timing */
62int cflag = 0; /* check disk */
63int preen = 0;
64int rwflag = 1;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +000065int swapfs = 0;
66int normalize_swapfs = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000067int inode_buffer_blocks = 0;
Theodore Ts'o21c84b71997-04-29 16:15:03 +000068blk_t use_superblock;
Theodore Ts'o3839e651997-04-26 13:21:57 +000069blk_t superblock;
70int blocksize = 0;
71int verbose = 0;
72int list = 0;
73int debug = 0;
74int force = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +000075int invalid_bitmaps = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000076static int show_version_only = 0;
77
78static int replace_bad_blocks = 0;
79static char *bad_blocks_file = 0;
80
81static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
82
83struct resource_track global_rtrack;
84
85static int root_filesystem = 0;
86static int read_only_root = 0;
87
Theodore Ts'of3db3561997-04-26 13:34:30 +000088int *invalid_inode_bitmap;
89int *invalid_block_bitmap;
90int *invalid_inode_table;
91int restart_e2fsck = 0;
92
Theodore Ts'o3839e651997-04-26 13:21:57 +000093static void usage(NOARGS)
94{
95 fprintf(stderr,
Theodore Ts'o1e3472c1997-04-29 14:53:37 +000096 "Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
Theodore Ts'of3db3561997-04-26 13:34:30 +000097 "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
98 "\t\t[-l|-L bad_blocks_file] device\n", program_name);
Theodore Ts'o3839e651997-04-26 13:21:57 +000099 exit(FSCK_USAGE);
100}
101
102static void show_stats(ext2_filsys fs)
103{
104 int inodes, inodes_used, blocks, blocks_used;
105 int dir_links;
106 int num_files, num_links;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000107 int frag_percent;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000108
109 dir_links = 2 * fs_directory_count - 1;
110 num_files = fs_total_count - dir_links;
111 num_links = fs_links_count - dir_links;
112 inodes = fs->super->s_inodes_count;
113 inodes_used = (fs->super->s_inodes_count -
114 fs->super->s_free_inodes_count);
115 blocks = fs->super->s_blocks_count;
116 blocks_used = (fs->super->s_blocks_count -
117 fs->super->s_free_blocks_count);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000118
119 frag_percent = (10000 * fs_fragmented) / inodes_used;
120 frag_percent = (frag_percent + 5) / 10;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000121
122 if (!verbose) {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000123 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
Theodore Ts'o74becf31997-04-26 14:37:06 +0000124 device_name, inodes_used, inodes,
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000125 frag_percent / 10, frag_percent % 10,
Theodore Ts'o74becf31997-04-26 14:37:06 +0000126 blocks_used, blocks);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000127 return;
128 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000129 printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000130 (inodes_used != 1) ? "s" : "",
131 100 * inodes_used / inodes);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000132 printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
133 fs_fragmented, frag_percent / 10, frag_percent % 10);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000134 printf (" # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
135 fs_ind_count, fs_dind_count, fs_tind_count);
136 printf ("%8d block%s used (%d%%)\n"
137 "%8d bad block%s\n", blocks_used,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000138 (blocks_used != 1) ? "s" : "",
139 100 * blocks_used / blocks, fs_badblocks_count,
140 fs_badblocks_count != 1 ? "s" : "");
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000141 printf ("\n%8d regular file%s\n"
142 "%8d director%s\n"
143 "%8d character device file%s\n"
144 "%8d block device file%s\n"
145 "%8d fifo%s\n"
146 "%8d link%s\n"
147 "%8d symbolic link%s (%d fast symbolic link%s)\n"
148 "%8d socket%s\n"
149 "--------\n"
150 "%8d file%s\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000151 fs_regular_count, (fs_regular_count != 1) ? "s" : "",
152 fs_directory_count, (fs_directory_count != 1) ? "ies" : "y",
153 fs_chardev_count, (fs_chardev_count != 1) ? "s" : "",
154 fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "",
155 fs_fifo_count, (fs_fifo_count != 1) ? "s" : "",
156 fs_links_count - dir_links,
157 ((fs_links_count - dir_links) != 1) ? "s" : "",
158 fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "",
159 fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "",
160 fs_sockets_count, (fs_sockets_count != 1) ? "s" : "",
161 fs_total_count - dir_links,
162 ((fs_total_count - dir_links) != 1) ? "s" : "");
163}
164
165static void check_mount(NOARGS)
166{
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000167 errcode_t retval;
Theodore Ts'o297f47a1997-04-26 14:25:20 +0000168 int mount_flags, cont, fd;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000169
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000170 retval = ext2fs_check_if_mounted(filesystem_name, &mount_flags);
171 if (retval) {
172 com_err("ext2fs_check_if_mount", retval,
173 "while determining whether %s is mounted.",
174 filesystem_name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000175 return;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000176 }
177 if (!(mount_flags & EXT2_MF_MOUNTED))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000178 return;
Theodore Ts'o297f47a1997-04-26 14:25:20 +0000179
Theodore Ts'o74becf31997-04-26 14:37:06 +0000180#if (defined(__linux__) && defined(HAVE_MNTENT_H))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000181 /*
182 * If the root is mounted read-only, then /etc/mtab is
183 * probably not correct; so we won't issue a warning based on
184 * it.
185 */
Theodore Ts'o297f47a1997-04-26 14:25:20 +0000186 fd = open(MOUNTED, O_RDWR);
187 if (fd < 0) {
188 if (errno == EROFS)
189 return;
190 } else
191 close(fd);
192#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000193
194 if (!rwflag) {
195 printf("Warning! %s is mounted.\n", device_name);
196 return;
197 }
198
199 printf ("%s is mounted. ", device_name);
200 if (isatty (0) && isatty (1))
201 cont = ask_yn("Do you really want to continue", -1);
202 else
203 cont = 0;
204 if (!cont) {
205 printf ("check aborted.\n");
206 exit (0);
207 }
208 return;
209}
210
211static void sync_disks(NOARGS)
212{
213 sync();
214 sync();
215 sleep(1);
216 sync();
217}
218
Theodore Ts'o521e3681997-04-29 17:48:10 +0000219static blk_t get_backup_sb(ext2_filsys fs)
220{
221 if (!fs || !fs->super)
222 return 8193;
223 return fs->super->s_blocks_per_group + 1;
224}
225
Theodore Ts'of3db3561997-04-26 13:34:30 +0000226#define MIN_CHECK 1
227#define MAX_CHECK 2
228
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000229static const char *corrupt_msg =
230"\nThe superblock could not be read or does not describe a correct ext2\n"
231"filesystem. If the device is valid and it really contains an ext2\n"
232"filesystem (and not swap or ufs or something else), then the superblock\n"
233"is corrupt, and you might try running e2fsck with an alternate superblock:\n"
Theodore Ts'o521e3681997-04-29 17:48:10 +0000234" e2fsck -b %d <device>\n\n";
Theodore Ts'of3db3561997-04-26 13:34:30 +0000235
Theodore Ts'o521e3681997-04-29 17:48:10 +0000236static void check_super_value(ext2_filsys fs, const char *descr,
237 unsigned long value, int flags,
238 unsigned long min, unsigned long max)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000239{
240 if (((flags & MIN_CHECK) && (value < min)) ||
241 ((flags & MAX_CHECK) && (value > max))) {
242 printf("Corruption found in superblock. (%s = %lu).\n",
243 descr, value);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000244 printf(corrupt_msg, get_backup_sb(fs));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000245 fatal_error(0);
246 }
247}
248
Theodore Ts'o521e3681997-04-29 17:48:10 +0000249static void relocate_hint(ext2_filsys fs)
Theodore Ts'o62c06f71997-04-29 14:34:47 +0000250{
251 static hint_issued = 0;
252
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000253 /*
254 * Only issue the hint once, and only if we're using the
255 * primary superblocks.
256 */
257 if (hint_issued || superblock)
Theodore Ts'o62c06f71997-04-29 14:34:47 +0000258 return;
259
260 printf("Note: if there is several inode or block bitmap blocks\n"
261 "which require relocation, or one part of the inode table\n"
262 "which must be moved, you may wish to try running e2fsck\n"
Theodore Ts'o521e3681997-04-29 17:48:10 +0000263 "with the '-b %d' option first. The problem may lie only\n"
264 "with the primary block group descriptor, and the backup\n"
265 "block group descriptor may be OK.\n\n", get_backup_sb(fs));
Theodore Ts'o62c06f71997-04-29 14:34:47 +0000266 hint_issued = 1;
267}
268
269
Theodore Ts'o3839e651997-04-26 13:21:57 +0000270static void check_super_block(ext2_filsys fs)
271{
272 blk_t first_block, last_block;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000273 struct ext2fs_sb *s = (struct ext2fs_sb *) fs->super;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000274 blk_t blocks_per_group = fs->super->s_blocks_per_group;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000275 int i;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000276 blk_t should_be;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000277 errcode_t retval;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000278 struct problem_context pctx;
279
280 clear_problem_context(&pctx);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000281
Theodore Ts'of3db3561997-04-26 13:34:30 +0000282 /*
283 * Verify the super block constants...
284 */
Theodore Ts'o521e3681997-04-29 17:48:10 +0000285 check_super_value(fs, "inodes_count", s->s_inodes_count,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000286 MIN_CHECK, 1, 0);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000287 check_super_value(fs, "blocks_count", s->s_blocks_count,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000288 MIN_CHECK, 1, 0);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000289 check_super_value(fs, "first_data_block", s->s_first_data_block,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000290 MAX_CHECK, 0, s->s_blocks_count);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000291 check_super_value(fs, "log_frag_size", s->s_log_frag_size,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000292 MAX_CHECK, 0, 2);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000293 check_super_value(fs, "log_block_size", s->s_log_block_size,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000294 MIN_CHECK | MAX_CHECK, s->s_log_frag_size,
295 2);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000296 check_super_value(fs, "frags_per_group", s->s_frags_per_group,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000297 MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
Theodore Ts'o521e3681997-04-29 17:48:10 +0000298 check_super_value(fs, "blocks_per_group", s->s_blocks_per_group,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000299 MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
Theodore Ts'o521e3681997-04-29 17:48:10 +0000300 check_super_value(fs, "inodes_per_group", s->s_inodes_per_group,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000301 MIN_CHECK, 1, 0);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000302 check_super_value(fs, "r_blocks_count", s->s_r_blocks_count,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000303 MAX_CHECK, 0, s->s_blocks_count);
304
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000305 retval = ext2fs_get_device_size(filesystem_name, EXT2_BLOCK_SIZE(s),
306 &should_be);
307 if (retval) {
308 com_err("ext2fs_get_device_size", retval,
309 "while trying to check physical size of filesystem");
310 fatal_error(0);
311 }
312 if (should_be < s->s_blocks_count) {
313 printf("The filesystem size (according to the superblock) is %d blocks\n", s->s_blocks_count);
314 printf("The physical size of the device is %d blocks\n",
315 should_be);
316 printf("Either the superblock or the partition table is likely to be corrupt!\n");
317 preenhalt(fs);
318 if (ask("Abort", 1))
319 fatal_error(0);
320 }
321
Theodore Ts'of3db3561997-04-26 13:34:30 +0000322 if (s->s_log_block_size != s->s_log_frag_size) {
323 printf("Superblock block_size = %d, fragsize = %d.\n",
324 EXT2_BLOCK_SIZE(s), EXT2_FRAG_SIZE(s));
325 printf("This version of e2fsck does not support fragment "
326 "sizes different\n"
327 "from the block size.\n");
328 fatal_error(0);
329 }
330
331 should_be = s->s_frags_per_group /
332 (s->s_log_block_size - s->s_log_frag_size + 1);
333 if (s->s_blocks_per_group != should_be) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000334 printf("Superblock blocks_per_group = %u, should "
335 "have been %u\n", s->s_blocks_per_group,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000336 should_be);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000337 printf(corrupt_msg, get_backup_sb(fs));
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000338 fatal_error(0);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000339 }
340
341 should_be = (s->s_log_block_size == 0) ? 1 : 0;
342 if (s->s_first_data_block != should_be) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000343 printf("Superblock first_data_block = %u, should "
344 "have been %u\n", s->s_first_data_block,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000345 should_be);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000346 printf(corrupt_msg, get_backup_sb(fs));
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000347 fatal_error(0);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000348 }
349
350 /*
351 * Verify the group descriptors....
352 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000353 first_block = fs->super->s_first_data_block;
354 last_block = first_block + blocks_per_group;
355
356 for (i = 0; i < fs->group_desc_count; i++) {
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000357 pctx.group = i;
358
Theodore Ts'of3db3561997-04-26 13:34:30 +0000359 if (i == fs->group_desc_count - 1)
360 last_block = fs->super->s_blocks_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000361 if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
362 (fs->group_desc[i].bg_block_bitmap >= last_block)) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000363 relocate_hint(fs);
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000364 pctx.blk = fs->group_desc[i].bg_block_bitmap;
365 if (fix_problem(fs, PR_0_BB_NOT_GROUP, &pctx)) {
366 fs->group_desc[i].bg_block_bitmap = 0;
367 invalid_block_bitmap[i]++;
368 invalid_bitmaps++;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000369 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000370 }
371 if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
372 (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000373 relocate_hint(fs);
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000374 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
375 if (fix_problem(fs, PR_0_IB_NOT_GROUP, &pctx)) {
376 fs->group_desc[i].bg_inode_bitmap = 0;
377 invalid_inode_bitmap[i]++;
378 invalid_bitmaps++;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000379 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000380 }
381 if ((fs->group_desc[i].bg_inode_table < first_block) ||
382 ((fs->group_desc[i].bg_inode_table +
383 fs->inode_blocks_per_group - 1) >= last_block)) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000384 relocate_hint(fs);
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000385 pctx.blk = fs->group_desc[i].bg_inode_table;
386 if (fix_problem(fs, PR_0_ITABLE_NOT_GROUP, &pctx)) {
387 fs->group_desc[i].bg_inode_table = 0;
388 invalid_inode_table[i]++;
389 invalid_bitmaps++;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000390 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000391 }
392 first_block += fs->super->s_blocks_per_group;
393 last_block += fs->super->s_blocks_per_group;
394 }
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000395 /*
396 * If we have invalid bitmaps, set the error state of the
397 * filesystem.
398 */
399 if (invalid_bitmaps && rwflag) {
400 fs->super->s_state &= ~EXT2_VALID_FS;
401 ext2fs_mark_super_dirty(fs);
402 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000403
404 /*
405 * If the UUID field isn't assigned, assign it.
406 */
407 if (rwflag && uuid_is_null(s->s_uuid)) {
408 if (preen)
409 printf("%s: Adding UUID to filesystem.\n",
410 device_name);
411 else
412 printf("Filesystem did not have a UUID; "
413 "generating one.\n\n");
414 uuid_generate(s->s_uuid);
415 ext2fs_mark_super_dirty(fs);
416 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000417 return;
418}
419
420/*
421 * This routine checks to see if a filesystem can be skipped; if so,
422 * it will exit with E2FSCK_OK. Under some conditions it will print a
423 * message explaining why a check is being forced.
424 */
425static void check_if_skip(ext2_filsys fs)
426{
427 const char *reason = NULL;
428
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000429 if (force || bad_blocks_file || cflag || swapfs)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000430 return;
431
432 if (fs->super->s_state & EXT2_ERROR_FS)
433 reason = "contains a file system with errors";
434 else if (fs->super->s_mnt_count >=
435 (unsigned) fs->super->s_max_mnt_count)
436 reason = "has reached maximal mount count";
437 else if (fs->super->s_checkinterval &&
438 time(0) >= (fs->super->s_lastcheck +
439 fs->super->s_checkinterval))
440 reason = "has gone too long without being checked";
Theodore Ts'o5c576471997-04-29 15:29:49 +0000441 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
442 reason = "was not cleanly unmounted";
Theodore Ts'o3839e651997-04-26 13:21:57 +0000443 if (reason) {
444 printf("%s %s, check forced.\n", device_name, reason);
445 return;
446 }
Theodore Ts'o5c576471997-04-29 15:29:49 +0000447 printf("%s: clean, %d/%d files, %d/%d blocks\n", device_name,
448 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
449 fs->super->s_inodes_count,
450 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
451 fs->super->s_blocks_count);
452 ext2fs_close(fs);
453 exit(FSCK_OK);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000454}
455
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000456#define PATH_SET "PATH=/sbin"
457
Theodore Ts'o3839e651997-04-26 13:21:57 +0000458static void PRS(int argc, char *argv[])
459{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000460 int flush = 0;
461 char c;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000462#ifdef MTRACE
Theodore Ts'of3db3561997-04-26 13:34:30 +0000463 extern void *mallwatch;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000464#endif
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000465 char *oldpath = getenv("PATH");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000466
467 /* Update our PATH to include /sbin */
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000468 if (oldpath) {
469 char *newpath;
470
471 newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath));
472 if (!newpath)
473 fatal_error("Couldn't malloc() newpath");
474 strcpy (newpath, PATH_SET);
475 strcat (newpath, ":");
476 strcat (newpath, oldpath);
477 putenv (newpath);
478 } else
479 putenv (PATH_SET);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000480
481 setbuf(stdout, NULL);
482 setbuf(stderr, NULL);
483 initialize_ext2_error_table();
484
485 if (argc && *argv)
486 program_name = *argv;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000487 while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000488 switch (c) {
489 case 'p':
490 case 'a':
491 preen = 1;
492 yflag = nflag = 0;
493 break;
494 case 'n':
495 nflag = 1;
496 preen = yflag = 0;
497 break;
498 case 'y':
499 yflag = 1;
500 preen = nflag = 0;
501 break;
502 case 't':
503 tflag++;
504 break;
505 case 'c':
506 cflag++;
507 break;
508 case 'r':
509 /* What we do by default, anyway! */
510 break;
511 case 'b':
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000512 use_superblock = atoi(optarg);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000513 break;
514 case 'B':
515 blocksize = atoi(optarg);
516 break;
517 case 'I':
518 inode_buffer_blocks = atoi(optarg);
519 break;
520 case 'P':
521 process_inode_size = atoi(optarg);
522 break;
523 case 'L':
524 replace_bad_blocks++;
525 case 'l':
526 bad_blocks_file = malloc(strlen(optarg)+1);
527 if (!bad_blocks_file)
528 fatal_error("Couldn't malloc bad_blocks_file");
529 strcpy(bad_blocks_file, optarg);
530 break;
531 case 'd':
532 debug = 1;
533 break;
534 case 'f':
535 force = 1;
536 break;
537 case 'F':
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000538#ifdef BLKFLSBUF
Theodore Ts'o3839e651997-04-26 13:21:57 +0000539 flush = 1;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000540#else
541 fatal_error ("-F not supported");
542#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000543 break;
544 case 'v':
545 verbose = 1;
546 break;
547 case 'V':
548 show_version_only = 1;
549 break;
550#ifdef MTRACE
551 case 'M':
552 mallwatch = (void *) strtol(optarg, NULL, 0);
553 break;
554#endif
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000555 case 'N':
556 device_name = optarg;
557 break;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000558 case 's':
559 normalize_swapfs = 1;
560 case 'S':
561 swapfs = 1;
562 break;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000563 default:
564 usage ();
565 }
566 if (show_version_only)
567 return;
568 if (optind != argc - 1)
569 usage ();
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000570 if (nflag && !bad_blocks_file && !cflag && !swapfs)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000571 rwflag = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000572 filesystem_name = argv[optind];
573 if (device_name == 0)
574 device_name = filesystem_name;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000575 if (flush) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000576#ifdef BLKFLSBUF
577 int fd = open(filesystem_name, O_RDONLY, 0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000578
579 if (fd < 0) {
580 com_err("open", errno, "while opening %s for flushing",
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000581 filesystem_name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000582 exit(FSCK_ERROR);
583 }
584 if (ioctl(fd, BLKFLSBUF, 0) < 0) {
585 com_err("BLKFLSBUF", errno, "while trying to flush %s",
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000586 filesystem_name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000587 exit(FSCK_ERROR);
588 }
589 close(fd);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000590#else
591 fatal_error ("BLKFLSBUF not supported");
592#endif /* BLKFLSBUF */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000593 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000594 if (swapfs) {
595 if (cflag || bad_blocks_file) {
596 fprintf(stderr, "Incompatible options not "
597 "allowed when byte-swapping.\n");
598 fatal_error(0);
599 }
600 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000601}
Theodore Ts'o521e3681997-04-29 17:48:10 +0000602
603static const char *my_ver_string = E2FSPROGS_VERSION;
604static const char *my_ver_date = E2FSPROGS_DATE;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000605
606int main (int argc, char *argv[])
607{
608 errcode_t retval = 0;
609 int exit_value = FSCK_OK;
610 int i;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000611 ext2_filsys fs = 0;
612 io_manager io_ptr;
613 struct ext2fs_sb *s;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000614 const char *lib_ver_date;
615 int my_ver, lib_ver;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000616
617#ifdef MTRACE
618 mtrace();
619#endif
620#ifdef MCHECK
621 mcheck(0);
622#endif
Theodore Ts'o521e3681997-04-29 17:48:10 +0000623 my_ver = ext2fs_parse_version_string(my_ver_string);
624 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
625 if (my_ver > lib_ver) {
626 fprintf( stderr, "Error: ext2fs library version "
627 "out of date!\n");
628 show_version_only++;
629 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000630
631 init_resource_track(&global_rtrack);
632
633 PRS(argc, argv);
634
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000635 if (!preen || show_version_only)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000636 fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
Theodore Ts'o521e3681997-04-29 17:48:10 +0000637 my_ver_string, my_ver_date, EXT2FS_VERSION,
638 EXT2FS_DATE);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000639
Theodore Ts'o5c576471997-04-29 15:29:49 +0000640 if (show_version_only) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000641 fprintf(stderr, "\tUsing %s, %s\n",
642 error_message(EXT2_ET_BASE), lib_ver_date);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000643 exit(0);
Theodore Ts'o5c576471997-04-29 15:29:49 +0000644 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000645
646 check_mount();
647
648 if (!preen && !nflag && !yflag) {
649 if (!isatty (0) || !isatty (1))
650 die ("need terminal for interactive repairs");
651 }
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000652 superblock = use_superblock;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000653restart:
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000654#if 1
655 io_ptr = unix_io_manager;
656#else
657 io_ptr = test_io_manager;
658 test_io_backing_manager = unix_io_manager;
659#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000660 sync_disks();
661 if (superblock && blocksize) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000662 retval = ext2fs_open(filesystem_name,
663 rwflag ? EXT2_FLAG_RW : 0,
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000664 superblock, blocksize, io_ptr, &fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000665 } else if (superblock) {
666 for (i=0; possible_block_sizes[i]; i++) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000667 retval = ext2fs_open(filesystem_name,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000668 rwflag ? EXT2_FLAG_RW : 0,
669 superblock,
670 possible_block_sizes[i],
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000671 io_ptr, &fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000672 if (!retval)
673 break;
674 }
675 } else
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000676 retval = ext2fs_open(filesystem_name,
677 rwflag ? EXT2_FLAG_RW : 0,
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000678 0, 0, io_ptr, &fs);
679 if (!superblock && !preen &&
680 ((retval == EXT2_ET_BAD_MAGIC) ||
681 ((retval == 0) && ext2fs_check_desc(fs)))) {
682 if (!fs || (fs->group_desc_count > 1)) {
683 printf("%s trying backup blocks...\n",
684 retval ? "Couldn't find ext2 superblock," :
685 "Group descriptors look bad...");
Theodore Ts'o521e3681997-04-29 17:48:10 +0000686 superblock = get_backup_sb(fs);
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000687 if (fs)
688 ext2fs_close(fs);
689 goto restart;
690 }
691 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000692 if (retval) {
693 com_err(program_name, retval, "while trying to open %s",
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000694 filesystem_name);
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000695 if (retval == EXT2_ET_REV_TOO_HIGH)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000696 printf ("Get a newer version of e2fsck!\n");
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000697 else if (retval == EXT2_ET_SHORT_READ)
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000698 printf ("Could this be a zero-length partition?\n");
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000699 else if ((retval == EPERM) || (retval == EACCES))
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000700 printf("You must have %s access to the "
701 "filesystem or be root\n",
702 rwflag ? "r/w" : "r/o");
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000703 else if (retval == ENXIO)
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000704 printf("Possibly non-existent or swap device?\n");
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000705 else
Theodore Ts'o521e3681997-04-29 17:48:10 +0000706 printf(corrupt_msg, get_backup_sb(fs));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000707 fatal_error(0);
708 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000709#ifdef EXT2_CURRENT_REV
710 if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000711 com_err(program_name, EXT2_ET_REV_TOO_HIGH,
712 "while trying to open %s",
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000713 filesystem_name);
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000714 goto get_newer;
715 }
716#endif
717 /*
Theodore Ts'o521e3681997-04-29 17:48:10 +0000718 * Check for compatibility with the feature sets. We need to
719 * be more stringent than ext2fs_open().
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000720 */
721 s = (struct ext2fs_sb *) fs->super;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000722 if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
723 (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000724 com_err(program_name, EXT2_ET_UNSUPP_FEATURE,
Theodore Ts'o521e3681997-04-29 17:48:10 +0000725 "(%s)", filesystem_name);
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000726 get_newer:
Theodore Ts'of3db3561997-04-26 13:34:30 +0000727 printf ("Get a newer version of e2fsck!\n");
728 fatal_error(0);
729 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000730 if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
731 com_err(program_name, EXT2_ET_RO_UNSUPP_FEATURE,
732 "(%s)", filesystem_name);
733 goto get_newer;
734 }
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000735
Theodore Ts'o3839e651997-04-26 13:21:57 +0000736 /*
737 * If the user specified a specific superblock, presumably the
738 * master superblock has been trashed. So we mark the
739 * superblock as dirty, so it can be written out.
740 */
741 if (superblock && rwflag)
742 ext2fs_mark_super_dirty(fs);
743
Theodore Ts'o5c576471997-04-29 15:29:49 +0000744 /*
745 * Don't overwrite the backup superblock and block
746 * descriptors, until we're sure the filesystem is OK....
747 */
748 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
749
Theodore Ts'o3839e651997-04-26 13:21:57 +0000750 ehandler_init(fs->io);
751
Theodore Ts'of3db3561997-04-26 13:34:30 +0000752 invalid_inode_bitmap = allocate_memory(sizeof(int) *
753 fs->group_desc_count,
754 "invalid_inode_bitmap");
755 invalid_block_bitmap = allocate_memory(sizeof(int) *
756 fs->group_desc_count,
757 "invalid_block_bitmap");
758 invalid_inode_table = allocate_memory(sizeof(int) *
759 fs->group_desc_count,
760 "invalid_inode_table");
761
Theodore Ts'o3839e651997-04-26 13:21:57 +0000762 check_super_block(fs);
763 check_if_skip(fs);
764 if (bad_blocks_file)
765 read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks);
766 else if (cflag)
767 test_disk(fs);
768
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000769 if (normalize_swapfs) {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000770 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
771 ext2fs_native_flag()) {
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000772 fprintf(stderr, "%s: Filesystem byte order "
773 "already normalized.\n", device_name);
774 fatal_error(0);
775 }
776 }
777 if (swapfs)
778 swap_filesys(fs);
779
Theodore Ts'o3839e651997-04-26 13:21:57 +0000780 /*
781 * Mark the system as valid, 'til proven otherwise
782 */
783 ext2fs_mark_valid(fs);
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000784
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000785 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
786 if (retval) {
787 com_err(program_name, retval,
788 "while reading bad blocks inode");
789 preenhalt(fs);
790 printf("This doesn't bode well, but we'll try to go on...\n");
791 }
792
Theodore Ts'o3839e651997-04-26 13:21:57 +0000793 pass1(fs);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000794 free(invalid_inode_bitmap);
795 free(invalid_block_bitmap);
796 free(invalid_inode_table);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000797 if (restart_e2fsck) {
798 ext2fs_close(fs);
799 printf("Restarting e2fsck from the beginning...\n");
800 restart_e2fsck = 0;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000801 superblock = use_superblock;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000802 goto restart;
803 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000804 pass2(fs);
805 pass3(fs);
806 pass4(fs);
807 pass5(fs);
808
809#ifdef MTRACE
810 mtrace_print("Cleanup");
811#endif
812 if (ext2fs_test_changed(fs)) {
813 exit_value = FSCK_NONDESTRUCT;
814 if (!preen)
815 printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
816 device_name);
817 if (root_filesystem && !read_only_root) {
818 printf("%s: ***** REBOOT LINUX *****\n", device_name);
819 exit_value = FSCK_REBOOT;
820 }
821 }
Theodore Ts'o5c576471997-04-29 15:29:49 +0000822 if (ext2fs_test_valid(fs))
823 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
824 else
Theodore Ts'o3839e651997-04-26 13:21:57 +0000825 exit_value = FSCK_UNCORRECTED;
826 if (rwflag) {
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000827 if (ext2fs_test_valid(fs)) {
828 if (!(fs->super->s_state & EXT2_VALID_FS))
829 exit_value = FSCK_NONDESTRUCT;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000830 fs->super->s_state = EXT2_VALID_FS;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000831 } else
Theodore Ts'o3839e651997-04-26 13:21:57 +0000832 fs->super->s_state &= ~EXT2_VALID_FS;
833 fs->super->s_mnt_count = 0;
834 fs->super->s_lastcheck = time(NULL);
835 ext2fs_mark_super_dirty(fs);
836 }
837 show_stats(fs);
838
839 write_bitmaps(fs);
840 ext2fs_close(fs);
841 sync_disks();
842
843 if (tflag)
844 print_resource_track(&global_rtrack);
845
846 return exit_value;
847}