]>
Commit | Line | Data |
---|---|---|
1b6bf175 TT |
1 | /* |
2 | * unix.c - The unix-specific code for e2fsck | |
3 | * | |
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% | |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #ifdef HAVE_STDLIB_H | |
14 | #include <stdlib.h> | |
15 | #endif | |
16 | #include <string.h> | |
17 | #include <fcntl.h> | |
18 | #include <ctype.h> | |
1b6bf175 | 19 | #include <time.h> |
9ecd8bec | 20 | #ifdef HAVE_SIGNAL_H |
5596defa | 21 | #include <signal.h> |
9ecd8bec | 22 | #endif |
1b6bf175 TT |
23 | #ifdef HAVE_GETOPT_H |
24 | #include <getopt.h> | |
373b8337 TT |
25 | #else |
26 | extern char *optarg; | |
27 | extern int optind; | |
1b6bf175 TT |
28 | #endif |
29 | #include <unistd.h> | |
30 | #ifdef HAVE_ERRNO_H | |
31 | #include <errno.h> | |
32 | #endif | |
33 | #ifdef HAVE_MNTENT_H | |
34 | #include <mntent.h> | |
35 | #endif | |
36 | #include <sys/ioctl.h> | |
37 | #include <malloc.h> | |
38 | ||
39 | #include "et/com_err.h" | |
40 | #include "e2fsck.h" | |
41 | #include "problem.h" | |
42 | #include "../version.h" | |
43 | ||
1b6bf175 TT |
44 | /* Command line options */ |
45 | static int blocksize = 0; | |
46 | static int swapfs = 0; | |
47 | static int normalize_swapfs = 0; | |
48 | static int cflag = 0; /* check disk */ | |
49 | static int show_version_only = 0; | |
50 | static int force = 0; | |
51 | static int verbose = 0; | |
52 | ||
53 | static int replace_bad_blocks = 0; | |
54 | static char *bad_blocks_file = 0; | |
55 | ||
56 | static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0}; | |
57 | ||
58 | static int root_filesystem = 0; | |
59 | static int read_only_root = 0; | |
60 | ||
1b6bf175 TT |
61 | static void usage(e2fsck_t ctx) |
62 | { | |
63 | fprintf(stderr, | |
0c4a0726 | 64 | _("Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n" |
1b6bf175 | 65 | "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n" |
0c4a0726 | 66 | "\t\t[-l|-L bad_blocks_file] [-C fd] device\n"), |
5596defa | 67 | ctx->program_name); |
b55199ea | 68 | |
0c4a0726 | 69 | fprintf(stderr, _("\nEmergency help:\n" |
b55199ea TT |
70 | " -p Automatic repair (no questions)\n" |
71 | " -n Make no changes to the filesystem\n" | |
72 | " -y Assume \"yes\" to all questions\n" | |
73 | " -c Check for bad blocks\n" | |
74 | " -f Force checking even if filesystem is marked clean\n" | |
75 | " -v Be verbose\n" | |
76 | " -b superblock Use alternative superblock\n" | |
77 | " -B blocksize Force blocksize when looking for superblock\n" | |
78 | " -l bad_blocks_file Add to badblocks list\n" | |
79 | " -L bad_blocks_file Set badblocks list\n" | |
0c4a0726 | 80 | )); |
b55199ea | 81 | |
1b6bf175 TT |
82 | exit(FSCK_USAGE); |
83 | } | |
84 | ||
85 | static void show_stats(e2fsck_t ctx) | |
86 | { | |
87 | ext2_filsys fs = ctx->fs; | |
88 | int inodes, inodes_used, blocks, blocks_used; | |
89 | int dir_links; | |
90 | int num_files, num_links; | |
91 | int frag_percent; | |
92 | ||
93 | dir_links = 2 * ctx->fs_directory_count - 1; | |
94 | num_files = ctx->fs_total_count - dir_links; | |
95 | num_links = ctx->fs_links_count - dir_links; | |
96 | inodes = fs->super->s_inodes_count; | |
97 | inodes_used = (fs->super->s_inodes_count - | |
98 | fs->super->s_free_inodes_count); | |
99 | blocks = fs->super->s_blocks_count; | |
100 | blocks_used = (fs->super->s_blocks_count - | |
101 | fs->super->s_free_blocks_count); | |
102 | ||
103 | frag_percent = (10000 * ctx->fs_fragmented) / inodes_used; | |
104 | frag_percent = (frag_percent + 5) / 10; | |
105 | ||
106 | if (!verbose) { | |
0c4a0726 | 107 | printf(_("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n"), |
1b6bf175 TT |
108 | ctx->device_name, inodes_used, inodes, |
109 | frag_percent / 10, frag_percent % 10, | |
110 | blocks_used, blocks); | |
111 | return; | |
112 | } | |
0c4a0726 TT |
113 | /* |
114 | * This is a bit ugly. But I think there will nearly always be more | |
115 | * than one "thing" to report about, so I won't try writing complex | |
116 | * code to handle one/two/many forms of all words. | |
117 | * Some languages (Italian, at least) never uses the plural form | |
118 | * of foreign words, so in real life this could not be a problem. | |
119 | * md@linux.it - 2000-1-15 | |
120 | */ | |
121 | #ifdef ENABLE_NLS | |
122 | printf (_("\n%8d inodes used (%d%%)\n"), inodes_used, | |
123 | (inodes_used != 1), 100 * inodes_used / inodes); | |
124 | printf (_("%8d non-contiguous inodes (%0d.%d%%)\n"), | |
125 | ctx->fs_fragmented, frag_percent / 10, frag_percent % 10); | |
126 | printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"), | |
127 | ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count); | |
128 | printf (_("%8d blocks used (%d%%)\n" | |
129 | "%8d bad blocks\n"), blocks_used, | |
130 | 100 * blocks_used / blocks, ctx->fs_badblocks_count); | |
131 | printf (_("\n%8d regular files\n" | |
132 | "%8d directories\n" | |
133 | "%8d character device files\n" | |
134 | "%8d block device files\n" | |
135 | "%8d fifos\n" | |
136 | "%8d links\n" | |
137 | "%8d symbolic links (%d fast symbolic links)\n" | |
138 | "%8d sockets\n" | |
139 | "--------\n" | |
140 | "%8d files\n"), | |
141 | ctx->fs_regular_count, | |
142 | ctx->fs_directory_count, | |
143 | ctx->fs_chardev_count, | |
144 | ctx->fs_blockdev_count, | |
145 | ctx->fs_fifo_count, | |
146 | ctx->fs_links_count - dir_links, | |
147 | ctx->fs_symlinks_count, | |
148 | ctx->fs_fast_symlinks_count, | |
149 | ctx->fs_sockets_count, | |
150 | ctx->fs_total_count - dir_links); | |
151 | #else | |
1b6bf175 TT |
152 | printf ("\n%8d inode%s used (%d%%)\n", inodes_used, |
153 | (inodes_used != 1) ? "s" : "", | |
154 | 100 * inodes_used / inodes); | |
155 | printf ("%8d non-contiguous inodes (%0d.%d%%)\n", | |
156 | ctx->fs_fragmented, frag_percent / 10, frag_percent % 10); | |
157 | printf (" # of inodes with ind/dind/tind blocks: %d/%d/%d\n", | |
158 | ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count); | |
159 | printf ("%8d block%s used (%d%%)\n" | |
160 | "%8d bad block%s\n", blocks_used, | |
161 | (blocks_used != 1) ? "s" : "", | |
162 | 100 * blocks_used / blocks, ctx->fs_badblocks_count, | |
163 | ctx->fs_badblocks_count != 1 ? "s" : ""); | |
164 | printf ("\n%8d regular file%s\n" | |
165 | "%8d director%s\n" | |
166 | "%8d character device file%s\n" | |
167 | "%8d block device file%s\n" | |
168 | "%8d fifo%s\n" | |
169 | "%8d link%s\n" | |
170 | "%8d symbolic link%s (%d fast symbolic link%s)\n" | |
171 | "%8d socket%s\n" | |
172 | "--------\n" | |
173 | "%8d file%s\n", | |
174 | ctx->fs_regular_count, | |
175 | (ctx->fs_regular_count != 1) ? "s" : "", | |
176 | ctx->fs_directory_count, | |
177 | (ctx->fs_directory_count != 1) ? "ies" : "y", | |
178 | ctx->fs_chardev_count, | |
179 | (ctx->fs_chardev_count != 1) ? "s" : "", | |
180 | ctx->fs_blockdev_count, | |
181 | (ctx->fs_blockdev_count != 1) ? "s" : "", | |
182 | ctx->fs_fifo_count, | |
183 | (ctx->fs_fifo_count != 1) ? "s" : "", | |
184 | ctx->fs_links_count - dir_links, | |
185 | ((ctx->fs_links_count - dir_links) != 1) ? "s" : "", | |
186 | ctx->fs_symlinks_count, | |
187 | (ctx->fs_symlinks_count != 1) ? "s" : "", | |
188 | ctx->fs_fast_symlinks_count, | |
189 | (ctx->fs_fast_symlinks_count != 1) ? "s" : "", | |
190 | ctx->fs_sockets_count, (ctx->fs_sockets_count != 1) ? "s" : "", | |
191 | ctx->fs_total_count - dir_links, | |
192 | ((ctx->fs_total_count - dir_links) != 1) ? "s" : ""); | |
0c4a0726 | 193 | #endif |
1b6bf175 TT |
194 | } |
195 | ||
196 | static void check_mount(e2fsck_t ctx) | |
197 | { | |
198 | errcode_t retval; | |
199 | int mount_flags, cont, fd; | |
200 | ||
201 | retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags); | |
202 | if (retval) { | |
203 | com_err("ext2fs_check_if_mount", retval, | |
0c4a0726 | 204 | _("while determining whether %s is mounted."), |
1b6bf175 TT |
205 | ctx->filesystem_name); |
206 | return; | |
207 | } | |
208 | if (!(mount_flags & EXT2_MF_MOUNTED)) | |
209 | return; | |
210 | ||
211 | #if (defined(__linux__) && defined(HAVE_MNTENT_H)) | |
212 | /* | |
213 | * If the root is mounted read-only, then /etc/mtab is | |
214 | * probably not correct; so we won't issue a warning based on | |
215 | * it. | |
216 | */ | |
217 | fd = open(MOUNTED, O_RDWR); | |
218 | if (fd < 0) { | |
219 | if (errno == EROFS) | |
220 | return; | |
221 | } else | |
222 | close(fd); | |
223 | #endif | |
224 | ||
225 | if (ctx->options & E2F_OPT_READONLY) { | |
0c4a0726 | 226 | printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name); |
1b6bf175 TT |
227 | return; |
228 | } | |
229 | ||
0c4a0726 | 230 | printf(_("%s is mounted. "), ctx->filesystem_name); |
74033350 | 231 | if (!isatty(0) || !isatty(1)) { |
0c4a0726 | 232 | printf(_("Cannot continue, aborting.\n\n")); |
74033350 TT |
233 | exit(FSCK_ERROR); |
234 | } | |
0c4a0726 | 235 | printf(_("\n\n\007\007\007\007WARNING!!! " |
74033350 | 236 | "Running e2fsck on a mounted filesystem may cause\n" |
0c4a0726 TT |
237 | "SEVERE filesystem damage.\007\007\007\n\n")); |
238 | cont = ask_yn(_("Do you really want to continue"), -1); | |
1b6bf175 | 239 | if (!cont) { |
0c4a0726 | 240 | printf (_("check aborted.\n")); |
1b6bf175 TT |
241 | exit (0); |
242 | } | |
243 | return; | |
244 | } | |
245 | ||
1b6bf175 TT |
246 | /* |
247 | * This routine checks to see if a filesystem can be skipped; if so, | |
248 | * it will exit with E2FSCK_OK. Under some conditions it will print a | |
249 | * message explaining why a check is being forced. | |
250 | */ | |
251 | static void check_if_skip(e2fsck_t ctx) | |
252 | { | |
253 | ext2_filsys fs = ctx->fs; | |
254 | const char *reason = NULL; | |
255 | ||
256 | if (force || bad_blocks_file || cflag || swapfs) | |
257 | return; | |
258 | ||
259 | if (fs->super->s_state & EXT2_ERROR_FS) | |
0c4a0726 | 260 | reason = _("contains a file system with errors"); |
24fc5032 | 261 | else if ((fs->super->s_state & EXT2_VALID_FS) == 0) |
0c4a0726 | 262 | reason = _("was not cleanly unmounted"); |
17390c04 TT |
263 | else if ((fs->super->s_max_mnt_count >= 0) && |
264 | (fs->super->s_mnt_count >= | |
265 | (unsigned) fs->super->s_max_mnt_count)) | |
0c4a0726 | 266 | reason = _("has reached maximal mount count"); |
1b6bf175 TT |
267 | else if (fs->super->s_checkinterval && |
268 | time(0) >= (fs->super->s_lastcheck + | |
269 | fs->super->s_checkinterval)) | |
0c4a0726 | 270 | reason = _("has gone too long without being checked"); |
1b6bf175 | 271 | if (reason) { |
0c4a0726 | 272 | printf(_("%s %s, check forced.\n"), ctx->device_name, reason); |
1b6bf175 TT |
273 | return; |
274 | } | |
0c4a0726 | 275 | printf(_("%s: clean, %d/%d files, %d/%d blocks\n"), ctx->device_name, |
1b6bf175 TT |
276 | fs->super->s_inodes_count - fs->super->s_free_inodes_count, |
277 | fs->super->s_inodes_count, | |
278 | fs->super->s_blocks_count - fs->super->s_free_blocks_count, | |
279 | fs->super->s_blocks_count); | |
280 | ext2fs_close(fs); | |
281 | exit(FSCK_OK); | |
282 | } | |
283 | ||
efac9a1b TT |
284 | /* |
285 | * For completion notice | |
286 | */ | |
5596defa TT |
287 | struct percent_tbl { |
288 | int max_pass; | |
289 | int table[32]; | |
290 | }; | |
291 | struct percent_tbl e2fsck_tbl = { | |
292 | 5, { 0, 70, 90, 92, 95, 100 } | |
293 | }; | |
5596defa TT |
294 | static char bar[] = |
295 | "===============================================================" | |
296 | "==============================================================="; | |
297 | static char spaces[] = | |
298 | " " | |
299 | " "; | |
300 | ||
301 | static float calc_percent(struct percent_tbl *tbl, int pass, int curr, | |
302 | int max) | |
303 | { | |
304 | float percent; | |
305 | ||
306 | if (pass <= 0) | |
307 | return 0.0; | |
b8d164cd | 308 | if (pass > tbl->max_pass || max == 0) |
5596defa TT |
309 | return 100.0; |
310 | percent = ((float) curr) / ((float) max); | |
311 | return ((percent * (tbl->table[pass] - tbl->table[pass-1])) | |
312 | + tbl->table[pass-1]); | |
313 | } | |
314 | ||
315 | extern void e2fsck_clear_progbar(e2fsck_t ctx) | |
316 | { | |
317 | if (!(ctx->flags & E2F_FLAG_PROG_BAR)) | |
318 | return; | |
319 | ||
320 | printf("%s\r", spaces + (sizeof(spaces) - 80)); | |
321 | ctx->flags &= ~E2F_FLAG_PROG_BAR; | |
322 | } | |
323 | ||
efac9a1b TT |
324 | static int e2fsck_update_progress(e2fsck_t ctx, int pass, |
325 | unsigned long cur, unsigned long max) | |
326 | { | |
327 | const char spinner[] = "\\|/-"; | |
efac9a1b | 328 | char buf[80]; |
5596defa TT |
329 | int i; |
330 | float percent; | |
06012323 TT |
331 | int tick; |
332 | struct timeval tv; | |
17390c04 | 333 | static int dpywidth = 0; |
f75c28de TT |
334 | |
335 | if (pass == 0) | |
336 | return 0; | |
efac9a1b TT |
337 | |
338 | if (ctx->progress_fd) { | |
339 | sprintf(buf, "%d %lu %lu\n", pass, cur, max); | |
340 | write(ctx->progress_fd, buf, strlen(buf)); | |
341 | } else { | |
5596defa TT |
342 | if (ctx->flags & E2F_FLAG_PROG_SUPPRESS) |
343 | return 0; | |
17390c04 TT |
344 | if (dpywidth == 0) { |
345 | dpywidth = 66 - strlen(ctx->device_name); | |
346 | dpywidth = 8 * (dpywidth / 8); | |
347 | } | |
e4c8e885 TT |
348 | /* |
349 | * Calculate the new progress position. If the | |
350 | * percentage hasn't changed, then we skip out right | |
351 | * away. | |
352 | */ | |
353 | percent = calc_percent(&e2fsck_tbl, pass, cur, max); | |
354 | if (ctx->progress_last_percent == (int) 10 * percent) | |
355 | return 0; | |
356 | ctx->progress_last_percent = (int) 10 * percent; | |
357 | ||
358 | /* | |
359 | * If we've already updated the spinner once within | |
360 | * the last 1/8th of a second, no point doing it | |
361 | * again. | |
362 | */ | |
06012323 TT |
363 | gettimeofday(&tv, NULL); |
364 | tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8)); | |
365 | if ((tick == ctx->progress_last_time) && | |
366 | (cur != max) && (cur != 0)) | |
367 | return 0; | |
368 | ctx->progress_last_time = tick; | |
e4c8e885 TT |
369 | |
370 | /* | |
371 | * Advance the spinner, and note that the progress bar | |
372 | * will be on the screen | |
373 | */ | |
efac9a1b | 374 | ctx->progress_pos = (ctx->progress_pos+1) & 3; |
5596defa | 375 | ctx->flags |= E2F_FLAG_PROG_BAR; |
e4c8e885 | 376 | |
5596defa TT |
377 | i = ((percent * dpywidth) + 50) / 100; |
378 | printf("%s: |%s%s", ctx->device_name, | |
379 | bar + (sizeof(bar) - (i+1)), | |
380 | spaces + (sizeof(spaces) - (dpywidth - i + 1))); | |
381 | if (percent == 100.0) | |
382 | fputc('|', stdout); | |
383 | else | |
384 | fputc(spinner[ctx->progress_pos & 3], stdout); | |
385 | printf(" %4.1f%% \r", percent); | |
386 | if (percent == 100.0) | |
387 | e2fsck_clear_progbar(ctx); | |
efac9a1b TT |
388 | fflush(stdout); |
389 | } | |
390 | return 0; | |
391 | } | |
1b6bf175 TT |
392 | |
393 | #define PATH_SET "PATH=/sbin" | |
394 | ||
24fc5032 TT |
395 | static void reserve_stdio_fds(NOARGS) |
396 | { | |
397 | int fd; | |
398 | ||
399 | while (1) { | |
400 | fd = open("/dev/null", O_RDWR); | |
401 | if (fd > 2) | |
402 | break; | |
75d83bec | 403 | if (fd < 0) { |
0c4a0726 TT |
404 | fprintf(stderr, _("ERROR: Couldn't open " |
405 | "/dev/null (%s)\n"), | |
75d83bec TT |
406 | strerror(errno)); |
407 | break; | |
408 | } | |
24fc5032 TT |
409 | } |
410 | close(fd); | |
411 | } | |
412 | ||
9ecd8bec | 413 | #ifdef HAVE_SIGNAL_H |
5596defa TT |
414 | static e2fsck_t global_signal_ctx; |
415 | ||
416 | static void signal_progress_on(int sig) | |
417 | { | |
418 | e2fsck_t ctx = global_signal_ctx; | |
419 | ||
420 | if (!ctx) | |
421 | return; | |
422 | ||
423 | ctx->progress = e2fsck_update_progress; | |
424 | ctx->progress_fd = 0; | |
425 | } | |
426 | ||
427 | static void signal_progress_off(int sig) | |
428 | { | |
429 | e2fsck_t ctx = global_signal_ctx; | |
430 | ||
431 | if (!ctx) | |
432 | return; | |
433 | ||
434 | e2fsck_clear_progbar(ctx); | |
435 | ctx->progress = 0; | |
436 | } | |
9ecd8bec | 437 | #endif |
5596defa | 438 | |
1b6bf175 TT |
439 | static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) |
440 | { | |
441 | int flush = 0; | |
519149fb | 442 | int c; |
1b6bf175 TT |
443 | #ifdef MTRACE |
444 | extern void *mallwatch; | |
445 | #endif | |
446 | char *oldpath = getenv("PATH"); | |
447 | e2fsck_t ctx; | |
448 | errcode_t retval; | |
9ecd8bec | 449 | #ifdef HAVE_SIGNAL_H |
5596defa | 450 | struct sigaction sa; |
9ecd8bec | 451 | #endif |
1b6bf175 TT |
452 | |
453 | retval = e2fsck_allocate_context(&ctx); | |
454 | if (retval) | |
455 | return retval; | |
456 | ||
457 | *ret_ctx = ctx; | |
458 | ||
459 | /* Update our PATH to include /sbin */ | |
460 | if (oldpath) { | |
461 | char *newpath; | |
462 | ||
54dc7ca2 TT |
463 | newpath = (char *) malloc(sizeof (PATH_SET) + 1 + |
464 | strlen (oldpath)); | |
1b6bf175 | 465 | if (!newpath) |
f8188fff | 466 | fatal_error(ctx, "Couldn't malloc() newpath"); |
1b6bf175 TT |
467 | strcpy (newpath, PATH_SET); |
468 | strcat (newpath, ":"); | |
469 | strcat (newpath, oldpath); | |
470 | putenv (newpath); | |
471 | } else | |
472 | putenv (PATH_SET); | |
473 | ||
474 | setbuf(stdout, NULL); | |
475 | setbuf(stderr, NULL); | |
476 | initialize_ext2_error_table(); | |
477 | ||
478 | if (argc && *argv) | |
479 | ctx->program_name = *argv; | |
480 | else | |
481 | ctx->program_name = "e2fsck"; | |
efac9a1b | 482 | while ((c = getopt (argc, argv, "panyrcC:B:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF) |
1b6bf175 | 483 | switch (c) { |
efac9a1b TT |
484 | case 'C': |
485 | ctx->progress = e2fsck_update_progress; | |
486 | ctx->progress_fd = atoi(optarg); | |
487 | break; | |
1b6bf175 TT |
488 | case 'p': |
489 | case 'a': | |
490 | ctx->options |= E2F_OPT_PREEN; | |
491 | ctx->options &= ~(E2F_OPT_YES|E2F_OPT_NO); | |
492 | break; | |
493 | case 'n': | |
494 | ctx->options |= E2F_OPT_NO; | |
495 | ctx->options &= ~(E2F_OPT_YES|E2F_OPT_PREEN); | |
496 | break; | |
497 | case 'y': | |
498 | ctx->options |= E2F_OPT_YES; | |
499 | ctx->options &= ~(E2F_OPT_PREEN|E2F_OPT_NO); | |
500 | break; | |
501 | case 't': | |
8bf191e8 | 502 | #ifdef RESOURCE_TRACK |
1b6bf175 TT |
503 | if (ctx->options & E2F_OPT_TIME) |
504 | ctx->options |= E2F_OPT_TIME2; | |
505 | else | |
506 | ctx->options |= E2F_OPT_TIME; | |
8bf191e8 | 507 | #else |
0c4a0726 TT |
508 | fprintf(stderr, _("The -t option is not " |
509 | "supported on this version of e2fsck.\n")); | |
8bf191e8 | 510 | #endif |
1b6bf175 TT |
511 | break; |
512 | case 'c': | |
513 | cflag++; | |
514 | ctx->options |= E2F_OPT_CHECKBLOCKS; | |
515 | break; | |
516 | case 'r': | |
517 | /* What we do by default, anyway! */ | |
518 | break; | |
519 | case 'b': | |
520 | ctx->use_superblock = atoi(optarg); | |
521 | break; | |
522 | case 'B': | |
523 | blocksize = atoi(optarg); | |
524 | break; | |
525 | case 'I': | |
526 | ctx->inode_buffer_blocks = atoi(optarg); | |
527 | break; | |
528 | case 'P': | |
529 | ctx->process_inode_size = atoi(optarg); | |
530 | break; | |
531 | case 'L': | |
532 | replace_bad_blocks++; | |
533 | case 'l': | |
54dc7ca2 | 534 | bad_blocks_file = (char *) malloc(strlen(optarg)+1); |
1b6bf175 | 535 | if (!bad_blocks_file) |
f8188fff TT |
536 | fatal_error(ctx, |
537 | "Couldn't malloc bad_blocks_file"); | |
1b6bf175 TT |
538 | strcpy(bad_blocks_file, optarg); |
539 | break; | |
540 | case 'd': | |
541 | ctx->options |= E2F_OPT_DEBUG; | |
542 | break; | |
543 | case 'f': | |
544 | force = 1; | |
545 | break; | |
546 | case 'F': | |
547 | #ifdef BLKFLSBUF | |
548 | flush = 1; | |
549 | #else | |
0c4a0726 | 550 | fatal_error(ctx, _("-F not supported")); |
1b6bf175 TT |
551 | #endif |
552 | break; | |
553 | case 'v': | |
554 | verbose = 1; | |
555 | break; | |
556 | case 'V': | |
557 | show_version_only = 1; | |
558 | break; | |
559 | #ifdef MTRACE | |
560 | case 'M': | |
561 | mallwatch = (void *) strtol(optarg, NULL, 0); | |
562 | break; | |
563 | #endif | |
564 | case 'N': | |
565 | ctx->device_name = optarg; | |
566 | break; | |
567 | case 's': | |
568 | normalize_swapfs = 1; | |
569 | case 'S': | |
570 | swapfs = 1; | |
571 | break; | |
572 | default: | |
573 | usage(ctx); | |
574 | } | |
575 | if (show_version_only) | |
576 | return 0; | |
577 | if (optind != argc - 1) | |
578 | usage(ctx); | |
579 | if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file && | |
580 | !cflag && !swapfs) | |
581 | ctx->options |= E2F_OPT_READONLY; | |
582 | ctx->filesystem_name = argv[optind]; | |
1b6bf175 | 583 | #ifdef BLKFLSBUF |
28ffafb0 | 584 | if (flush) { |
1b6bf175 TT |
585 | int fd = open(ctx->filesystem_name, O_RDONLY, 0); |
586 | ||
587 | if (fd < 0) { | |
0c4a0726 TT |
588 | com_err("open", errno, |
589 | _("while opening %s for flushing"), | |
1b6bf175 TT |
590 | ctx->filesystem_name); |
591 | exit(FSCK_ERROR); | |
592 | } | |
28ffafb0 TT |
593 | if (fsync(fd) < 0) { |
594 | com_err("fsync", errno, | |
595 | _("while trying to flush %s"), | |
596 | ctx->filesystem_name); | |
597 | exit(FSCK_ERROR); | |
598 | } | |
1b6bf175 | 599 | if (ioctl(fd, BLKFLSBUF, 0) < 0) { |
0c4a0726 TT |
600 | com_err("BLKFLSBUF", errno, |
601 | _("while trying to flush %s"), | |
1b6bf175 TT |
602 | ctx->filesystem_name); |
603 | exit(FSCK_ERROR); | |
604 | } | |
605 | close(fd); | |
1b6bf175 | 606 | } |
28ffafb0 | 607 | #endif /* BLKFLSBUF */ |
1b6bf175 TT |
608 | if (swapfs) { |
609 | if (cflag || bad_blocks_file) { | |
0c4a0726 TT |
610 | fprintf(stderr, _("Incompatible options not " |
611 | "allowed when byte-swapping.\n")); | |
f8188fff | 612 | exit(FSCK_ERROR); |
1b6bf175 TT |
613 | } |
614 | } | |
9ecd8bec | 615 | #ifdef HAVE_SIGNAL_H |
5596defa TT |
616 | /* |
617 | * Set up signal action | |
618 | */ | |
619 | memset(&sa, 0, sizeof(struct sigaction)); | |
620 | #ifdef SA_RESTART | |
621 | sa.sa_flags = SA_RESTART; | |
622 | #endif | |
623 | global_signal_ctx = ctx; | |
624 | sa.sa_handler = signal_progress_on; | |
625 | sigaction(SIGUSR1, &sa, 0); | |
626 | sa.sa_handler = signal_progress_off; | |
627 | sigaction(SIGUSR2, &sa, 0); | |
9ecd8bec | 628 | #endif |
1b6bf175 TT |
629 | return 0; |
630 | } | |
631 | ||
632 | static const char *my_ver_string = E2FSPROGS_VERSION; | |
633 | static const char *my_ver_date = E2FSPROGS_DATE; | |
634 | ||
635 | int main (int argc, char *argv[]) | |
636 | { | |
637 | errcode_t retval = 0; | |
638 | int exit_value = FSCK_OK; | |
639 | int i; | |
640 | ext2_filsys fs = 0; | |
641 | io_manager io_ptr; | |
642 | struct ext2fs_sb *s; | |
643 | const char *lib_ver_date; | |
644 | int my_ver, lib_ver; | |
645 | e2fsck_t ctx; | |
646 | struct problem_context pctx; | |
08b21301 | 647 | int flags, run_result; |
1b6bf175 TT |
648 | |
649 | clear_problem_context(&pctx); | |
650 | #ifdef MTRACE | |
651 | mtrace(); | |
652 | #endif | |
653 | #ifdef MCHECK | |
654 | mcheck(0); | |
0c4a0726 TT |
655 | #endif |
656 | #ifdef ENABLE_NLS | |
657 | setlocale(LC_MESSAGES, ""); | |
658 | bindtextdomain(NLS_CAT_NAME, LOCALEDIR); | |
659 | textdomain(NLS_CAT_NAME); | |
1b6bf175 TT |
660 | #endif |
661 | my_ver = ext2fs_parse_version_string(my_ver_string); | |
662 | lib_ver = ext2fs_get_library_version(0, &lib_ver_date); | |
663 | if (my_ver > lib_ver) { | |
0c4a0726 TT |
664 | fprintf( stderr, _("Error: ext2fs library version " |
665 | "out of date!\n")); | |
1b6bf175 TT |
666 | show_version_only++; |
667 | } | |
668 | ||
669 | retval = PRS(argc, argv, &ctx); | |
670 | if (retval) { | |
671 | com_err("e2fsck", retval, | |
0c4a0726 | 672 | _("while trying to initialize program")); |
1b6bf175 TT |
673 | exit(1); |
674 | } | |
24fc5032 TT |
675 | reserve_stdio_fds(); |
676 | ||
8bf191e8 | 677 | #ifdef RESOURCE_TRACK |
1b6bf175 | 678 | init_resource_track(&ctx->global_rtrack); |
8bf191e8 | 679 | #endif |
1b6bf175 TT |
680 | |
681 | if (!(ctx->options & E2F_OPT_PREEN) || show_version_only) | |
0c4a0726 | 682 | fprintf (stderr, _("e2fsck %s, %s for EXT2 FS %s, %s\n"), |
1b6bf175 TT |
683 | my_ver_string, my_ver_date, EXT2FS_VERSION, |
684 | EXT2FS_DATE); | |
685 | ||
686 | if (show_version_only) { | |
0c4a0726 | 687 | fprintf(stderr, _("\tUsing %s, %s\n"), |
1b6bf175 TT |
688 | error_message(EXT2_ET_BASE), lib_ver_date); |
689 | exit(0); | |
690 | } | |
691 | ||
692 | check_mount(ctx); | |
693 | ||
694 | if (!(ctx->options & E2F_OPT_PREEN) && | |
695 | !(ctx->options & E2F_OPT_NO) && | |
696 | !(ctx->options & E2F_OPT_YES)) { | |
697 | if (!isatty (0) || !isatty (1)) | |
f8188fff | 698 | fatal_error(ctx, |
0c4a0726 | 699 | _("need terminal for interactive repairs")); |
1b6bf175 TT |
700 | } |
701 | ctx->superblock = ctx->use_superblock; | |
702 | restart: | |
703 | #if 1 | |
704 | io_ptr = unix_io_manager; | |
705 | #else | |
706 | io_ptr = test_io_manager; | |
707 | test_io_backing_manager = unix_io_manager; | |
708 | #endif | |
17390c04 TT |
709 | flags = 0; |
710 | if ((ctx->options & E2F_OPT_READONLY) == 0) | |
711 | flags |= EXT2_FLAG_RW; | |
712 | ||
1b6bf175 TT |
713 | if (ctx->superblock && blocksize) { |
714 | retval = ext2fs_open(ctx->filesystem_name, flags, | |
715 | ctx->superblock, blocksize, io_ptr, &fs); | |
716 | } else if (ctx->superblock) { | |
717 | for (i=0; possible_block_sizes[i]; i++) { | |
718 | retval = ext2fs_open(ctx->filesystem_name, flags, | |
719 | ctx->superblock, | |
720 | possible_block_sizes[i], | |
721 | io_ptr, &fs); | |
722 | if (!retval) | |
723 | break; | |
724 | } | |
725 | } else | |
726 | retval = ext2fs_open(ctx->filesystem_name, flags, | |
727 | 0, 0, io_ptr, &fs); | |
728 | if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && | |
729 | ((retval == EXT2_ET_BAD_MAGIC) || | |
730 | ((retval == 0) && ext2fs_check_desc(fs)))) { | |
731 | if (!fs || (fs->group_desc_count > 1)) { | |
0c4a0726 TT |
732 | printf(_("%s trying backup blocks...\n"), |
733 | retval ? _("Couldn't find ext2 superblock,") : | |
734 | _("Group descriptors look bad...")); | |
1b6bf175 TT |
735 | ctx->superblock = get_backup_sb(fs); |
736 | if (fs) | |
737 | ext2fs_close(fs); | |
738 | goto restart; | |
739 | } | |
740 | } | |
741 | if (retval) { | |
0c4a0726 | 742 | com_err(ctx->program_name, retval, _("while trying to open %s"), |
1b6bf175 | 743 | ctx->filesystem_name); |
24dd4028 | 744 | if (retval == EXT2_ET_REV_TOO_HIGH) { |
0c4a0726 | 745 | printf(_("The filesystem revision is apparently " |
24dd4028 TT |
746 | "too high for this version of e2fsck.\n" |
747 | "(Or the filesystem superblock " | |
0c4a0726 | 748 | "is corrupt)\n\n")); |
24dd4028 TT |
749 | fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); |
750 | } else if (retval == EXT2_ET_SHORT_READ) | |
0c4a0726 | 751 | printf(_("Could this be a zero-length partition?\n")); |
1b6bf175 | 752 | else if ((retval == EPERM) || (retval == EACCES)) |
0c4a0726 TT |
753 | printf(_("You must have %s access to the " |
754 | "filesystem or be root\n"), | |
1b6bf175 TT |
755 | (ctx->options & E2F_OPT_READONLY) ? |
756 | "r/o" : "r/w"); | |
757 | else if (retval == ENXIO) | |
0c4a0726 | 758 | printf(_("Possibly non-existent or swap device?\n")); |
68227542 TT |
759 | #ifdef EROFS |
760 | else if (retval == EROFS) | |
0c4a0726 | 761 | printf(_("Disk write-protected; use the -n option " |
68227542 | 762 | "to do a read-only\n" |
0c4a0726 | 763 | "check of the device.\n")); |
68227542 | 764 | #endif |
1b6bf175 TT |
765 | else |
766 | fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); | |
f8188fff | 767 | exit(FSCK_ERROR); |
1b6bf175 TT |
768 | } |
769 | ctx->fs = fs; | |
54dc7ca2 | 770 | fs->priv_data = ctx; |
1b6bf175 TT |
771 | #ifdef EXT2_CURRENT_REV |
772 | if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) { | |
773 | com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH, | |
0c4a0726 | 774 | _("while trying to open %s"), |
1b6bf175 | 775 | ctx->filesystem_name); |
f8188fff | 776 | get_newer: |
0c4a0726 | 777 | fatal_error(ctx, _("Get a newer version of e2fsck!")); |
1b6bf175 TT |
778 | } |
779 | #endif | |
17390c04 TT |
780 | s = (struct ext2fs_sb *) fs->super; |
781 | /* | |
782 | * Check to see if we need to do ext3-style recovery. If so, | |
783 | * do it, and then restart the fsck. | |
784 | */ | |
785 | if (s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) { | |
786 | printf("%s: reading journal for ext3 filesystem...\n", | |
787 | ctx->filesystem_name); | |
788 | ext2fs_close(fs); | |
789 | retval = e2fsck_run_ext3_journal(ctx->filesystem_name); | |
790 | if (retval) { | |
791 | com_err(ctx->program_name, retval, | |
792 | ": couldn't load ext3 journal for %s", | |
793 | ctx->filesystem_name); | |
794 | exit(FSCK_ERROR); | |
795 | } | |
796 | goto restart; | |
797 | } | |
1b6bf175 TT |
798 | /* |
799 | * Check for compatibility with the feature sets. We need to | |
800 | * be more stringent than ext2fs_open(). | |
801 | */ | |
1b6bf175 | 802 | if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) || |
8144d679 | 803 | (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) { |
1b6bf175 TT |
804 | com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE, |
805 | "(%s)", ctx->filesystem_name); | |
f8188fff | 806 | goto get_newer; |
1b6bf175 TT |
807 | } |
808 | if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { | |
809 | com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE, | |
810 | "(%s)", ctx->filesystem_name); | |
811 | goto get_newer; | |
812 | } | |
1917875f TT |
813 | #ifdef ENABLE_COMPRESSION |
814 | if (s->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) | |
815 | com_err(ctx->program_name, 0, | |
816 | _("Warning: compression support is experimental.\n")); | |
817 | #endif | |
5596defa TT |
818 | if (ctx->device_name == 0 && |
819 | (s->s_volume_name[0] != 0)) { | |
820 | char *cp = malloc(sizeof(s->s_volume_name)+1); | |
821 | if (cp) { | |
822 | strncpy(cp, s->s_volume_name, | |
823 | sizeof(s->s_volume_name)); | |
824 | cp[sizeof(s->s_volume_name)] = 0; | |
825 | ctx->device_name = cp; | |
826 | } | |
827 | } | |
828 | if (ctx->device_name == 0) | |
829 | ctx->device_name = ctx->filesystem_name; | |
1b6bf175 TT |
830 | |
831 | /* | |
832 | * If the user specified a specific superblock, presumably the | |
833 | * master superblock has been trashed. So we mark the | |
834 | * superblock as dirty, so it can be written out. | |
835 | */ | |
836 | if (ctx->superblock && | |
837 | !(ctx->options & E2F_OPT_READONLY)) | |
838 | ext2fs_mark_super_dirty(fs); | |
839 | ||
840 | /* | |
841 | * Don't overwrite the backup superblock and block | |
842 | * descriptors, until we're sure the filesystem is OK.... | |
843 | */ | |
844 | fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; | |
845 | ||
846 | ehandler_init(fs->io); | |
847 | ||
848 | if (ctx->superblock) | |
849 | set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0); | |
850 | check_super_block(ctx); | |
a02ce9df | 851 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
f8188fff | 852 | exit(FSCK_ERROR); |
1b6bf175 TT |
853 | check_if_skip(ctx); |
854 | if (bad_blocks_file) | |
855 | read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks); | |
856 | else if (cflag) | |
857 | test_disk(ctx); | |
a02ce9df | 858 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
f8188fff | 859 | exit(FSCK_ERROR); |
1b6bf175 TT |
860 | |
861 | if (normalize_swapfs) { | |
862 | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == | |
863 | ext2fs_native_flag()) { | |
0c4a0726 TT |
864 | fprintf(stderr, _("%s: Filesystem byte order " |
865 | "already normalized.\n"), ctx->device_name); | |
f8188fff | 866 | exit(FSCK_ERROR); |
1b6bf175 TT |
867 | } |
868 | } | |
f8188fff | 869 | if (swapfs) { |
1b6bf175 | 870 | swap_filesys(ctx); |
a02ce9df | 871 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
f8188fff TT |
872 | exit(FSCK_ERROR); |
873 | } | |
1b6bf175 TT |
874 | |
875 | /* | |
876 | * Mark the system as valid, 'til proven otherwise | |
877 | */ | |
878 | ext2fs_mark_valid(fs); | |
879 | ||
880 | retval = ext2fs_read_bb_inode(fs, &fs->badblocks); | |
881 | if (retval) { | |
882 | com_err(ctx->program_name, retval, | |
0c4a0726 | 883 | _("while reading bad blocks inode")); |
1b6bf175 | 884 | preenhalt(ctx); |
0c4a0726 TT |
885 | printf(_("This doesn't bode well," |
886 | " but we'll try to go on...\n")); | |
1b6bf175 | 887 | } |
08b21301 TT |
888 | |
889 | run_result = e2fsck_run(ctx); | |
5596defa | 890 | e2fsck_clear_progbar(ctx); |
08b21301 | 891 | if (run_result == E2F_FLAG_RESTART) { |
0c4a0726 | 892 | printf(_("Restarting e2fsck from the beginning...\n")); |
1b6bf175 TT |
893 | retval = e2fsck_reset_context(ctx); |
894 | if (retval) { | |
895 | com_err(ctx->program_name, retval, | |
0c4a0726 | 896 | _("while resetting context")); |
1b6bf175 TT |
897 | exit(1); |
898 | } | |
73f17cfc | 899 | ext2fs_close(fs); |
1b6bf175 TT |
900 | goto restart; |
901 | } | |
a02ce9df | 902 | if (run_result & E2F_FLAG_SIGNAL_MASK) |
08b21301 TT |
903 | exit(FSCK_ERROR); |
904 | if (run_result & E2F_FLAG_CANCEL) | |
905 | ext2fs_unmark_valid(fs); | |
1b6bf175 TT |
906 | |
907 | #ifdef MTRACE | |
908 | mtrace_print("Cleanup"); | |
909 | #endif | |
910 | if (ext2fs_test_changed(fs)) { | |
911 | exit_value = FSCK_NONDESTRUCT; | |
912 | if (!(ctx->options & E2F_OPT_PREEN)) | |
0c4a0726 | 913 | printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"), |
1b6bf175 TT |
914 | ctx->device_name); |
915 | if (root_filesystem && !read_only_root) { | |
0c4a0726 | 916 | printf(_("%s: ***** REBOOT LINUX *****\n"), |
1b6bf175 TT |
917 | ctx->device_name); |
918 | exit_value = FSCK_REBOOT; | |
919 | } | |
920 | } | |
921 | if (ext2fs_test_valid(fs)) | |
922 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; | |
923 | else | |
924 | exit_value = FSCK_UNCORRECTED; | |
925 | if (!(ctx->options & E2F_OPT_READONLY)) { | |
926 | if (ext2fs_test_valid(fs)) { | |
927 | if (!(fs->super->s_state & EXT2_VALID_FS)) | |
928 | exit_value = FSCK_NONDESTRUCT; | |
929 | fs->super->s_state = EXT2_VALID_FS; | |
930 | } else | |
931 | fs->super->s_state &= ~EXT2_VALID_FS; | |
932 | fs->super->s_mnt_count = 0; | |
933 | fs->super->s_lastcheck = time(NULL); | |
934 | ext2fs_mark_super_dirty(fs); | |
935 | } | |
936 | show_stats(ctx); | |
937 | ||
f8188fff | 938 | e2fsck_write_bitmaps(ctx); |
1b6bf175 | 939 | |
8bf191e8 | 940 | #ifdef RESOURCE_TRACK |
1b6bf175 TT |
941 | if (ctx->options & E2F_OPT_TIME) |
942 | print_resource_track(NULL, &ctx->global_rtrack); | |
8bf191e8 | 943 | #endif |
1b6bf175 | 944 | |
1dde43f0 | 945 | ext2fs_close(fs); |
7142db08 TT |
946 | ctx->fs = NULL; |
947 | e2fsck_free_context(ctx); | |
1b6bf175 TT |
948 | |
949 | return exit_value; | |
950 | } |