]>
Commit | Line | Data |
---|---|---|
3839e657 TT |
1 | /* |
2 | * e2fsck.c - a consistency checker for the new extended file system. | |
3 | * | |
21c84b71 TT |
4 | * 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% | |
3839e657 TT |
10 | */ |
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 | ||
1e3472c5 TT |
24 | #include <stdio.h> |
25 | #ifdef HAVE_STDLIB_H | |
26 | #include <stdlib.h> | |
27 | #endif | |
3839e657 TT |
28 | #include <string.h> |
29 | #include <fcntl.h> | |
30 | #include <ctype.h> | |
31 | #include <termios.h> | |
32 | #include <time.h> | |
50e1e10f | 33 | #ifdef HAVE_GETOPT_H |
3839e657 | 34 | #include <getopt.h> |
50e1e10f | 35 | #endif |
3839e657 | 36 | #include <unistd.h> |
50e1e10f TT |
37 | #ifdef HAVE_ERRNO_H |
38 | #include <errno.h> | |
39 | #endif | |
40 | #ifdef HAVE_MNTENT_H | |
3839e657 | 41 | #include <mntent.h> |
50e1e10f | 42 | #endif |
3839e657 TT |
43 | #include <sys/ioctl.h> |
44 | #include <malloc.h> | |
45 | ||
46 | #include "et/com_err.h" | |
1e3472c5 | 47 | #include "uuid/uuid.h" |
3839e657 | 48 | #include "e2fsck.h" |
21c84b71 | 49 | #include "problem.h" |
3839e657 TT |
50 | #include "../version.h" |
51 | ||
52 | extern int isatty(int); | |
53 | ||
54 | const char * program_name = "e2fsck"; | |
55 | const char * device_name = NULL; | |
50e1e10f | 56 | const char * filesystem_name = NULL; |
3839e657 TT |
57 | |
58 | /* Command line options */ | |
59 | int nflag = 0; | |
60 | int yflag = 0; | |
61 | int tflag = 0; /* Do timing */ | |
62 | int cflag = 0; /* check disk */ | |
63 | int preen = 0; | |
64 | int rwflag = 1; | |
1e3472c5 TT |
65 | int swapfs = 0; |
66 | int normalize_swapfs = 0; | |
3839e657 | 67 | int inode_buffer_blocks = 0; |
21c84b71 | 68 | blk_t use_superblock; |
3839e657 TT |
69 | blk_t superblock; |
70 | int blocksize = 0; | |
71 | int verbose = 0; | |
72 | int list = 0; | |
73 | int debug = 0; | |
74 | int force = 0; | |
f3db3566 | 75 | int invalid_bitmaps = 0; |
3839e657 TT |
76 | static int show_version_only = 0; |
77 | ||
78 | static int replace_bad_blocks = 0; | |
79 | static char *bad_blocks_file = 0; | |
80 | ||
81 | static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0}; | |
82 | ||
83 | struct resource_track global_rtrack; | |
84 | ||
85 | static int root_filesystem = 0; | |
86 | static int read_only_root = 0; | |
87 | ||
f3db3566 TT |
88 | int *invalid_inode_bitmap; |
89 | int *invalid_block_bitmap; | |
90 | int *invalid_inode_table; | |
91 | int restart_e2fsck = 0; | |
92 | ||
3839e657 TT |
93 | static void usage(NOARGS) |
94 | { | |
95 | fprintf(stderr, | |
1e3472c5 | 96 | "Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n" |
f3db3566 TT |
97 | "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n" |
98 | "\t\t[-l|-L bad_blocks_file] device\n", program_name); | |
3839e657 TT |
99 | exit(FSCK_USAGE); |
100 | } | |
101 | ||
102 | static 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; | |
7f88b043 | 107 | int frag_percent; |
3839e657 TT |
108 | |
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); | |
7f88b043 TT |
118 | |
119 | frag_percent = (10000 * fs_fragmented) / inodes_used; | |
120 | frag_percent = (frag_percent + 5) / 10; | |
3839e657 TT |
121 | |
122 | if (!verbose) { | |
7f88b043 | 123 | printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n", |
74becf3c | 124 | device_name, inodes_used, inodes, |
7f88b043 | 125 | frag_percent / 10, frag_percent % 10, |
74becf3c | 126 | blocks_used, blocks); |
3839e657 TT |
127 | return; |
128 | } | |
50e1e10f | 129 | printf ("\n%8d inode%s used (%d%%)\n", inodes_used, |
3839e657 TT |
130 | (inodes_used != 1) ? "s" : "", |
131 | 100 * inodes_used / inodes); | |
7f88b043 TT |
132 | printf ("%8d non-contiguous inodes (%0d.%d%%)\n", |
133 | fs_fragmented, frag_percent / 10, frag_percent % 10); | |
50e1e10f TT |
134 | 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, | |
3839e657 TT |
138 | (blocks_used != 1) ? "s" : "", |
139 | 100 * blocks_used / blocks, fs_badblocks_count, | |
140 | fs_badblocks_count != 1 ? "s" : ""); | |
50e1e10f TT |
141 | 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", | |
3839e657 TT |
151 | 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 | ||
165 | static void check_mount(NOARGS) | |
166 | { | |
50e1e10f | 167 | errcode_t retval; |
297f47a1 | 168 | int mount_flags, cont, fd; |
3839e657 | 169 | |
50e1e10f TT |
170 | 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); | |
3839e657 | 175 | return; |
50e1e10f TT |
176 | } |
177 | if (!(mount_flags & EXT2_MF_MOUNTED)) | |
3839e657 | 178 | return; |
297f47a1 | 179 | |
74becf3c | 180 | #if (defined(__linux__) && defined(HAVE_MNTENT_H)) |
3839e657 TT |
181 | /* |
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 | */ | |
297f47a1 TT |
186 | fd = open(MOUNTED, O_RDWR); |
187 | if (fd < 0) { | |
188 | if (errno == EROFS) | |
189 | return; | |
190 | } else | |
191 | close(fd); | |
192 | #endif | |
3839e657 TT |
193 | |
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 | ||
211 | static void sync_disks(NOARGS) | |
212 | { | |
213 | sync(); | |
214 | sync(); | |
215 | sleep(1); | |
216 | sync(); | |
217 | } | |
218 | ||
521e3685 TT |
219 | static 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 | ||
f3db3566 TT |
226 | #define MIN_CHECK 1 |
227 | #define MAX_CHECK 2 | |
228 | ||
1e3472c5 TT |
229 | static 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" | |
521e3685 | 234 | " e2fsck -b %d <device>\n\n"; |
f3db3566 | 235 | |
521e3685 TT |
236 | static void check_super_value(ext2_filsys fs, const char *descr, |
237 | unsigned long value, int flags, | |
238 | unsigned long min, unsigned long max) | |
f3db3566 TT |
239 | { |
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); | |
521e3685 | 244 | printf(corrupt_msg, get_backup_sb(fs)); |
f3db3566 TT |
245 | fatal_error(0); |
246 | } | |
247 | } | |
248 | ||
521e3685 | 249 | static void relocate_hint(ext2_filsys fs) |
62c06f79 TT |
250 | { |
251 | static hint_issued = 0; | |
252 | ||
21c84b71 TT |
253 | /* |
254 | * Only issue the hint once, and only if we're using the | |
255 | * primary superblocks. | |
256 | */ | |
257 | if (hint_issued || superblock) | |
62c06f79 TT |
258 | 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" | |
521e3685 TT |
263 | "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)); | |
62c06f79 TT |
266 | hint_issued = 1; |
267 | } | |
268 | ||
269 | ||
3839e657 TT |
270 | static void check_super_block(ext2_filsys fs) |
271 | { | |
272 | blk_t first_block, last_block; | |
1e3472c5 | 273 | struct ext2fs_sb *s = (struct ext2fs_sb *) fs->super; |
f3db3566 | 274 | blk_t blocks_per_group = fs->super->s_blocks_per_group; |
3839e657 | 275 | int i; |
f3db3566 | 276 | blk_t should_be; |
50e1e10f | 277 | errcode_t retval; |
21c84b71 TT |
278 | struct problem_context pctx; |
279 | ||
280 | clear_problem_context(&pctx); | |
3839e657 | 281 | |
f3db3566 TT |
282 | /* |
283 | * Verify the super block constants... | |
284 | */ | |
521e3685 | 285 | check_super_value(fs, "inodes_count", s->s_inodes_count, |
f3db3566 | 286 | MIN_CHECK, 1, 0); |
521e3685 | 287 | check_super_value(fs, "blocks_count", s->s_blocks_count, |
f3db3566 | 288 | MIN_CHECK, 1, 0); |
521e3685 | 289 | check_super_value(fs, "first_data_block", s->s_first_data_block, |
f3db3566 | 290 | MAX_CHECK, 0, s->s_blocks_count); |
521e3685 | 291 | check_super_value(fs, "log_frag_size", s->s_log_frag_size, |
f3db3566 | 292 | MAX_CHECK, 0, 2); |
521e3685 | 293 | check_super_value(fs, "log_block_size", s->s_log_block_size, |
f3db3566 TT |
294 | MIN_CHECK | MAX_CHECK, s->s_log_frag_size, |
295 | 2); | |
521e3685 | 296 | check_super_value(fs, "frags_per_group", s->s_frags_per_group, |
f3db3566 | 297 | MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s)); |
521e3685 | 298 | check_super_value(fs, "blocks_per_group", s->s_blocks_per_group, |
f3db3566 | 299 | MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s)); |
521e3685 | 300 | check_super_value(fs, "inodes_per_group", s->s_inodes_per_group, |
f3db3566 | 301 | MIN_CHECK, 1, 0); |
521e3685 | 302 | check_super_value(fs, "r_blocks_count", s->s_r_blocks_count, |
f3db3566 TT |
303 | MAX_CHECK, 0, s->s_blocks_count); |
304 | ||
50e1e10f TT |
305 | 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 | ||
f3db3566 TT |
322 | 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) { | |
50e1e10f TT |
334 | printf("Superblock blocks_per_group = %u, should " |
335 | "have been %u\n", s->s_blocks_per_group, | |
f3db3566 | 336 | should_be); |
521e3685 | 337 | printf(corrupt_msg, get_backup_sb(fs)); |
7f88b043 | 338 | fatal_error(0); |
f3db3566 TT |
339 | } |
340 | ||
341 | should_be = (s->s_log_block_size == 0) ? 1 : 0; | |
342 | if (s->s_first_data_block != should_be) { | |
50e1e10f TT |
343 | printf("Superblock first_data_block = %u, should " |
344 | "have been %u\n", s->s_first_data_block, | |
f3db3566 | 345 | should_be); |
521e3685 | 346 | printf(corrupt_msg, get_backup_sb(fs)); |
7f88b043 | 347 | fatal_error(0); |
f3db3566 TT |
348 | } |
349 | ||
350 | /* | |
351 | * Verify the group descriptors.... | |
352 | */ | |
3839e657 TT |
353 | 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++) { | |
21c84b71 TT |
357 | pctx.group = i; |
358 | ||
f3db3566 TT |
359 | if (i == fs->group_desc_count - 1) |
360 | last_block = fs->super->s_blocks_count; | |
3839e657 TT |
361 | if ((fs->group_desc[i].bg_block_bitmap < first_block) || |
362 | (fs->group_desc[i].bg_block_bitmap >= last_block)) { | |
521e3685 | 363 | relocate_hint(fs); |
21c84b71 TT |
364 | 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++; | |
f3db3566 | 369 | } |
3839e657 TT |
370 | } |
371 | if ((fs->group_desc[i].bg_inode_bitmap < first_block) || | |
372 | (fs->group_desc[i].bg_inode_bitmap >= last_block)) { | |
521e3685 | 373 | relocate_hint(fs); |
21c84b71 TT |
374 | 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++; | |
f3db3566 | 379 | } |
3839e657 TT |
380 | } |
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)) { | |
521e3685 | 384 | relocate_hint(fs); |
21c84b71 TT |
385 | 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++; | |
f3db3566 | 390 | } |
3839e657 TT |
391 | } |
392 | first_block += fs->super->s_blocks_per_group; | |
393 | last_block += fs->super->s_blocks_per_group; | |
394 | } | |
21c84b71 TT |
395 | /* |
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 | } | |
1e3472c5 TT |
403 | |
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 | } | |
3839e657 TT |
417 | 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 | */ | |
425 | static void check_if_skip(ext2_filsys fs) | |
426 | { | |
427 | const char *reason = NULL; | |
428 | ||
1e3472c5 | 429 | if (force || bad_blocks_file || cflag || swapfs) |
3839e657 TT |
430 | 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"; | |
5c576477 TT |
441 | else if ((fs->super->s_state & EXT2_VALID_FS) == 0) |
442 | reason = "was not cleanly unmounted"; | |
3839e657 TT |
443 | if (reason) { |
444 | printf("%s %s, check forced.\n", device_name, reason); | |
445 | return; | |
446 | } | |
5c576477 TT |
447 | 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); | |
3839e657 TT |
454 | } |
455 | ||
50e1e10f TT |
456 | #define PATH_SET "PATH=/sbin" |
457 | ||
3839e657 TT |
458 | static void PRS(int argc, char *argv[]) |
459 | { | |
f3db3566 TT |
460 | int flush = 0; |
461 | char c; | |
3839e657 | 462 | #ifdef MTRACE |
f3db3566 | 463 | extern void *mallwatch; |
3839e657 | 464 | #endif |
50e1e10f | 465 | char *oldpath = getenv("PATH"); |
3839e657 TT |
466 | |
467 | /* Update our PATH to include /sbin */ | |
50e1e10f TT |
468 | 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); | |
3839e657 TT |
480 | |
481 | setbuf(stdout, NULL); | |
482 | setbuf(stderr, NULL); | |
483 | initialize_ext2_error_table(); | |
484 | ||
485 | if (argc && *argv) | |
486 | program_name = *argv; | |
1e3472c5 | 487 | while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF) |
3839e657 TT |
488 | 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': | |
21c84b71 | 512 | use_superblock = atoi(optarg); |
3839e657 TT |
513 | 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': | |
50e1e10f | 538 | #ifdef BLKFLSBUF |
3839e657 | 539 | flush = 1; |
50e1e10f TT |
540 | #else |
541 | fatal_error ("-F not supported"); | |
542 | #endif | |
3839e657 TT |
543 | 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 | |
50e1e10f TT |
555 | case 'N': |
556 | device_name = optarg; | |
557 | break; | |
1e3472c5 TT |
558 | case 's': |
559 | normalize_swapfs = 1; | |
560 | case 'S': | |
561 | swapfs = 1; | |
562 | break; | |
3839e657 TT |
563 | default: |
564 | usage (); | |
565 | } | |
566 | if (show_version_only) | |
567 | return; | |
568 | if (optind != argc - 1) | |
569 | usage (); | |
1e3472c5 | 570 | if (nflag && !bad_blocks_file && !cflag && !swapfs) |
3839e657 | 571 | rwflag = 0; |
50e1e10f TT |
572 | filesystem_name = argv[optind]; |
573 | if (device_name == 0) | |
574 | device_name = filesystem_name; | |
3839e657 | 575 | if (flush) { |
50e1e10f TT |
576 | #ifdef BLKFLSBUF |
577 | int fd = open(filesystem_name, O_RDONLY, 0); | |
3839e657 TT |
578 | |
579 | if (fd < 0) { | |
580 | com_err("open", errno, "while opening %s for flushing", | |
50e1e10f | 581 | filesystem_name); |
3839e657 TT |
582 | exit(FSCK_ERROR); |
583 | } | |
584 | if (ioctl(fd, BLKFLSBUF, 0) < 0) { | |
585 | com_err("BLKFLSBUF", errno, "while trying to flush %s", | |
50e1e10f | 586 | filesystem_name); |
3839e657 TT |
587 | exit(FSCK_ERROR); |
588 | } | |
589 | close(fd); | |
50e1e10f TT |
590 | #else |
591 | fatal_error ("BLKFLSBUF not supported"); | |
592 | #endif /* BLKFLSBUF */ | |
3839e657 | 593 | } |
1e3472c5 TT |
594 | 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 | } | |
3839e657 | 601 | } |
521e3685 TT |
602 | |
603 | static const char *my_ver_string = E2FSPROGS_VERSION; | |
604 | static const char *my_ver_date = E2FSPROGS_DATE; | |
3839e657 TT |
605 | |
606 | int main (int argc, char *argv[]) | |
607 | { | |
608 | errcode_t retval = 0; | |
609 | int exit_value = FSCK_OK; | |
610 | int i; | |
21c84b71 TT |
611 | ext2_filsys fs = 0; |
612 | io_manager io_ptr; | |
613 | struct ext2fs_sb *s; | |
521e3685 TT |
614 | const char *lib_ver_date; |
615 | int my_ver, lib_ver; | |
3839e657 TT |
616 | |
617 | #ifdef MTRACE | |
618 | mtrace(); | |
619 | #endif | |
620 | #ifdef MCHECK | |
621 | mcheck(0); | |
622 | #endif | |
521e3685 TT |
623 | 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 | } | |
3839e657 TT |
630 | |
631 | init_resource_track(&global_rtrack); | |
632 | ||
633 | PRS(argc, argv); | |
634 | ||
21c84b71 | 635 | if (!preen || show_version_only) |
3839e657 | 636 | fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n", |
521e3685 TT |
637 | my_ver_string, my_ver_date, EXT2FS_VERSION, |
638 | EXT2FS_DATE); | |
3839e657 | 639 | |
5c576477 | 640 | if (show_version_only) { |
521e3685 TT |
641 | fprintf(stderr, "\tUsing %s, %s\n", |
642 | error_message(EXT2_ET_BASE), lib_ver_date); | |
3839e657 | 643 | exit(0); |
5c576477 | 644 | } |
3839e657 TT |
645 | |
646 | check_mount(); | |
647 | ||
648 | if (!preen && !nflag && !yflag) { | |
649 | if (!isatty (0) || !isatty (1)) | |
650 | die ("need terminal for interactive repairs"); | |
651 | } | |
21c84b71 | 652 | superblock = use_superblock; |
f3db3566 | 653 | restart: |
21c84b71 TT |
654 | #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 | |
3839e657 TT |
660 | sync_disks(); |
661 | if (superblock && blocksize) { | |
50e1e10f TT |
662 | retval = ext2fs_open(filesystem_name, |
663 | rwflag ? EXT2_FLAG_RW : 0, | |
21c84b71 | 664 | superblock, blocksize, io_ptr, &fs); |
3839e657 TT |
665 | } else if (superblock) { |
666 | for (i=0; possible_block_sizes[i]; i++) { | |
50e1e10f | 667 | retval = ext2fs_open(filesystem_name, |
3839e657 TT |
668 | rwflag ? EXT2_FLAG_RW : 0, |
669 | superblock, | |
670 | possible_block_sizes[i], | |
21c84b71 | 671 | io_ptr, &fs); |
3839e657 TT |
672 | if (!retval) |
673 | break; | |
674 | } | |
675 | } else | |
50e1e10f TT |
676 | retval = ext2fs_open(filesystem_name, |
677 | rwflag ? EXT2_FLAG_RW : 0, | |
21c84b71 TT |
678 | 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..."); | |
521e3685 | 686 | superblock = get_backup_sb(fs); |
21c84b71 TT |
687 | if (fs) |
688 | ext2fs_close(fs); | |
689 | goto restart; | |
690 | } | |
691 | } | |
3839e657 TT |
692 | if (retval) { |
693 | com_err(program_name, retval, "while trying to open %s", | |
50e1e10f | 694 | filesystem_name); |
1e3472c5 | 695 | if (retval == EXT2_ET_REV_TOO_HIGH) |
f3db3566 | 696 | printf ("Get a newer version of e2fsck!\n"); |
1e3472c5 | 697 | else if (retval == EXT2_ET_SHORT_READ) |
50e1e10f | 698 | printf ("Could this be a zero-length partition?\n"); |
1e3472c5 | 699 | else if ((retval == EPERM) || (retval == EACCES)) |
50e1e10f TT |
700 | printf("You must have %s access to the " |
701 | "filesystem or be root\n", | |
702 | rwflag ? "r/w" : "r/o"); | |
1e3472c5 | 703 | else if (retval == ENXIO) |
50e1e10f | 704 | printf("Possibly non-existent or swap device?\n"); |
1e3472c5 | 705 | else |
521e3685 | 706 | printf(corrupt_msg, get_backup_sb(fs)); |
3839e657 TT |
707 | fatal_error(0); |
708 | } | |
f3db3566 TT |
709 | #ifdef EXT2_CURRENT_REV |
710 | if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) { | |
7f88b043 TT |
711 | com_err(program_name, EXT2_ET_REV_TOO_HIGH, |
712 | "while trying to open %s", | |
50e1e10f | 713 | filesystem_name); |
21c84b71 TT |
714 | goto get_newer; |
715 | } | |
716 | #endif | |
717 | /* | |
521e3685 TT |
718 | * Check for compatibility with the feature sets. We need to |
719 | * be more stringent than ext2fs_open(). | |
21c84b71 TT |
720 | */ |
721 | s = (struct ext2fs_sb *) fs->super; | |
521e3685 TT |
722 | if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) || |
723 | (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) { | |
21c84b71 | 724 | com_err(program_name, EXT2_ET_UNSUPP_FEATURE, |
521e3685 | 725 | "(%s)", filesystem_name); |
21c84b71 | 726 | get_newer: |
f3db3566 TT |
727 | printf ("Get a newer version of e2fsck!\n"); |
728 | fatal_error(0); | |
729 | } | |
521e3685 TT |
730 | 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 | } | |
21c84b71 | 735 | |
3839e657 TT |
736 | /* |
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 | ||
5c576477 TT |
744 | /* |
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 | ||
3839e657 TT |
750 | ehandler_init(fs->io); |
751 | ||
f3db3566 TT |
752 | 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 | ||
3839e657 TT |
762 | 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 | ||
1e3472c5 | 769 | if (normalize_swapfs) { |
5c576477 TT |
770 | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == |
771 | ext2fs_native_flag()) { | |
1e3472c5 TT |
772 | 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 | ||
3839e657 TT |
780 | /* |
781 | * Mark the system as valid, 'til proven otherwise | |
782 | */ | |
783 | ext2fs_mark_valid(fs); | |
1e3472c5 | 784 | |
21c84b71 TT |
785 | 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 | ||
3839e657 | 793 | pass1(fs); |
50e1e10f TT |
794 | free(invalid_inode_bitmap); |
795 | free(invalid_block_bitmap); | |
796 | free(invalid_inode_table); | |
f3db3566 TT |
797 | if (restart_e2fsck) { |
798 | ext2fs_close(fs); | |
799 | printf("Restarting e2fsck from the beginning...\n"); | |
800 | restart_e2fsck = 0; | |
21c84b71 | 801 | superblock = use_superblock; |
f3db3566 TT |
802 | goto restart; |
803 | } | |
3839e657 TT |
804 | 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 | } | |
5c576477 TT |
822 | if (ext2fs_test_valid(fs)) |
823 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; | |
824 | else | |
3839e657 TT |
825 | exit_value = FSCK_UNCORRECTED; |
826 | if (rwflag) { | |
1e3472c5 TT |
827 | if (ext2fs_test_valid(fs)) { |
828 | if (!(fs->super->s_state & EXT2_VALID_FS)) | |
829 | exit_value = FSCK_NONDESTRUCT; | |
3839e657 | 830 | fs->super->s_state = EXT2_VALID_FS; |
1e3472c5 | 831 | } else |
3839e657 TT |
832 | 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 | } |