blob: 0eaac17c0f1871562485e3613c6d174d45110b9d
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * fsck.c - a file system consistency checker for Linux. |
4 | * |
5 | * (C) 1991, 1992 Linus Torvalds. |
6 | * |
7 | * Licensed under GPLv2, see file LICENSE in this source tree. |
8 | */ |
9 | |
10 | /* |
11 | * 09.11.91 - made the first rudimentary functions |
12 | * |
13 | * 10.11.91 - updated, does checking, no repairs yet. |
14 | * Sent out to the mailing-list for testing. |
15 | * |
16 | * 14.11.91 - Testing seems to have gone well. Added some |
17 | * correction-code, and changed some functions. |
18 | * |
19 | * 15.11.91 - More correction code. Hopefully it notices most |
20 | * cases now, and tries to do something about them. |
21 | * |
22 | * 16.11.91 - More corrections (thanks to Mika Jalava). Most |
23 | * things seem to work now. Yeah, sure. |
24 | * |
25 | * 19.04.92 - Had to start over again from this old version, as a |
26 | * kernel bug ate my enhanced fsck in february. |
27 | * |
28 | * 28.02.93 - added support for different directory entry sizes.. |
29 | * |
30 | * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with |
31 | * superblock information |
32 | * |
33 | * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform |
34 | * to that required by fsutil |
35 | * |
36 | * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu) |
37 | * Added support for file system valid flag. Also |
38 | * added program_version variable and output of |
39 | * program name and version number when program |
40 | * is executed. |
41 | * |
42 | * 30.10.94 - added support for v2 filesystem |
43 | * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) |
44 | * |
45 | * 10.12.94 - added test to prevent checking of mounted fs adapted |
46 | * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck |
47 | * program. (Daniel Quinlan, quinlan@yggdrasil.com) |
48 | * |
49 | * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such |
50 | * for modern libcs (janl@math.uio.no, Nicolai Langfeldt) |
51 | * |
52 | * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk |
53 | * (Russell King). He made them for ARM. It would seem |
54 | * that the ARM is powerful enough to do this in C whereas |
55 | * i386 and m64k must use assembly to get it fast >:-) |
56 | * This should make minix fsck system-independent. |
57 | * (janl@math.uio.no, Nicolai Langfeldt) |
58 | * |
59 | * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler |
60 | * warnings. Added mc68k bitops from |
61 | * Joerg Dorchain <dorchain@mpi-sb.mpg.de>. |
62 | * |
63 | * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by |
64 | * Andreas Schwab. |
65 | * |
66 | * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org> |
67 | * - added Native Language Support |
68 | * |
69 | * |
70 | * I've had no time to add comments - hopefully the function names |
71 | * are comments enough. As with all file system checkers, this assumes |
72 | * the file system is quiescent - don't use it on a mounted device |
73 | * unless you can be sure nobody is writing to it (and remember that the |
74 | * kernel can write to it when it searches for files). |
75 | * |
76 | * Usage: fsck [-larvsm] device |
77 | * -l for a listing of all the filenames |
78 | * -a for automatic repairs (not implemented) |
79 | * -r for repairs (interactive) (not implemented) |
80 | * -v for verbose (tells how many files) |
81 | * -s for superblock info |
82 | * -m for minix-like "mode not cleared" warnings |
83 | * -f force filesystem check even if filesystem marked as valid |
84 | * |
85 | * The device may be a block device or a image of one, but this isn't |
86 | * enforced (but it's not much fun on a character device :-). |
87 | */ |
88 | //config:config FSCK_MINIX |
89 | //config: bool "fsck_minix" |
90 | //config: default y |
91 | //config: help |
92 | //config: The minix filesystem is a nice, small, compact, read-write filesystem |
93 | //config: with little overhead. It is not a journaling filesystem however and |
94 | //config: can experience corruption if it is not properly unmounted or if the |
95 | //config: power goes off in the middle of a write. This utility allows you to |
96 | //config: check for and attempt to repair any corruption that occurs to a minix |
97 | //config: filesystem. |
98 | |
99 | //applet:IF_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, BB_DIR_SBIN, BB_SUID_DROP, fsck_minix)) |
100 | |
101 | //kbuild:lib-$(CONFIG_FSCK_MINIX) += fsck_minix.o |
102 | |
103 | //usage:#define fsck_minix_trivial_usage |
104 | //usage: "[-larvsmf] BLOCKDEV" |
105 | //usage:#define fsck_minix_full_usage "\n\n" |
106 | //usage: "Check MINIX filesystem\n" |
107 | //usage: "\n -l List all filenames" |
108 | //usage: "\n -r Perform interactive repairs" |
109 | //usage: "\n -a Perform automatic repairs" |
110 | //usage: "\n -v Verbose" |
111 | //usage: "\n -s Output superblock information" |
112 | //usage: "\n -m Show \"mode not cleared\" warnings" |
113 | //usage: "\n -f Force file system check" |
114 | |
115 | #include <mntent.h> |
116 | #include "libbb.h" |
117 | #include "minix.h" |
118 | |
119 | #ifndef BLKGETSIZE |
120 | #define BLKGETSIZE _IO(0x12,96) /* return device size */ |
121 | #endif |
122 | |
123 | struct BUG_bad_inode_size { |
124 | char BUG_bad_inode1_size[(INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1]; |
125 | #if ENABLE_FEATURE_MINIX2 |
126 | char BUG_bad_inode2_size[(INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1]; |
127 | #endif |
128 | }; |
129 | |
130 | enum { |
131 | #ifdef UNUSED |
132 | MINIX1_LINK_MAX = 250, |
133 | MINIX2_LINK_MAX = 65530, |
134 | MINIX_I_MAP_SLOTS = 8, |
135 | MINIX_Z_MAP_SLOTS = 64, |
136 | MINIX_V1 = 0x0001, /* original minix fs */ |
137 | MINIX_V2 = 0x0002, /* minix V2 fs */ |
138 | #endif |
139 | MINIX_NAME_MAX = 255, /* # chars in a file name */ |
140 | }; |
141 | |
142 | |
143 | #if !ENABLE_FEATURE_MINIX2 |
144 | enum { version2 = 0 }; |
145 | #endif |
146 | |
147 | enum { MAX_DEPTH = 32 }; |
148 | |
149 | enum { dev_fd = 3 }; |
150 | |
151 | struct globals { |
152 | #if ENABLE_FEATURE_MINIX2 |
153 | smallint version2; |
154 | #endif |
155 | smallint changed; /* is filesystem modified? */ |
156 | smallint errors_uncorrected; /* flag if some error was not corrected */ |
157 | smallint termios_set; |
158 | smallint dirsize; |
159 | smallint namelen; |
160 | const char *device_name; |
161 | int directory, regular, blockdev, chardev, links, symlinks, total; |
162 | char *inode_buffer; |
163 | |
164 | char *inode_map; |
165 | char *zone_map; |
166 | |
167 | unsigned char *inode_count; |
168 | unsigned char *zone_count; |
169 | |
170 | /* File-name data */ |
171 | int name_depth; |
172 | char *name_component[MAX_DEPTH+1]; |
173 | |
174 | /* Bigger stuff */ |
175 | struct termios sv_termios; |
176 | char superblock_buffer[BLOCK_SIZE]; |
177 | char add_zone_ind_blk[BLOCK_SIZE]; |
178 | char add_zone_dind_blk[BLOCK_SIZE]; |
179 | IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];) |
180 | char check_file_blk[BLOCK_SIZE]; |
181 | |
182 | /* File-name data */ |
183 | char current_name[MAX_DEPTH * MINIX_NAME_MAX]; |
184 | }; |
185 | #define G (*ptr_to_globals) |
186 | #if ENABLE_FEATURE_MINIX2 |
187 | #define version2 (G.version2 ) |
188 | #endif |
189 | #define changed (G.changed ) |
190 | #define errors_uncorrected (G.errors_uncorrected ) |
191 | #define termios_set (G.termios_set ) |
192 | #define dirsize (G.dirsize ) |
193 | #define namelen (G.namelen ) |
194 | #define device_name (G.device_name ) |
195 | #define directory (G.directory ) |
196 | #define regular (G.regular ) |
197 | #define blockdev (G.blockdev ) |
198 | #define chardev (G.chardev ) |
199 | #define links (G.links ) |
200 | #define symlinks (G.symlinks ) |
201 | #define total (G.total ) |
202 | #define inode_buffer (G.inode_buffer ) |
203 | #define inode_map (G.inode_map ) |
204 | #define zone_map (G.zone_map ) |
205 | #define inode_count (G.inode_count ) |
206 | #define zone_count (G.zone_count ) |
207 | #define name_depth (G.name_depth ) |
208 | #define name_component (G.name_component ) |
209 | #define sv_termios (G.sv_termios ) |
210 | #define superblock_buffer (G.superblock_buffer ) |
211 | #define add_zone_ind_blk (G.add_zone_ind_blk ) |
212 | #define add_zone_dind_blk (G.add_zone_dind_blk ) |
213 | #define add_zone_tind_blk (G.add_zone_tind_blk ) |
214 | #define check_file_blk (G.check_file_blk ) |
215 | #define current_name (G.current_name ) |
216 | #define INIT_G() do { \ |
217 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
218 | dirsize = 16; \ |
219 | namelen = 14; \ |
220 | current_name[0] = '/'; \ |
221 | /*current_name[1] = '\0';*/ \ |
222 | name_component[0] = ¤t_name[0]; \ |
223 | } while (0) |
224 | |
225 | |
226 | #define OPTION_STR "larvsmf" |
227 | enum { |
228 | OPT_l = (1 << 0), |
229 | OPT_a = (1 << 1), |
230 | OPT_r = (1 << 2), |
231 | OPT_v = (1 << 3), |
232 | OPT_s = (1 << 4), |
233 | OPT_w = (1 << 5), |
234 | OPT_f = (1 << 6), |
235 | }; |
236 | #define OPT_list (option_mask32 & OPT_l) |
237 | #define OPT_automatic (option_mask32 & OPT_a) |
238 | #define OPT_repair (option_mask32 & OPT_r) |
239 | #define OPT_verbose (option_mask32 & OPT_v) |
240 | #define OPT_show (option_mask32 & OPT_s) |
241 | #define OPT_warn_mode (option_mask32 & OPT_w) |
242 | #define OPT_force (option_mask32 & OPT_f) |
243 | /* non-automatic repairs requested? */ |
244 | #define OPT_manual ((option_mask32 & (OPT_a|OPT_r)) == OPT_r) |
245 | |
246 | |
247 | #define Inode1 (((struct minix1_inode *) inode_buffer)-1) |
248 | #define Inode2 (((struct minix2_inode *) inode_buffer)-1) |
249 | |
250 | #define Super (*(struct minix_superblock *)(superblock_buffer)) |
251 | |
252 | #if ENABLE_FEATURE_MINIX2 |
253 | # define ZONES ((unsigned)(version2 ? Super.s_zones : Super.s_nzones)) |
254 | #else |
255 | # define ZONES ((unsigned)(Super.s_nzones)) |
256 | #endif |
257 | #define INODES ((unsigned)Super.s_ninodes) |
258 | #define IMAPS ((unsigned)Super.s_imap_blocks) |
259 | #define ZMAPS ((unsigned)Super.s_zmap_blocks) |
260 | #define FIRSTZONE ((unsigned)Super.s_firstdatazone) |
261 | #define ZONESIZE ((unsigned)Super.s_log_zone_size) |
262 | #define MAXSIZE ((unsigned)Super.s_max_size) |
263 | #define MAGIC (Super.s_magic) |
264 | |
265 | /* gcc likes this more (code is smaller) than macro variant */ |
266 | static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n) |
267 | { |
268 | return (size + n-1) / n; |
269 | } |
270 | |
271 | #if !ENABLE_FEATURE_MINIX2 |
272 | #define INODE_BLOCKS div_roundup(INODES, MINIX1_INODES_PER_BLOCK) |
273 | #else |
274 | #define INODE_BLOCKS div_roundup(INODES, \ |
275 | (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK)) |
276 | #endif |
277 | |
278 | #define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) |
279 | #define NORM_FIRSTZONE (2 + IMAPS + ZMAPS + INODE_BLOCKS) |
280 | |
281 | /* Before you ask "where they come from?": */ |
282 | /* setbit/clrbit are supplied by sys/param.h */ |
283 | |
284 | static int minix_bit(const char *a, unsigned i) |
285 | { |
286 | return (a[i >> 3] & (1<<(i & 7))); |
287 | } |
288 | |
289 | static void minix_setbit(char *a, unsigned i) |
290 | { |
291 | setbit(a, i); |
292 | changed = 1; |
293 | } |
294 | static void minix_clrbit(char *a, unsigned i) |
295 | { |
296 | clrbit(a, i); |
297 | changed = 1; |
298 | } |
299 | |
300 | /* Note: do not assume 0/1, it is 0/nonzero */ |
301 | #define zone_in_use(x) (minix_bit(zone_map,(x)-FIRSTZONE+1)) |
302 | #define inode_in_use(x) (minix_bit(inode_map,(x))) |
303 | |
304 | #define mark_inode(x) (minix_setbit(inode_map,(x))) |
305 | #define unmark_inode(x) (minix_clrbit(inode_map,(x))) |
306 | |
307 | #define mark_zone(x) (minix_setbit(zone_map,(x)-FIRSTZONE+1)) |
308 | #define unmark_zone(x) (minix_clrbit(zone_map,(x)-FIRSTZONE+1)) |
309 | |
310 | |
311 | static void recursive_check(unsigned ino); |
312 | #if ENABLE_FEATURE_MINIX2 |
313 | static void recursive_check2(unsigned ino); |
314 | #endif |
315 | |
316 | static void die(const char *str) NORETURN; |
317 | static void die(const char *str) |
318 | { |
319 | if (termios_set) |
320 | tcsetattr_stdin_TCSANOW(&sv_termios); |
321 | bb_error_msg_and_die("%s", str); |
322 | } |
323 | |
324 | static void push_filename(const char *name) |
325 | { |
326 | // /dir/dir/dir/file |
327 | // ^ ^ ^ |
328 | // [0] [1] [2] <-name_component[i] |
329 | if (name_depth < MAX_DEPTH) { |
330 | int len; |
331 | char *p = name_component[name_depth]; |
332 | *p++ = '/'; |
333 | len = sprintf(p, "%.*s", namelen, name); |
334 | name_component[name_depth + 1] = p + len; |
335 | } |
336 | name_depth++; |
337 | } |
338 | |
339 | static void pop_filename(void) |
340 | { |
341 | name_depth--; |
342 | if (name_depth < MAX_DEPTH) { |
343 | *name_component[name_depth] = '\0'; |
344 | if (!name_depth) { |
345 | current_name[0] = '/'; |
346 | current_name[1] = '\0'; |
347 | } |
348 | } |
349 | } |
350 | |
351 | static int ask(const char *string, int def) |
352 | { |
353 | int c; |
354 | |
355 | if (!OPT_repair) { |
356 | bb_putchar('\n'); |
357 | errors_uncorrected = 1; |
358 | return 0; |
359 | } |
360 | if (OPT_automatic) { |
361 | bb_putchar('\n'); |
362 | if (!def) |
363 | errors_uncorrected = 1; |
364 | return def; |
365 | } |
366 | printf(def ? "%s (y/n)? " : "%s (n/y)? ", string); |
367 | for (;;) { |
368 | fflush_all(); |
369 | c = getchar(); |
370 | if (c == EOF) { |
371 | if (!def) |
372 | errors_uncorrected = 1; |
373 | return def; |
374 | } |
375 | if (c == '\n') |
376 | break; |
377 | c |= 0x20; /* tolower */ |
378 | if (c == 'y') { |
379 | def = 1; |
380 | break; |
381 | } |
382 | if (c == 'n') { |
383 | def = 0; |
384 | break; |
385 | } |
386 | } |
387 | if (def) |
388 | puts("y"); |
389 | else { |
390 | puts("n"); |
391 | errors_uncorrected = 1; |
392 | } |
393 | return def; |
394 | } |
395 | |
396 | /* |
397 | * Make certain that we aren't checking a filesystem that is on a |
398 | * mounted partition. Code adapted from e2fsck, Copyright (C) 1993, |
399 | * 1994 Theodore Ts'o. Also licensed under GPL. |
400 | */ |
401 | static void check_mount(void) |
402 | { |
403 | if (find_mount_point(device_name, 0)) { |
404 | int cont; |
405 | #if ENABLE_FEATURE_MTAB_SUPPORT |
406 | /* |
407 | * If the root is mounted read-only, then /etc/mtab is |
408 | * probably not correct; so we won't issue a warning based on |
409 | * it. |
410 | */ |
411 | int fd = open(bb_path_mtab_file, O_RDWR); |
412 | |
413 | if (fd < 0 && errno == EROFS) |
414 | return; |
415 | close(fd); |
416 | #endif |
417 | printf("%s is mounted. ", device_name); |
418 | cont = 0; |
419 | if (isatty(0) && isatty(1)) |
420 | cont = ask("Do you really want to continue", 0); |
421 | if (!cont) { |
422 | puts("Check aborted"); |
423 | exit(EXIT_SUCCESS); |
424 | } |
425 | } |
426 | } |
427 | |
428 | /* |
429 | * check_zone_nr checks to see that *nr is a valid zone nr. If it |
430 | * isn't, it will possibly be repaired. Check_zone_nr sets *corrected |
431 | * if an error was corrected, and returns the zone (0 for no zone |
432 | * or a bad zone-number). |
433 | */ |
434 | static int check_zone_nr2(uint32_t *nr, smallint *corrected) |
435 | { |
436 | const char *msg; |
437 | if (!*nr) |
438 | return 0; |
439 | if (*nr < FIRSTZONE) |
440 | msg = "< FIRSTZONE"; |
441 | else if (*nr >= ZONES) |
442 | msg = ">= ZONES"; |
443 | else |
444 | return *nr; |
445 | printf("Zone nr %s in file '%s'. ", msg, current_name); |
446 | if (ask("Remove block", 1)) { |
447 | *nr = 0; |
448 | *corrected = 1; |
449 | } |
450 | return 0; |
451 | } |
452 | |
453 | static int check_zone_nr(uint16_t *nr, smallint *corrected) |
454 | { |
455 | uint32_t nr32 = *nr; |
456 | int r = check_zone_nr2(&nr32, corrected); |
457 | *nr = (uint16_t)nr32; |
458 | return r; |
459 | } |
460 | |
461 | /* |
462 | * read-block reads block nr into the buffer at addr. |
463 | */ |
464 | static void read_block(unsigned nr, void *addr) |
465 | { |
466 | if (!nr) { |
467 | memset(addr, 0, BLOCK_SIZE); |
468 | return; |
469 | } |
470 | xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET); |
471 | if (BLOCK_SIZE != full_read(dev_fd, addr, BLOCK_SIZE)) { |
472 | printf("%s: bad block %u in file '%s'\n", |
473 | bb_msg_read_error, nr, current_name); |
474 | errors_uncorrected = 1; |
475 | memset(addr, 0, BLOCK_SIZE); |
476 | } |
477 | } |
478 | |
479 | /* |
480 | * write_block writes block nr to disk. |
481 | */ |
482 | static void write_block(unsigned nr, void *addr) |
483 | { |
484 | if (!nr) |
485 | return; |
486 | if (nr < FIRSTZONE || nr >= ZONES) { |
487 | puts("Internal error: trying to write bad block\n" |
488 | "Write request ignored"); |
489 | errors_uncorrected = 1; |
490 | return; |
491 | } |
492 | xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET); |
493 | if (BLOCK_SIZE != full_write(dev_fd, addr, BLOCK_SIZE)) { |
494 | printf("%s: bad block %u in file '%s'\n", |
495 | bb_msg_write_error, nr, current_name); |
496 | errors_uncorrected = 1; |
497 | } |
498 | } |
499 | |
500 | /* |
501 | * map_block calculates the absolute block nr of a block in a file. |
502 | * It sets 'changed' if the inode has needed changing, and re-writes |
503 | * any indirect blocks with errors. |
504 | */ |
505 | static int map_block(struct minix1_inode *inode, unsigned blknr) |
506 | { |
507 | uint16_t ind[BLOCK_SIZE >> 1]; |
508 | int block, result; |
509 | smallint blk_chg; |
510 | |
511 | if (blknr < 7) |
512 | return check_zone_nr(inode->i_zone + blknr, &changed); |
513 | blknr -= 7; |
514 | if (blknr < 512) { |
515 | block = check_zone_nr(inode->i_zone + 7, &changed); |
516 | goto common; |
517 | } |
518 | blknr -= 512; |
519 | block = check_zone_nr(inode->i_zone + 8, &changed); |
520 | read_block(block, ind); /* double indirect */ |
521 | blk_chg = 0; |
522 | result = check_zone_nr(&ind[blknr / 512], &blk_chg); |
523 | if (blk_chg) |
524 | write_block(block, ind); |
525 | block = result; |
526 | common: |
527 | read_block(block, ind); |
528 | blk_chg = 0; |
529 | result = check_zone_nr(&ind[blknr % 512], &blk_chg); |
530 | if (blk_chg) |
531 | write_block(block, ind); |
532 | return result; |
533 | } |
534 | |
535 | #if ENABLE_FEATURE_MINIX2 |
536 | static int map_block2(struct minix2_inode *inode, unsigned blknr) |
537 | { |
538 | uint32_t ind[BLOCK_SIZE >> 2]; |
539 | int block, result; |
540 | smallint blk_chg; |
541 | |
542 | if (blknr < 7) |
543 | return check_zone_nr2(inode->i_zone + blknr, &changed); |
544 | blknr -= 7; |
545 | if (blknr < 256) { |
546 | block = check_zone_nr2(inode->i_zone + 7, &changed); |
547 | goto common2; |
548 | } |
549 | blknr -= 256; |
550 | if (blknr < 256 * 256) { |
551 | block = check_zone_nr2(inode->i_zone + 8, &changed); |
552 | goto common1; |
553 | } |
554 | blknr -= 256 * 256; |
555 | block = check_zone_nr2(inode->i_zone + 9, &changed); |
556 | read_block(block, ind); /* triple indirect */ |
557 | blk_chg = 0; |
558 | result = check_zone_nr2(&ind[blknr / (256 * 256)], &blk_chg); |
559 | if (blk_chg) |
560 | write_block(block, ind); |
561 | block = result; |
562 | common1: |
563 | read_block(block, ind); /* double indirect */ |
564 | blk_chg = 0; |
565 | result = check_zone_nr2(&ind[(blknr / 256) % 256], &blk_chg); |
566 | if (blk_chg) |
567 | write_block(block, ind); |
568 | block = result; |
569 | common2: |
570 | read_block(block, ind); |
571 | blk_chg = 0; |
572 | result = check_zone_nr2(&ind[blknr % 256], &blk_chg); |
573 | if (blk_chg) |
574 | write_block(block, ind); |
575 | return result; |
576 | } |
577 | #endif |
578 | |
579 | static void write_superblock(void) |
580 | { |
581 | /* |
582 | * Set the state of the filesystem based on whether or not there |
583 | * are uncorrected errors. The filesystem valid flag is |
584 | * unconditionally set if we get this far. |
585 | */ |
586 | Super.s_state |= MINIX_VALID_FS | MINIX_ERROR_FS; |
587 | if (!errors_uncorrected) |
588 | Super.s_state &= ~MINIX_ERROR_FS; |
589 | |
590 | xlseek(dev_fd, BLOCK_SIZE, SEEK_SET); |
591 | if (BLOCK_SIZE != full_write(dev_fd, superblock_buffer, BLOCK_SIZE)) |
592 | die("can't write superblock"); |
593 | } |
594 | |
595 | static void write_tables(void) |
596 | { |
597 | write_superblock(); |
598 | |
599 | if (IMAPS * BLOCK_SIZE != write(dev_fd, inode_map, IMAPS * BLOCK_SIZE)) |
600 | die("can't write inode map"); |
601 | if (ZMAPS * BLOCK_SIZE != write(dev_fd, zone_map, ZMAPS * BLOCK_SIZE)) |
602 | die("can't write zone map"); |
603 | if (INODE_BUFFER_SIZE != write(dev_fd, inode_buffer, INODE_BUFFER_SIZE)) |
604 | die("can't write inodes"); |
605 | } |
606 | |
607 | static void get_dirsize(void) |
608 | { |
609 | int block; |
610 | char blk[BLOCK_SIZE]; |
611 | int size; |
612 | |
613 | #if ENABLE_FEATURE_MINIX2 |
614 | if (version2) |
615 | block = Inode2[MINIX_ROOT_INO].i_zone[0]; |
616 | else |
617 | #endif |
618 | block = Inode1[MINIX_ROOT_INO].i_zone[0]; |
619 | read_block(block, blk); |
620 | for (size = 16; size < BLOCK_SIZE; size <<= 1) { |
621 | if (strcmp(blk + size + 2, "..") == 0) { |
622 | dirsize = size; |
623 | namelen = size - 2; |
624 | return; |
625 | } |
626 | } |
627 | /* use defaults */ |
628 | } |
629 | |
630 | static void read_superblock(void) |
631 | { |
632 | xlseek(dev_fd, BLOCK_SIZE, SEEK_SET); |
633 | if (BLOCK_SIZE != full_read(dev_fd, superblock_buffer, BLOCK_SIZE)) |
634 | die("can't read superblock"); |
635 | /* already initialized to: |
636 | namelen = 14; |
637 | dirsize = 16; |
638 | version2 = 0; |
639 | */ |
640 | if (MAGIC == MINIX1_SUPER_MAGIC) { |
641 | } else if (MAGIC == MINIX1_SUPER_MAGIC2) { |
642 | namelen = 30; |
643 | dirsize = 32; |
644 | #if ENABLE_FEATURE_MINIX2 |
645 | } else if (MAGIC == MINIX2_SUPER_MAGIC) { |
646 | version2 = 1; |
647 | } else if (MAGIC == MINIX2_SUPER_MAGIC2) { |
648 | namelen = 30; |
649 | dirsize = 32; |
650 | version2 = 1; |
651 | #endif |
652 | } else |
653 | die("bad magic number in superblock"); |
654 | if (ZONESIZE != 0 || BLOCK_SIZE != 1024) |
655 | die("only 1k blocks/zones supported"); |
656 | if (IMAPS * BLOCK_SIZE * 8 < INODES + 1) |
657 | die("bad s_imap_blocks field in superblock"); |
658 | if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1) |
659 | die("bad s_zmap_blocks field in superblock"); |
660 | } |
661 | |
662 | static void read_tables(void) |
663 | { |
664 | inode_map = xzalloc(IMAPS * BLOCK_SIZE); |
665 | zone_map = xzalloc(ZMAPS * BLOCK_SIZE); |
666 | inode_buffer = xmalloc(INODE_BUFFER_SIZE); |
667 | inode_count = xmalloc(INODES + 1); |
668 | zone_count = xmalloc(ZONES); |
669 | if (IMAPS * BLOCK_SIZE != read(dev_fd, inode_map, IMAPS * BLOCK_SIZE)) |
670 | die("can't read inode map"); |
671 | if (ZMAPS * BLOCK_SIZE != read(dev_fd, zone_map, ZMAPS * BLOCK_SIZE)) |
672 | die("can't read zone map"); |
673 | if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE)) |
674 | die("can't read inodes"); |
675 | if (NORM_FIRSTZONE != FIRSTZONE) { |
676 | puts("warning: firstzone!=norm_firstzone"); |
677 | errors_uncorrected = 1; |
678 | } |
679 | get_dirsize(); |
680 | if (OPT_show) { |
681 | printf("%u inodes\n" |
682 | "%u blocks\n" |
683 | "Firstdatazone=%u (%u)\n" |
684 | "Zonesize=%u\n" |
685 | "Maxsize=%u\n" |
686 | "Filesystem state=%u\n" |
687 | "namelen=%u\n\n", |
688 | INODES, |
689 | ZONES, |
690 | FIRSTZONE, NORM_FIRSTZONE, |
691 | BLOCK_SIZE << ZONESIZE, |
692 | MAXSIZE, |
693 | Super.s_state, |
694 | namelen); |
695 | } |
696 | } |
697 | |
698 | static void get_inode_common(unsigned nr, uint16_t i_mode) |
699 | { |
700 | total++; |
701 | if (!inode_count[nr]) { |
702 | if (!inode_in_use(nr)) { |
703 | printf("Inode %u is marked as 'unused', but it is used " |
704 | "for file '%s'\n", nr, current_name); |
705 | if (OPT_repair) { |
706 | if (ask("Mark as 'in use'", 1)) |
707 | mark_inode(nr); |
708 | else |
709 | errors_uncorrected = 1; |
710 | } |
711 | } |
712 | if (S_ISDIR(i_mode)) |
713 | directory++; |
714 | else if (S_ISREG(i_mode)) |
715 | regular++; |
716 | else if (S_ISCHR(i_mode)) |
717 | chardev++; |
718 | else if (S_ISBLK(i_mode)) |
719 | blockdev++; |
720 | else if (S_ISLNK(i_mode)) |
721 | symlinks++; |
722 | else if (S_ISSOCK(i_mode)); |
723 | else if (S_ISFIFO(i_mode)); |
724 | else { |
725 | printf("%s has mode %05o\n", current_name, i_mode); |
726 | } |
727 | } else |
728 | links++; |
729 | if (!++inode_count[nr]) { |
730 | puts("Warning: inode count too big"); |
731 | inode_count[nr]--; |
732 | errors_uncorrected = 1; |
733 | } |
734 | } |
735 | |
736 | static struct minix1_inode *get_inode(unsigned nr) |
737 | { |
738 | struct minix1_inode *inode; |
739 | |
740 | if (!nr || nr > INODES) |
741 | return NULL; |
742 | inode = Inode1 + nr; |
743 | get_inode_common(nr, inode->i_mode); |
744 | return inode; |
745 | } |
746 | |
747 | #if ENABLE_FEATURE_MINIX2 |
748 | static struct minix2_inode *get_inode2(unsigned nr) |
749 | { |
750 | struct minix2_inode *inode; |
751 | |
752 | if (!nr || nr > INODES) |
753 | return NULL; |
754 | inode = Inode2 + nr; |
755 | get_inode_common(nr, inode->i_mode); |
756 | return inode; |
757 | } |
758 | #endif |
759 | |
760 | static void check_root(void) |
761 | { |
762 | struct minix1_inode *inode = Inode1 + MINIX_ROOT_INO; |
763 | |
764 | if (!inode || !S_ISDIR(inode->i_mode)) |
765 | die("root inode isn't a directory"); |
766 | } |
767 | |
768 | #if ENABLE_FEATURE_MINIX2 |
769 | static void check_root2(void) |
770 | { |
771 | struct minix2_inode *inode = Inode2 + MINIX_ROOT_INO; |
772 | |
773 | if (!inode || !S_ISDIR(inode->i_mode)) |
774 | die("root inode isn't a directory"); |
775 | } |
776 | #else |
777 | void check_root2(void); |
778 | #endif |
779 | |
780 | static int add_zone_common(int block, smallint *corrected) |
781 | { |
782 | if (!block) |
783 | return 0; |
784 | if (zone_count[block]) { |
785 | printf("Already used block is reused in file '%s'. ", |
786 | current_name); |
787 | if (ask("Clear", 1)) { |
788 | block = 0; |
789 | *corrected = 1; |
790 | return -1; /* "please zero out *znr" */ |
791 | } |
792 | } |
793 | if (!zone_in_use(block)) { |
794 | printf("Block %d in file '%s' is marked as 'unused'. ", |
795 | block, current_name); |
796 | if (ask("Correct", 1)) |
797 | mark_zone(block); |
798 | } |
799 | if (!++zone_count[block]) |
800 | zone_count[block]--; |
801 | return block; |
802 | } |
803 | |
804 | static int add_zone(uint16_t *znr, smallint *corrected) |
805 | { |
806 | int block; |
807 | |
808 | block = check_zone_nr(znr, corrected); |
809 | block = add_zone_common(block, corrected); |
810 | if (block == -1) { |
811 | *znr = 0; |
812 | block = 0; |
813 | } |
814 | return block; |
815 | } |
816 | |
817 | #if ENABLE_FEATURE_MINIX2 |
818 | static int add_zone2(uint32_t *znr, smallint *corrected) |
819 | { |
820 | int block; |
821 | |
822 | block = check_zone_nr2(znr, corrected); |
823 | block = add_zone_common(block, corrected); |
824 | if (block == -1) { |
825 | *znr = 0; |
826 | block = 0; |
827 | } |
828 | return block; |
829 | } |
830 | #endif |
831 | |
832 | static void add_zone_ind(uint16_t *znr, smallint *corrected) |
833 | { |
834 | int i; |
835 | int block; |
836 | smallint chg_blk = 0; |
837 | |
838 | block = add_zone(znr, corrected); |
839 | if (!block) |
840 | return; |
841 | read_block(block, add_zone_ind_blk); |
842 | for (i = 0; i < (BLOCK_SIZE >> 1); i++) |
843 | add_zone(i + (uint16_t *) add_zone_ind_blk, &chg_blk); |
844 | if (chg_blk) |
845 | write_block(block, add_zone_ind_blk); |
846 | } |
847 | |
848 | #if ENABLE_FEATURE_MINIX2 |
849 | static void add_zone_ind2(uint32_t *znr, smallint *corrected) |
850 | { |
851 | int i; |
852 | int block; |
853 | smallint chg_blk = 0; |
854 | |
855 | block = add_zone2(znr, corrected); |
856 | if (!block) |
857 | return; |
858 | read_block(block, add_zone_ind_blk); |
859 | for (i = 0; i < BLOCK_SIZE >> 2; i++) |
860 | add_zone2(i + (uint32_t *) add_zone_ind_blk, &chg_blk); |
861 | if (chg_blk) |
862 | write_block(block, add_zone_ind_blk); |
863 | } |
864 | #endif |
865 | |
866 | static void add_zone_dind(uint16_t *znr, smallint *corrected) |
867 | { |
868 | int i; |
869 | int block; |
870 | smallint chg_blk = 0; |
871 | |
872 | block = add_zone(znr, corrected); |
873 | if (!block) |
874 | return; |
875 | read_block(block, add_zone_dind_blk); |
876 | for (i = 0; i < (BLOCK_SIZE >> 1); i++) |
877 | add_zone_ind(i + (uint16_t *) add_zone_dind_blk, &chg_blk); |
878 | if (chg_blk) |
879 | write_block(block, add_zone_dind_blk); |
880 | } |
881 | |
882 | #if ENABLE_FEATURE_MINIX2 |
883 | static void add_zone_dind2(uint32_t *znr, smallint *corrected) |
884 | { |
885 | int i; |
886 | int block; |
887 | smallint chg_blk = 0; |
888 | |
889 | block = add_zone2(znr, corrected); |
890 | if (!block) |
891 | return; |
892 | read_block(block, add_zone_dind_blk); |
893 | for (i = 0; i < BLOCK_SIZE >> 2; i++) |
894 | add_zone_ind2(i + (uint32_t *) add_zone_dind_blk, &chg_blk); |
895 | if (chg_blk) |
896 | write_block(block, add_zone_dind_blk); |
897 | } |
898 | |
899 | static void add_zone_tind2(uint32_t *znr, smallint *corrected) |
900 | { |
901 | int i; |
902 | int block; |
903 | smallint chg_blk = 0; |
904 | |
905 | block = add_zone2(znr, corrected); |
906 | if (!block) |
907 | return; |
908 | read_block(block, add_zone_tind_blk); |
909 | for (i = 0; i < BLOCK_SIZE >> 2; i++) |
910 | add_zone_dind2(i + (uint32_t *) add_zone_tind_blk, &chg_blk); |
911 | if (chg_blk) |
912 | write_block(block, add_zone_tind_blk); |
913 | } |
914 | #endif |
915 | |
916 | static void check_zones(unsigned i) |
917 | { |
918 | struct minix1_inode *inode; |
919 | |
920 | if (!i || i > INODES) |
921 | return; |
922 | if (inode_count[i] > 1) /* have we counted this file already? */ |
923 | return; |
924 | inode = Inode1 + i; |
925 | if (!S_ISDIR(inode->i_mode) |
926 | && !S_ISREG(inode->i_mode) |
927 | && !S_ISLNK(inode->i_mode) |
928 | ) { |
929 | return; |
930 | } |
931 | for (i = 0; i < 7; i++) |
932 | add_zone(i + inode->i_zone, &changed); |
933 | add_zone_ind(7 + inode->i_zone, &changed); |
934 | add_zone_dind(8 + inode->i_zone, &changed); |
935 | } |
936 | |
937 | #if ENABLE_FEATURE_MINIX2 |
938 | static void check_zones2(unsigned i) |
939 | { |
940 | struct minix2_inode *inode; |
941 | |
942 | if (!i || i > INODES) |
943 | return; |
944 | if (inode_count[i] > 1) /* have we counted this file already? */ |
945 | return; |
946 | inode = Inode2 + i; |
947 | if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) |
948 | && !S_ISLNK(inode->i_mode)) |
949 | return; |
950 | for (i = 0; i < 7; i++) |
951 | add_zone2(i + inode->i_zone, &changed); |
952 | add_zone_ind2(7 + inode->i_zone, &changed); |
953 | add_zone_dind2(8 + inode->i_zone, &changed); |
954 | add_zone_tind2(9 + inode->i_zone, &changed); |
955 | } |
956 | #endif |
957 | |
958 | static void check_file(struct minix1_inode *dir, unsigned offset) |
959 | { |
960 | struct minix1_inode *inode; |
961 | int ino; |
962 | char *name; |
963 | int block; |
964 | |
965 | block = map_block(dir, offset / BLOCK_SIZE); |
966 | read_block(block, check_file_blk); |
967 | name = check_file_blk + (offset % BLOCK_SIZE) + 2; |
968 | ino = *(uint16_t *) (name - 2); |
969 | if (ino > INODES) { |
970 | printf("%s contains a bad inode number for file '%.*s'. ", |
971 | current_name, namelen, name); |
972 | if (ask("Remove", 1)) { |
973 | *(uint16_t *) (name - 2) = 0; |
974 | write_block(block, check_file_blk); |
975 | } |
976 | ino = 0; |
977 | } |
978 | push_filename(name); |
979 | inode = get_inode(ino); |
980 | pop_filename(); |
981 | if (!offset) { |
982 | if (inode && LONE_CHAR(name, '.')) |
983 | return; |
984 | printf("%s: bad directory: '.' isn't first\n", current_name); |
985 | errors_uncorrected = 1; |
986 | } |
987 | if (offset == dirsize) { |
988 | if (inode && strcmp("..", name) == 0) |
989 | return; |
990 | printf("%s: bad directory: '..' isn't second\n", current_name); |
991 | errors_uncorrected = 1; |
992 | } |
993 | if (!inode) |
994 | return; |
995 | push_filename(name); |
996 | if (OPT_list) { |
997 | if (OPT_verbose) |
998 | printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); |
999 | printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : ""); |
1000 | } |
1001 | check_zones(ino); |
1002 | if (inode && S_ISDIR(inode->i_mode)) |
1003 | recursive_check(ino); |
1004 | pop_filename(); |
1005 | } |
1006 | |
1007 | #if ENABLE_FEATURE_MINIX2 |
1008 | static void check_file2(struct minix2_inode *dir, unsigned offset) |
1009 | { |
1010 | struct minix2_inode *inode; |
1011 | int ino; |
1012 | char *name; |
1013 | int block; |
1014 | |
1015 | block = map_block2(dir, offset / BLOCK_SIZE); |
1016 | read_block(block, check_file_blk); |
1017 | name = check_file_blk + (offset % BLOCK_SIZE) + 2; |
1018 | ino = *(uint16_t *) (name - 2); |
1019 | if (ino > INODES) { |
1020 | printf("%s contains a bad inode number for file '%.*s'. ", |
1021 | current_name, namelen, name); |
1022 | if (ask("Remove", 1)) { |
1023 | *(uint16_t *) (name - 2) = 0; |
1024 | write_block(block, check_file_blk); |
1025 | } |
1026 | ino = 0; |
1027 | } |
1028 | push_filename(name); |
1029 | inode = get_inode2(ino); |
1030 | pop_filename(); |
1031 | if (!offset) { |
1032 | if (inode && LONE_CHAR(name, '.')) |
1033 | return; |
1034 | printf("%s: bad directory: '.' isn't first\n", current_name); |
1035 | errors_uncorrected = 1; |
1036 | } |
1037 | if (offset == dirsize) { |
1038 | if (inode && strcmp("..", name) == 0) |
1039 | return; |
1040 | printf("%s: bad directory: '..' isn't second\n", current_name); |
1041 | errors_uncorrected = 1; |
1042 | } |
1043 | if (!inode) |
1044 | return; |
1045 | push_filename(name); |
1046 | if (OPT_list) { |
1047 | if (OPT_verbose) |
1048 | printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); |
1049 | printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : ""); |
1050 | } |
1051 | check_zones2(ino); |
1052 | if (inode && S_ISDIR(inode->i_mode)) |
1053 | recursive_check2(ino); |
1054 | pop_filename(); |
1055 | } |
1056 | #endif |
1057 | |
1058 | static void recursive_check(unsigned ino) |
1059 | { |
1060 | struct minix1_inode *dir; |
1061 | unsigned offset; |
1062 | |
1063 | dir = Inode1 + ino; |
1064 | if (!S_ISDIR(dir->i_mode)) |
1065 | die("internal error"); |
1066 | if (dir->i_size < 2 * dirsize) { |
1067 | printf("%s: bad directory: size<32", current_name); |
1068 | errors_uncorrected = 1; |
1069 | } |
1070 | for (offset = 0; offset < dir->i_size; offset += dirsize) |
1071 | check_file(dir, offset); |
1072 | } |
1073 | |
1074 | #if ENABLE_FEATURE_MINIX2 |
1075 | static void recursive_check2(unsigned ino) |
1076 | { |
1077 | struct minix2_inode *dir; |
1078 | unsigned offset; |
1079 | |
1080 | dir = Inode2 + ino; |
1081 | if (!S_ISDIR(dir->i_mode)) |
1082 | die("internal error"); |
1083 | if (dir->i_size < 2 * dirsize) { |
1084 | printf("%s: bad directory: size<32", current_name); |
1085 | errors_uncorrected = 1; |
1086 | } |
1087 | for (offset = 0; offset < dir->i_size; offset += dirsize) |
1088 | check_file2(dir, offset); |
1089 | } |
1090 | #endif |
1091 | |
1092 | static int bad_zone(int i) |
1093 | { |
1094 | char buffer[BLOCK_SIZE]; |
1095 | |
1096 | xlseek(dev_fd, BLOCK_SIZE * i, SEEK_SET); |
1097 | return (BLOCK_SIZE != full_read(dev_fd, buffer, BLOCK_SIZE)); |
1098 | } |
1099 | |
1100 | static void check_counts(void) |
1101 | { |
1102 | int i; |
1103 | |
1104 | for (i = 1; i <= INODES; i++) { |
1105 | if (OPT_warn_mode && Inode1[i].i_mode && !inode_in_use(i)) { |
1106 | printf("Inode %d has non-zero mode. ", i); |
1107 | if (ask("Clear", 1)) { |
1108 | Inode1[i].i_mode = 0; |
1109 | changed = 1; |
1110 | } |
1111 | } |
1112 | if (!inode_count[i]) { |
1113 | if (!inode_in_use(i)) |
1114 | continue; |
1115 | printf("Unused inode %d is marked as 'used' in the bitmap. ", i); |
1116 | if (ask("Clear", 1)) |
1117 | unmark_inode(i); |
1118 | continue; |
1119 | } |
1120 | if (!inode_in_use(i)) { |
1121 | printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i); |
1122 | if (ask("Set", 1)) |
1123 | mark_inode(i); |
1124 | } |
1125 | if (Inode1[i].i_nlinks != inode_count[i]) { |
1126 | printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ", |
1127 | i, Inode1[i].i_mode, Inode1[i].i_nlinks, |
1128 | inode_count[i]); |
1129 | if (ask("Set i_nlinks to count", 1)) { |
1130 | Inode1[i].i_nlinks = inode_count[i]; |
1131 | changed = 1; |
1132 | } |
1133 | } |
1134 | } |
1135 | for (i = FIRSTZONE; i < ZONES; i++) { |
1136 | if ((zone_in_use(i) != 0) == zone_count[i]) |
1137 | continue; |
1138 | if (!zone_count[i]) { |
1139 | if (bad_zone(i)) |
1140 | continue; |
1141 | printf("Zone %d is marked 'in use', but no file uses it. ", i); |
1142 | if (ask("Unmark", 1)) |
1143 | unmark_zone(i); |
1144 | continue; |
1145 | } |
1146 | printf("Zone %d: %sin use, counted=%d\n", |
1147 | i, zone_in_use(i) ? "" : "not ", zone_count[i]); |
1148 | } |
1149 | } |
1150 | |
1151 | #if ENABLE_FEATURE_MINIX2 |
1152 | static void check_counts2(void) |
1153 | { |
1154 | int i; |
1155 | |
1156 | for (i = 1; i <= INODES; i++) { |
1157 | if (OPT_warn_mode && Inode2[i].i_mode && !inode_in_use(i)) { |
1158 | printf("Inode %d has non-zero mode. ", i); |
1159 | if (ask("Clear", 1)) { |
1160 | Inode2[i].i_mode = 0; |
1161 | changed = 1; |
1162 | } |
1163 | } |
1164 | if (!inode_count[i]) { |
1165 | if (!inode_in_use(i)) |
1166 | continue; |
1167 | printf("Unused inode %d is marked as 'used' in the bitmap. ", i); |
1168 | if (ask("Clear", 1)) |
1169 | unmark_inode(i); |
1170 | continue; |
1171 | } |
1172 | if (!inode_in_use(i)) { |
1173 | printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i); |
1174 | if (ask("Set", 1)) |
1175 | mark_inode(i); |
1176 | } |
1177 | if (Inode2[i].i_nlinks != inode_count[i]) { |
1178 | printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ", |
1179 | i, Inode2[i].i_mode, Inode2[i].i_nlinks, |
1180 | inode_count[i]); |
1181 | if (ask("Set i_nlinks to count", 1)) { |
1182 | Inode2[i].i_nlinks = inode_count[i]; |
1183 | changed = 1; |
1184 | } |
1185 | } |
1186 | } |
1187 | for (i = FIRSTZONE; i < ZONES; i++) { |
1188 | if ((zone_in_use(i) != 0) == zone_count[i]) |
1189 | continue; |
1190 | if (!zone_count[i]) { |
1191 | if (bad_zone(i)) |
1192 | continue; |
1193 | printf("Zone %d is marked 'in use', but no file uses it. ", i); |
1194 | if (ask("Unmark", 1)) |
1195 | unmark_zone(i); |
1196 | continue; |
1197 | } |
1198 | printf("Zone %d: %sin use, counted=%d\n", |
1199 | i, zone_in_use(i) ? "" : "not ", zone_count[i]); |
1200 | } |
1201 | } |
1202 | #endif |
1203 | |
1204 | static void check(void) |
1205 | { |
1206 | memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); |
1207 | memset(zone_count, 0, ZONES * sizeof(*zone_count)); |
1208 | check_zones(MINIX_ROOT_INO); |
1209 | recursive_check(MINIX_ROOT_INO); |
1210 | check_counts(); |
1211 | } |
1212 | |
1213 | #if ENABLE_FEATURE_MINIX2 |
1214 | static void check2(void) |
1215 | { |
1216 | memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); |
1217 | memset(zone_count, 0, ZONES * sizeof(*zone_count)); |
1218 | check_zones2(MINIX_ROOT_INO); |
1219 | recursive_check2(MINIX_ROOT_INO); |
1220 | check_counts2(); |
1221 | } |
1222 | #else |
1223 | void check2(void); |
1224 | #endif |
1225 | |
1226 | int fsck_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1227 | int fsck_minix_main(int argc UNUSED_PARAM, char **argv) |
1228 | { |
1229 | struct termios tmp; |
1230 | int retcode = 0; |
1231 | |
1232 | xfunc_error_retval = 8; |
1233 | |
1234 | INIT_G(); |
1235 | |
1236 | opt_complementary = "=1:ar"; /* one argument; -a assumes -r */ |
1237 | getopt32(argv, OPTION_STR); |
1238 | argv += optind; |
1239 | device_name = argv[0]; |
1240 | |
1241 | check_mount(); /* trying to check a mounted filesystem? */ |
1242 | if (OPT_manual) { |
1243 | if (!isatty(0) || !isatty(1)) |
1244 | die("need terminal for interactive repairs"); |
1245 | } |
1246 | xmove_fd(xopen(device_name, OPT_repair ? O_RDWR : O_RDONLY), dev_fd); |
1247 | |
1248 | /*sync(); paranoia? */ |
1249 | read_superblock(); |
1250 | |
1251 | /* |
1252 | * Determine whether or not we should continue with the checking. |
1253 | * This is based on the status of the filesystem valid and error |
1254 | * flags and whether or not the -f switch was specified on the |
1255 | * command line. |
1256 | */ |
1257 | printf("%s: %s\n", applet_name, bb_banner); |
1258 | |
1259 | if (!(Super.s_state & MINIX_ERROR_FS) |
1260 | && (Super.s_state & MINIX_VALID_FS) && !OPT_force |
1261 | ) { |
1262 | if (OPT_repair) |
1263 | printf("%s is clean, check is skipped\n", device_name); |
1264 | return 0; |
1265 | } else if (OPT_force) |
1266 | printf("Forcing filesystem check on %s\n", device_name); |
1267 | else if (OPT_repair) |
1268 | printf("Filesystem on %s is dirty, needs checking\n", |
1269 | device_name); |
1270 | |
1271 | read_tables(); |
1272 | |
1273 | if (OPT_manual) { |
1274 | tcgetattr(0, &sv_termios); |
1275 | tmp = sv_termios; |
1276 | tmp.c_lflag &= ~(ICANON | ECHO); |
1277 | tcsetattr_stdin_TCSANOW(&tmp); |
1278 | termios_set = 1; |
1279 | } |
1280 | |
1281 | if (version2) { |
1282 | check_root2(); |
1283 | check2(); |
1284 | } else { |
1285 | check_root(); |
1286 | check(); |
1287 | } |
1288 | |
1289 | if (OPT_verbose) { |
1290 | int i, free_cnt; |
1291 | |
1292 | for (i = 1, free_cnt = 0; i <= INODES; i++) |
1293 | if (!inode_in_use(i)) |
1294 | free_cnt++; |
1295 | printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt), |
1296 | 100 * (INODES - free_cnt) / INODES); |
1297 | for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++) |
1298 | if (!zone_in_use(i)) |
1299 | free_cnt++; |
1300 | printf("%6u zones used (%u%%)\n\n" |
1301 | "%6u regular files\n" |
1302 | "%6u directories\n" |
1303 | "%6u character device files\n" |
1304 | "%6u block device files\n" |
1305 | "%6u links\n" |
1306 | "%6u symbolic links\n" |
1307 | "------\n" |
1308 | "%6u files\n", |
1309 | (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES, |
1310 | regular, directory, chardev, blockdev, |
1311 | links - 2 * directory + 1, symlinks, |
1312 | total - 2 * directory + 1); |
1313 | } |
1314 | if (changed) { |
1315 | write_tables(); |
1316 | puts("FILE SYSTEM HAS BEEN CHANGED"); |
1317 | sync(); |
1318 | } else if (OPT_repair) |
1319 | write_superblock(); |
1320 | |
1321 | if (OPT_manual) |
1322 | tcsetattr_stdin_TCSANOW(&sv_termios); |
1323 | |
1324 | if (changed) |
1325 | retcode += 3; |
1326 | if (errors_uncorrected) |
1327 | retcode += 4; |
1328 | return retcode; |
1329 | } |
1330 |