]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/fsck.minix.c
Merge branch 'close_stream' of git://github.com/kerolasa/lelux-utiliteetit
[thirdparty/util-linux.git] / disk-utils / fsck.minix.c
1 /*
2 * fsck.minix.c - a file system consistency checker for Linux.
3 *
4 * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
5 * as per the GNU copyleft.
6 */
7
8 /*
9 * 09.11.91 - made the first rudimetary functions
10 *
11 * 10.11.91 - updated, does checking, no repairs yet.
12 * Sent out to the mailing-list for testing.
13 *
14 * 14.11.91 - Testing seems to have gone well. Added some
15 * correction-code, and changed some functions.
16 *
17 * 15.11.91 - More correction code. Hopefully it notices most
18 * cases now, and tries to do something about them.
19 *
20 * 16.11.91 - More corrections (thanks to Mika Jalava). Most
21 * things seem to work now. Yeah, sure.
22 *
23 *
24 * 19.04.92 - Had to start over again from this old version, as a
25 * kernel bug ate my enhanced fsck in february.
26 *
27 * 28.02.93 - added support for different directory entry sizes..
28 *
29 * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
30 * super-block information
31 *
32 * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
33 * to that required by fsutil
34 *
35 * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
36 * Added support for file system valid flag. Also
37 * added program_version variable and output of
38 * program name and version number when program
39 * is executed.
40 *
41 * 30.10.94 - added support for v2 filesystem
42 * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
43 *
44 * 10.12.94 - added test to prevent checking of mounted fs adapted
45 * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
46 * program. (Daniel Quinlan, quinlan@yggdrasil.com)
47 *
48 * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such
49 * for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
50 *
51 * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
52 * (Russell King). He made them for ARM. It would seem
53 * that the ARM is powerful enough to do this in C whereas
54 * i386 and m64k must use assembly to get it fast >:-)
55 * This should make minix fsck systemindependent.
56 * (janl@math.uio.no, Nicolai Langfeldt)
57 *
58 * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler
59 * warnings. Added mc68k bitops from
60 * Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
61 *
62 * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by
63 * Andreas Schwab.
64 *
65 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
66 * - added Native Language Support
67 *
68 * 2008-04-06 James Youngman <jay@gnu.org>
69 * - Issue better error message if we fail to open the device.
70 * - Restore terminal state if we get a fatal signal.
71 *
72 *
73 * I've had no time to add comments - hopefully the function names
74 * are comments enough. As with all file system checkers, this assumes
75 * the file system is quiescent - don't use it on a mounted device
76 * unless you can be sure nobody is writing to it (and remember that the
77 * kernel can write to it when it searches for files).
78 *
79 * Usuage: fsck [-larvsm] device
80 * -l for a listing of all the filenames
81 * -a for automatic repairs (not implemented)
82 * -r for repairs (interactive) (not implemented)
83 * -v for verbose (tells how many files)
84 * -s for super-block info
85 * -m for minix-like "mode not cleared" warnings
86 * -f force filesystem check even if filesystem marked as valid
87 *
88 * The device may be a block device or a image of one, but this isn't
89 * enforced (but it's not much fun on a character device :-).
90 */
91
92 #include <stdio.h>
93 #include <stdarg.h>
94 #include <errno.h>
95 #include <unistd.h>
96 #include <string.h>
97 #include <fcntl.h>
98 #include <ctype.h>
99 #include <stdlib.h>
100 #include <termios.h>
101 #include <mntent.h>
102 #include <sys/stat.h>
103 #include <signal.h>
104
105 #include "c.h"
106 #include "exitcodes.h"
107 #include "minix_programs.h"
108 #include "nls.h"
109 #include "pathnames.h"
110 #include "bitops.h"
111 #include "ismounted.h"
112 #include "writeall.h"
113 #include "closestream.h"
114
115 #define ROOT_INO 1
116 #define YESNO_LENGTH 64
117
118 /* Global variables used in minix_programs.h inline fuctions */
119 int fs_version = 1;
120 char *super_block_buffer;
121
122 static char *inode_buffer;
123
124 #define Inode (((struct minix_inode *) inode_buffer) - 1)
125 #define Inode2 (((struct minix2_inode *) inode_buffer) - 1)
126
127 static char *device_name;
128 static int IN;
129 static int repair, automatic, verbose, list, show, warn_mode, force;
130 static int directory, regular, blockdev, chardev, links, symlinks, total;
131
132 static int changed; /* flags if the filesystem has been changed */
133 static int errors_uncorrected; /* flag if some error was not corrected */
134 static size_t dirsize = 16;
135 static size_t namelen = 14;
136 static struct termios termios;
137 static volatile sig_atomic_t termios_set;
138
139 /* File-name data */
140 #define MAX_DEPTH 50
141 static int name_depth;
142 static char name_list[MAX_DEPTH][MINIX_NAME_MAX + 1];
143
144 /* Copy of the previous, just for error reporting - see get_current_name. This
145 * is a waste of 12kB or so. */
146 static char current_name[MAX_DEPTH * (MINIX_NAME_MAX + 1) + 1];
147
148 #define MAGIC (Super.s_magic)
149
150 static unsigned char *inode_count = NULL;
151 static unsigned char *zone_count = NULL;
152
153 static void recursive_check(unsigned int ino);
154 static void recursive_check2(unsigned int ino);
155
156 static char *inode_map;
157 static char *zone_map;
158
159 #define inode_in_use(x) (isset(inode_map,(x)) != 0)
160 #define zone_in_use(x) (isset(zone_map,(x)-get_first_zone()+1) != 0)
161
162 #define mark_inode(x) (setbit(inode_map,(x)),changed=1)
163 #define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
164
165 #define mark_zone(x) (setbit(zone_map,(x)-get_first_zone()+1),changed=1)
166 #define unmark_zone(x) (clrbit(zone_map,(x)-get_first_zone()+1),changed=1)
167
168 static void
169 reset(void) {
170 if (termios_set)
171 tcsetattr(0, TCSANOW, &termios);
172 }
173
174 static void
175 fatalsig(int sig) {
176 /* We received a fatal signal. Reset the terminal. Also reset the
177 * signal handler and re-send the signal, so that the parent process
178 * knows which signal actually caused our death. */
179 signal(sig, SIG_DFL);
180 reset();
181 raise(sig);
182 }
183
184 static void
185 leave(int status) {
186 reset();
187 exit(status);
188 }
189
190 static void
191 usage(void) {
192 fputs(USAGE_HEADER, stderr);
193 fprintf(stderr,
194 _(" %s [options] <device>\n"), program_invocation_short_name);
195 fputs(USAGE_OPTIONS, stderr);
196 fputs(_(" -l list all filenames\n"), stderr);
197 fputs(_(" -a automatic repair\n"), stderr);
198 fputs(_(" -r interactive repair\n"), stderr);
199 fputs(_(" -v be verbose\n"), stderr);
200 fputs(_(" -s output super-block information\n"), stderr);
201 fputs(_(" -m activate mode not cleared warnings\n"), stderr);
202 fputs(_(" -f force check\n"), stderr);
203 fputs(USAGE_SEPARATOR, stderr);
204 fputs(USAGE_VERSION, stderr);
205 fprintf(stderr, USAGE_MAN_TAIL("fsck.minix(8)"));
206 leave(FSCK_EX_USAGE);
207 }
208
209 static void die(const char *fmt, ...)
210 __attribute__ ((__format__(__printf__, 1, 2)));
211
212 static void
213 die(const char *fmt, ...) {
214 va_list ap;
215
216 fprintf(stderr, "%s: ", program_invocation_short_name);
217 va_start(ap, fmt);
218 vfprintf(stderr, fmt, ap);
219 va_end(ap);
220 fputc('\n', stderr);
221 leave(FSCK_EX_ERROR);
222 }
223
224 /* This simply goes through the file-name data and prints out the current file. */
225 static void
226 get_current_name(void) {
227 int i = 0, ct;
228 char *p, *q;
229
230 q = current_name;
231 while (i < name_depth) {
232 p = name_list[i++];
233 ct = namelen;
234 *q++ = '/';
235 while (ct-- && *p)
236 *q++ = *p++;
237 }
238 if (i == 0)
239 *q++ = '/';
240 *q = 0;
241 }
242
243 static int
244 ask(const char *string, int def) {
245 int resp;
246 char input[YESNO_LENGTH];
247
248 if (!repair) {
249 printf("\n");
250 errors_uncorrected = 1;
251 return 0;
252 }
253 if (automatic) {
254 printf("\n");
255 if (!def)
256 errors_uncorrected = 1;
257 return def;
258 }
259 /* TRANSLATORS: these yes no questions uses rpmatch(), and should be
260 * translated. */
261 printf(def ? _("%s (y/n)? ") : _("%s (n/y)? "), string);
262 fflush(stdout);
263 fgets(input, YESNO_LENGTH, stdin);
264 resp = rpmatch(input);
265 switch (resp) {
266 case -1:
267 /* def = def */
268 break;
269 case 0:
270 case 1:
271 def = resp;
272 break;
273 default:
274 /* rpmatch bug? */
275 abort();
276 }
277 if (def)
278 printf(_("y\n"));
279 else {
280 printf(_("n\n"));
281 errors_uncorrected = 1;
282 }
283 return def;
284 }
285
286 /* Make certain that we aren't checking a filesystem that is on a mounted
287 * partition. Code adapted from e2fsck, Copyright (C) 1993, 1994 Theodore
288 * Ts'o. Also licensed under GPL. */
289 static void
290 check_mount(void) {
291 int cont;
292
293 if (!is_mounted(device_name))
294 return;
295
296 printf(_("%s is mounted. "), device_name);
297 if (isatty(0) && isatty(1))
298 cont = ask(_("Do you really want to continue"), 0);
299 else
300 cont = 0;
301 if (!cont) {
302 printf(_("check aborted.\n"));
303 exit(FSCK_EX_OK);
304 }
305 return;
306 }
307
308 /* check_zone_nr checks to see that *nr is a valid zone nr. If it isn't, it
309 * will possibly be repaired. Check_zone_nr sets *corrected if an error was
310 * corrected, and returns the zone (0 for no zone or a bad zone-number). */
311 static int
312 check_zone_nr(unsigned short *nr, int *corrected) {
313 if (!*nr)
314 return 0;
315
316 if (*nr < get_first_zone()) {
317 get_current_name();
318 printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name);
319 } else if (*nr >= get_nzones()) {
320 get_current_name();
321 printf(_("Zone nr >= ZONES in file `%s'."), current_name);
322 } else
323 return *nr;
324
325 if (ask(_("Remove block"), 1)) {
326 *nr = 0;
327 *corrected = 1;
328 }
329 return 0;
330 }
331
332 static int
333 check_zone_nr2(unsigned int *nr, int *corrected) {
334 if (!*nr)
335 return 0;
336
337 if (*nr < get_first_zone()) {
338 get_current_name();
339 printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name);
340 } else if (*nr >= get_nzones()) {
341 get_current_name();
342 printf(_("Zone nr >= ZONES in file `%s'."), current_name);
343 } else
344 return *nr;
345
346 if (ask(_("Remove block"), 1)) {
347 *nr = 0;
348 *corrected = 1;
349 }
350 return 0;
351 }
352
353 /* read-block reads block nr into the buffer at addr. */
354 static void
355 read_block(unsigned int nr, char *addr) {
356 if (!nr) {
357 memset(addr, 0, MINIX_BLOCK_SIZE);
358 return;
359 }
360 if (MINIX_BLOCK_SIZE * nr != lseek(IN, MINIX_BLOCK_SIZE * nr, SEEK_SET)) {
361 get_current_name();
362 printf(_("Read error: unable to seek to block in file '%s'\n"),
363 current_name);
364 memset(addr, 0, MINIX_BLOCK_SIZE);
365 errors_uncorrected = 1;
366 } else if (MINIX_BLOCK_SIZE != read(IN, addr, MINIX_BLOCK_SIZE)) {
367 get_current_name();
368 printf(_("Read error: bad block in file '%s'\n"), current_name);
369 memset(addr, 0, MINIX_BLOCK_SIZE);
370 errors_uncorrected = 1;
371 }
372 }
373
374 /* write_block writes block nr to disk. */
375 static void
376 write_block(unsigned int nr, char *addr) {
377 if (!nr)
378 return;
379 if (nr < get_first_zone() || nr >= get_nzones()) {
380 printf(_("Internal error: trying to write bad block\n"
381 "Write request ignored\n"));
382 errors_uncorrected = 1;
383 return;
384 }
385 if (MINIX_BLOCK_SIZE * nr != lseek(IN, MINIX_BLOCK_SIZE * nr, SEEK_SET))
386 die(_("seek failed in write_block"));
387 if (MINIX_BLOCK_SIZE != write(IN, addr, MINIX_BLOCK_SIZE)) {
388 get_current_name();
389 printf(_("Write error: bad block in file '%s'\n"),
390 current_name);
391 errors_uncorrected = 1;
392 }
393 }
394
395 /* map-block calculates the absolute block nr of a block in a file. It sets
396 * 'changed' if the inode has needed changing, and re-writes any indirect
397 * blocks with errors. */
398 static int
399 map_block(struct minix_inode *inode, unsigned int blknr) {
400 unsigned short ind[MINIX_BLOCK_SIZE >> 1];
401 unsigned short dind[MINIX_BLOCK_SIZE >> 1];
402 int blk_chg, block, result;
403
404 if (blknr < 7)
405 return check_zone_nr(inode->i_zone + blknr, &changed);
406 blknr -= 7;
407 if (blknr < 512) {
408 block = check_zone_nr(inode->i_zone + 7, &changed);
409 read_block(block, (char *)ind);
410 blk_chg = 0;
411 result = check_zone_nr(blknr + ind, &blk_chg);
412 if (blk_chg)
413 write_block(block, (char *)ind);
414 return result;
415 }
416 blknr -= 512;
417 block = check_zone_nr(inode->i_zone + 8, &changed);
418 read_block(block, (char *)dind);
419 blk_chg = 0;
420 result = check_zone_nr(dind + (blknr / 512), &blk_chg);
421 if (blk_chg)
422 write_block(block, (char *)dind);
423 block = result;
424 read_block(block, (char *)ind);
425 blk_chg = 0;
426 result = check_zone_nr(ind + (blknr % 512), &blk_chg);
427 if (blk_chg)
428 write_block(block, (char *)ind);
429 return result;
430 }
431
432 static int
433 map_block2(struct minix2_inode *inode, unsigned int blknr) {
434 unsigned int ind[MINIX_BLOCK_SIZE >> 2];
435 unsigned int dind[MINIX_BLOCK_SIZE >> 2];
436 unsigned int tind[MINIX_BLOCK_SIZE >> 2];
437 int blk_chg, block, result;
438
439 if (blknr < 7)
440 return check_zone_nr2(inode->i_zone + blknr, &changed);
441 blknr -= 7;
442 if (blknr < 256) {
443 block = check_zone_nr2(inode->i_zone + 7, &changed);
444 read_block(block, (char *)ind);
445 blk_chg = 0;
446 result = check_zone_nr2(blknr + ind, &blk_chg);
447 if (blk_chg)
448 write_block(block, (char *)ind);
449 return result;
450 }
451 blknr -= 256;
452 if (blknr < 256 * 256) {
453 block = check_zone_nr2(inode->i_zone + 8, &changed);
454 read_block(block, (char *)dind);
455 blk_chg = 0;
456 result = check_zone_nr2(dind + blknr / 256, &blk_chg);
457 if (blk_chg)
458 write_block(block, (char *)dind);
459 block = result;
460 read_block(block, (char *)ind);
461 blk_chg = 0;
462 result = check_zone_nr2(ind + blknr % 256, &blk_chg);
463 if (blk_chg)
464 write_block(block, (char *)ind);
465 return result;
466 }
467 blknr -= 256 * 256;
468 block = check_zone_nr2(inode->i_zone + 9, &changed);
469 read_block(block, (char *)tind);
470 blk_chg = 0;
471 result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg);
472 if (blk_chg)
473 write_block(block, (char *)tind);
474 block = result;
475 read_block(block, (char *)dind);
476 blk_chg = 0;
477 result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg);
478 if (blk_chg)
479 write_block(block, (char *)dind);
480 block = result;
481 read_block(block, (char *)ind);
482 blk_chg = 0;
483 result = check_zone_nr2(ind + blknr % 256, &blk_chg);
484 if (blk_chg)
485 write_block(block, (char *)ind);
486 return result;
487 }
488
489 static void
490 write_super_block(void) {
491 /* Set the state of the filesystem based on whether or not there are
492 * uncorrected errors. The filesystem valid flag is unconditionally
493 * set if we get this far. */
494 Super.s_state |= MINIX_VALID_FS;
495 if (errors_uncorrected)
496 Super.s_state |= MINIX_ERROR_FS;
497 else
498 Super.s_state &= ~MINIX_ERROR_FS;
499
500 if (MINIX_BLOCK_SIZE != lseek(IN, MINIX_BLOCK_SIZE, SEEK_SET))
501 die(_("seek failed in write_super_block"));
502 if (MINIX_BLOCK_SIZE != write(IN, super_block_buffer, MINIX_BLOCK_SIZE))
503 die(_("unable to write super-block"));
504 return;
505 }
506
507 static void
508 write_tables(void) {
509 write_super_block();
510 unsigned long buffsz = get_inode_buffer_size();
511 unsigned long imaps = get_nimaps();
512 unsigned long zmaps = get_nzmaps();
513
514 if (write_all(IN, inode_map, imaps * MINIX_BLOCK_SIZE))
515 die(_("Unable to write inode map"));
516
517 if (write_all(IN, zone_map, zmaps * MINIX_BLOCK_SIZE))
518 die(_("Unable to write zone map"));
519
520 if (write_all(IN, inode_buffer, buffsz))
521 die(_("Unable to write inodes"));
522 }
523
524 static void
525 get_dirsize(void) {
526 int block;
527 char blk[MINIX_BLOCK_SIZE];
528 size_t size;
529
530 if (fs_version == 2)
531 block = Inode2[ROOT_INO].i_zone[0];
532 else
533 block = Inode[ROOT_INO].i_zone[0];
534 read_block(block, blk);
535
536 for (size = 16; size < MINIX_BLOCK_SIZE; size <<= 1) {
537 if (strcmp(blk + size + 2, "..") == 0) {
538 dirsize = size;
539 namelen = size - 2;
540 return;
541 }
542 }
543 /* use defaults */
544 }
545
546 static void
547 read_superblock(void) {
548 if (MINIX_BLOCK_SIZE != lseek(IN, MINIX_BLOCK_SIZE, SEEK_SET))
549 die(_("seek failed"));
550
551 super_block_buffer = calloc(1, MINIX_BLOCK_SIZE);
552 if (!super_block_buffer)
553 die(_("unable to alloc buffer for superblock"));
554
555 if (MINIX_BLOCK_SIZE != read(IN, super_block_buffer, MINIX_BLOCK_SIZE))
556 die(_("unable to read super block"));
557 if (MAGIC == MINIX_SUPER_MAGIC) {
558 namelen = 14;
559 dirsize = 16;
560 fs_version = 1;
561 } else if (MAGIC == MINIX_SUPER_MAGIC2) {
562 namelen = 30;
563 dirsize = 32;
564 fs_version = 1;
565 } else if (MAGIC == MINIX2_SUPER_MAGIC) {
566 namelen = 14;
567 dirsize = 16;
568 fs_version = 2;
569 } else if (MAGIC == MINIX2_SUPER_MAGIC2) {
570 namelen = 30;
571 dirsize = 32;
572 fs_version = 2;
573 } else
574 die(_("bad magic number in super-block"));
575 if (get_zone_size() != 0 || MINIX_BLOCK_SIZE != 1024)
576 die(_("Only 1k blocks/zones supported"));
577 if (get_nimaps() * MINIX_BLOCK_SIZE * 8 < get_ninodes() + 1)
578 die(_("bad s_imap_blocks field in super-block"));
579 if (get_nzmaps() * MINIX_BLOCK_SIZE * 8 <
580 get_nzones() - get_first_zone() + 1)
581 die(_("bad s_zmap_blocks field in super-block"));
582 }
583
584 static void
585 read_tables(void) {
586 unsigned long inodes = get_ninodes();
587 unsigned long buffsz = get_inode_buffer_size();
588 unsigned long norm_first_zone = first_zone_data();
589 unsigned long first_zone = get_first_zone();
590 unsigned long zones = get_nzones();
591 unsigned long imaps = get_nimaps();
592 unsigned long zmaps = get_nzmaps();
593 ssize_t rc;
594
595 inode_map = malloc(imaps * MINIX_BLOCK_SIZE);
596 if (!inode_map)
597 die(_("Unable to allocate buffer for inode map"));
598 zone_map = malloc(zmaps * MINIX_BLOCK_SIZE);
599 if (!zone_map)
600 die(_("Unable to allocate buffer for zone map"));
601 inode_buffer = malloc(buffsz);
602 if (!inode_buffer)
603 die(_("Unable to allocate buffer for inodes"));
604 inode_count = calloc(1, inodes + 1);
605 if (!inode_count)
606 die(_("Unable to allocate buffer for inode count"));
607 zone_count = calloc(1, zones);
608 if (!zone_count)
609 die(_("Unable to allocate buffer for zone count"));
610
611 rc = read(IN, inode_map, imaps * MINIX_BLOCK_SIZE);
612 if (rc < 0 || imaps * MINIX_BLOCK_SIZE != (size_t) rc)
613 die(_("Unable to read inode map"));
614
615 rc = read(IN, zone_map, zmaps * MINIX_BLOCK_SIZE);
616 if (rc < 0 || zmaps * MINIX_BLOCK_SIZE != (size_t) rc)
617 die(_("Unable to read zone map"));
618
619 rc = read(IN, inode_buffer, buffsz);
620 if (rc < 0 || buffsz != (size_t) rc)
621 die(_("Unable to read inodes"));
622 if (norm_first_zone != first_zone) {
623 printf(_("Warning: Firstzone != Norm_firstzone\n"));
624 errors_uncorrected = 1;
625 }
626 get_dirsize();
627 if (show) {
628 printf(_("%ld inodes\n"), inodes);
629 printf(_("%ld blocks\n"), zones);
630 printf(_("Firstdatazone=%ld (%ld)\n"), first_zone, norm_first_zone);
631 printf(_("Zonesize=%d\n"), MINIX_BLOCK_SIZE << get_zone_size());
632 printf(_("Maxsize=%ld\n"), get_max_size());
633 printf(_("Filesystem state=%d\n"), Super.s_state);
634 printf(_("namelen=%zd\n\n"), namelen);
635 }
636 }
637
638 static struct minix_inode *
639 get_inode(unsigned int nr) {
640 struct minix_inode *inode;
641
642 if (!nr || nr > get_ninodes())
643 return NULL;
644 total++;
645 inode = Inode + nr;
646 if (!inode_count[nr]) {
647 if (!inode_in_use(nr)) {
648 get_current_name();
649 printf(_("Inode %d marked unused, "
650 "but used for file '%s'\n"), nr, current_name);
651 if (repair) {
652 if (ask(_("Mark in use"), 1))
653 mark_inode(nr);
654 } else {
655 errors_uncorrected = 1;
656 }
657 }
658 if (S_ISDIR(inode->i_mode))
659 directory++;
660 else if (S_ISREG(inode->i_mode))
661 regular++;
662 else if (S_ISCHR(inode->i_mode))
663 chardev++;
664 else if (S_ISBLK(inode->i_mode))
665 blockdev++;
666 else if (S_ISLNK(inode->i_mode))
667 symlinks++;
668 else if (S_ISSOCK(inode->i_mode))
669 ;
670 else if (S_ISFIFO(inode->i_mode))
671 ;
672 else {
673 get_current_name();
674 printf(_("The file `%s' has mode %05o\n"),
675 current_name, inode->i_mode);
676 }
677
678 } else
679 links++;
680 if (!++inode_count[nr]) {
681 printf(_("Warning: inode count too big.\n"));
682 inode_count[nr]--;
683 errors_uncorrected = 1;
684 }
685 return inode;
686 }
687
688 static struct minix2_inode *
689 get_inode2(unsigned int nr) {
690 struct minix2_inode *inode;
691
692 if (!nr || nr > get_ninodes())
693 return NULL;
694 total++;
695 inode = Inode2 + nr;
696 if (!inode_count[nr]) {
697 if (!inode_in_use(nr)) {
698 get_current_name();
699 printf(_("Inode %d marked unused, "
700 "but used for file '%s'\n"), nr, current_name);
701 if (repair) {
702 if (ask(_("Mark in use"), 1))
703 mark_inode(nr);
704 else
705 errors_uncorrected = 1;
706 }
707 }
708 if (S_ISDIR(inode->i_mode))
709 directory++;
710 else if (S_ISREG(inode->i_mode))
711 regular++;
712 else if (S_ISCHR(inode->i_mode))
713 chardev++;
714 else if (S_ISBLK(inode->i_mode))
715 blockdev++;
716 else if (S_ISLNK(inode->i_mode))
717 symlinks++;
718 else if (S_ISSOCK(inode->i_mode)) ;
719 else if (S_ISFIFO(inode->i_mode)) ;
720 else {
721 get_current_name();
722 printf(_("The file `%s' has mode %05o\n"),
723 current_name, inode->i_mode);
724 }
725 } else
726 links++;
727 if (!++inode_count[nr]) {
728 printf(_("Warning: inode count too big.\n"));
729 inode_count[nr]--;
730 errors_uncorrected = 1;
731 }
732 return inode;
733 }
734
735 static void
736 check_root(void) {
737 struct minix_inode *inode = Inode + ROOT_INO;
738
739 if (!inode || !S_ISDIR(inode->i_mode))
740 die(_("root inode isn't a directory"));
741 }
742
743 static void
744 check_root2(void) {
745 struct minix2_inode *inode = Inode2 + ROOT_INO;
746
747 if (!inode || !S_ISDIR(inode->i_mode))
748 die(_("root inode isn't a directory"));
749 }
750
751 static int
752 add_zone(unsigned short *znr, int *corrected) {
753 int block;
754
755 block = check_zone_nr(znr, corrected);
756 if (!block)
757 return 0;
758 if (zone_count[block]) {
759 get_current_name();
760 printf(_("Block has been used before. Now in file `%s'."),
761 current_name);
762 if (ask(_("Clear"), 1)) {
763 *znr = 0;
764 block = 0;
765 *corrected = 1;
766 }
767 }
768 if (!block)
769 return 0;
770 if (!zone_in_use(block)) {
771 get_current_name();
772 printf(_("Block %d in file `%s' is marked not in use."),
773 block, current_name);
774 if (ask(_("Correct"), 1))
775 mark_zone(block);
776 }
777 if (!++zone_count[block])
778 zone_count[block]--;
779 return block;
780 }
781
782 static int
783 add_zone2(unsigned int *znr, int *corrected) {
784 int block;
785
786 block = check_zone_nr2(znr, corrected);
787 if (!block)
788 return 0;
789 if (zone_count[block]) {
790 get_current_name();
791 printf(_("Block has been used before. Now in file `%s'."),
792 current_name);
793 if (ask(_("Clear"), 1)) {
794 *znr = 0;
795 block = 0;
796 *corrected = 1;
797 }
798 }
799 if (!block)
800 return 0;
801 if (!zone_in_use(block)) {
802 get_current_name();
803 printf(_("Block %d in file `%s' is marked not in use."),
804 block, current_name);
805 if (ask(_("Correct"), 1))
806 mark_zone(block);
807 }
808 if (!++zone_count[block])
809 zone_count[block]--;
810 return block;
811 }
812
813 static void
814 add_zone_ind(unsigned short *znr, int *corrected) {
815 static char blk[MINIX_BLOCK_SIZE];
816 int i, chg_blk = 0;
817 int block;
818
819 block = add_zone(znr, corrected);
820 if (!block)
821 return;
822 read_block(block, blk);
823 for (i = 0; i < (MINIX_BLOCK_SIZE >> 1); i++)
824 add_zone(i + (unsigned short *)blk, &chg_blk);
825 if (chg_blk)
826 write_block(block, blk);
827 }
828
829 static void
830 add_zone_ind2(unsigned int *znr, int *corrected) {
831 static char blk[MINIX_BLOCK_SIZE];
832 int i, chg_blk = 0;
833 int block;
834
835 block = add_zone2(znr, corrected);
836 if (!block)
837 return;
838 read_block(block, blk);
839 for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++)
840 add_zone2(i + (unsigned int *)blk, &chg_blk);
841 if (chg_blk)
842 write_block(block, blk);
843 }
844
845 static void
846 add_zone_dind(unsigned short *znr, int *corrected) {
847 static char blk[MINIX_BLOCK_SIZE];
848 int i, blk_chg = 0;
849 int block;
850
851 block = add_zone(znr, corrected);
852 if (!block)
853 return;
854 read_block(block, blk);
855 for (i = 0; i < (MINIX_BLOCK_SIZE >> 1); i++)
856 add_zone_ind(i + (unsigned short *)blk, &blk_chg);
857 if (blk_chg)
858 write_block(block, blk);
859 }
860
861 static void
862 add_zone_dind2(unsigned int *znr, int *corrected) {
863 static char blk[MINIX_BLOCK_SIZE];
864 int i, blk_chg = 0;
865 int block;
866
867 block = add_zone2(znr, corrected);
868 if (!block)
869 return;
870 read_block(block, blk);
871 for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++)
872 add_zone_ind2(i + (unsigned int *)blk, &blk_chg);
873 if (blk_chg)
874 write_block(block, blk);
875 }
876
877 static void
878 add_zone_tind2(unsigned int *znr, int *corrected) {
879 static char blk[MINIX_BLOCK_SIZE];
880 int i, blk_chg = 0;
881 int block;
882
883 block = add_zone2(znr, corrected);
884 if (!block)
885 return;
886 read_block(block, blk);
887 for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++)
888 add_zone_dind2(i + (unsigned int *)blk, &blk_chg);
889 if (blk_chg)
890 write_block(block, blk);
891 }
892
893 static void
894 check_zones(unsigned int i) {
895 struct minix_inode *inode;
896
897 if (!i || i > get_ninodes())
898 return;
899 if (inode_count[i] > 1) /* have we counted this file already? */
900 return;
901 inode = Inode + i;
902 if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
903 !S_ISLNK(inode->i_mode))
904 return;
905 for (i = 0; i < 7; i++)
906 add_zone(i + inode->i_zone, &changed);
907 add_zone_ind(7 + inode->i_zone, &changed);
908 add_zone_dind(8 + inode->i_zone, &changed);
909 }
910
911 static void
912 check_zones2(unsigned int i) {
913 struct minix2_inode *inode;
914
915 if (!i || i > get_ninodes())
916 return;
917 if (inode_count[i] > 1) /* have we counted this file already? */
918 return;
919 inode = Inode2 + i;
920 if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
921 && !S_ISLNK(inode->i_mode))
922 return;
923 for (i = 0; i < 7; i++)
924 add_zone2(i + inode->i_zone, &changed);
925 add_zone_ind2(7 + inode->i_zone, &changed);
926 add_zone_dind2(8 + inode->i_zone, &changed);
927 add_zone_tind2(9 + inode->i_zone, &changed);
928 }
929
930 static void
931 check_file(struct minix_inode *dir, unsigned int offset) {
932 static char blk[MINIX_BLOCK_SIZE];
933 struct minix_inode *inode;
934 unsigned int ino;
935 char *name;
936 int block;
937
938 block = map_block(dir, offset / MINIX_BLOCK_SIZE);
939 read_block(block, blk);
940 name = blk + (offset % MINIX_BLOCK_SIZE) + 2;
941 ino = *(unsigned short *)(name - 2);
942 if (ino > get_ninodes()) {
943 get_current_name();
944 printf(_("The directory '%s' contains a bad inode number "
945 "for file '%.*s'."), current_name, (int)namelen, name);
946 if (ask(_(" Remove"), 1)) {
947 *(unsigned short *)(name - 2) = 0;
948 write_block(block, blk);
949 }
950 ino = 0;
951 }
952 if (name_depth < MAX_DEPTH)
953 strncpy(name_list[name_depth], name, namelen);
954 name_depth++;
955 inode = get_inode(ino);
956 name_depth--;
957 if (!offset) {
958 if (!inode || strcmp(".", name)) {
959 get_current_name();
960 printf(_("%s: bad directory: '.' isn't first\n"),
961 current_name);
962 errors_uncorrected = 1;
963 } else
964 return;
965 }
966 if (offset == dirsize) {
967 if (!inode || strcmp("..", name)) {
968 get_current_name();
969 printf(_("%s: bad directory: '..' isn't second\n"),
970 current_name);
971 errors_uncorrected = 1;
972 } else
973 return;
974 }
975 if (!inode)
976 return;
977 if (name_depth < MAX_DEPTH)
978 strncpy(name_list[name_depth], name, namelen);
979 name_depth++;
980 if (list) {
981 if (verbose)
982 printf("%6d %07o %3d ", ino,
983 inode->i_mode, inode->i_nlinks);
984 get_current_name();
985 printf("%s", current_name);
986 if (S_ISDIR(inode->i_mode))
987 printf(":\n");
988 else
989 printf("\n");
990 }
991 check_zones(ino);
992 if (inode && S_ISDIR(inode->i_mode))
993 recursive_check(ino);
994 name_depth--;
995 return;
996 }
997
998 static void
999 check_file2(struct minix2_inode *dir, unsigned int offset) {
1000 static char blk[MINIX_BLOCK_SIZE];
1001 struct minix2_inode *inode;
1002 unsigned long ino;
1003 char *name;
1004 int block;
1005
1006 block = map_block2(dir, offset / MINIX_BLOCK_SIZE);
1007 read_block(block, blk);
1008 name = blk + (offset % MINIX_BLOCK_SIZE) + 2;
1009 ino = *(unsigned short *)(name - 2);
1010 if (ino > get_ninodes()) {
1011 get_current_name();
1012 printf(_("The directory '%s' contains a bad inode number "
1013 "for file '%.*s'."), current_name, (int)namelen, name);
1014 if (ask(_(" Remove"), 1)) {
1015 *(unsigned short *)(name - 2) = 0;
1016 write_block(block, blk);
1017 }
1018 ino = 0;
1019 }
1020 if (name_depth < MAX_DEPTH)
1021 strncpy(name_list[name_depth], name, namelen);
1022 name_depth++;
1023 inode = get_inode2(ino);
1024 name_depth--;
1025 if (!offset) {
1026 if (!inode || strcmp(".", name)) {
1027 get_current_name();
1028 printf(_("%s: bad directory: '.' isn't first\n"),
1029 current_name);
1030 errors_uncorrected = 1;
1031 } else
1032 return;
1033 }
1034 if (offset == dirsize) {
1035 if (!inode || strcmp("..", name)) {
1036 get_current_name();
1037 printf(_("%s: bad directory: '..' isn't second\n"),
1038 current_name);
1039 errors_uncorrected = 1;
1040 } else
1041 return;
1042 }
1043 if (!inode)
1044 return;
1045 name_depth++;
1046 if (list) {
1047 if (verbose)
1048 printf("%6zd %07o %3d ", ino, inode->i_mode,
1049 inode->i_nlinks);
1050 get_current_name();
1051 printf("%s", current_name);
1052 if (S_ISDIR(inode->i_mode))
1053 printf(":\n");
1054 else
1055 printf("\n");
1056 }
1057 check_zones2(ino);
1058 if (inode && S_ISDIR(inode->i_mode))
1059 recursive_check2(ino);
1060 name_depth--;
1061 return;
1062 }
1063
1064 static void
1065 recursive_check(unsigned int ino) {
1066 struct minix_inode *dir;
1067 unsigned int offset;
1068
1069 dir = Inode + ino;
1070 if (!S_ISDIR(dir->i_mode))
1071 die(_("internal error"));
1072 if (dir->i_size < 2 * dirsize) {
1073 get_current_name();
1074 printf(_("%s: bad directory: size < 32"), current_name);
1075 errors_uncorrected = 1;
1076 }
1077 for (offset = 0; offset < dir->i_size; offset += dirsize)
1078 check_file(dir, offset);
1079 }
1080
1081 static void
1082 recursive_check2(unsigned int ino) {
1083 struct minix2_inode *dir;
1084 unsigned int offset;
1085
1086 dir = Inode2 + ino;
1087 if (!S_ISDIR(dir->i_mode))
1088 die(_("internal error"));
1089 if (dir->i_size < 2 * dirsize) {
1090 get_current_name();
1091 printf(_("%s: bad directory: size < 32"), current_name);
1092 errors_uncorrected = 1;
1093 }
1094 for (offset = 0; offset < dir->i_size; offset += dirsize)
1095 check_file2(dir, offset);
1096 }
1097
1098 static int
1099 bad_zone(int i) {
1100 char buffer[1024];
1101
1102 if (MINIX_BLOCK_SIZE * i != lseek(IN, MINIX_BLOCK_SIZE * i, SEEK_SET))
1103 die(_("seek failed in bad_zone"));
1104 return (MINIX_BLOCK_SIZE != read(IN, buffer, MINIX_BLOCK_SIZE));
1105 }
1106
1107 static void
1108 check_counts(void) {
1109 unsigned long i;
1110
1111 for (i = 1; i <= get_ninodes(); i++) {
1112 if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
1113 printf(_("Inode %lu mode not cleared."), i);
1114 if (ask(_("Clear"), 1)) {
1115 Inode[i].i_mode = 0;
1116 changed = 1;
1117 }
1118 }
1119 if (!inode_count[i]) {
1120 if (!inode_in_use(i))
1121 continue;
1122 printf(_("Inode %lu not used, marked used in the bitmap."), i);
1123 if (ask(_("Clear"), 1))
1124 unmark_inode(i);
1125 continue;
1126 }
1127 if (!inode_in_use(i)) {
1128 printf(_("Inode %lu used, marked unused in the bitmap."), i);
1129 if (ask(_("Set"), 1))
1130 mark_inode(i);
1131 }
1132 if (Inode[i].i_nlinks != inode_count[i]) {
1133 printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."),
1134 i, Inode[i].i_mode, Inode[i].i_nlinks,
1135 inode_count[i]);
1136 if (ask(_("Set i_nlinks to count"), 1)) {
1137 Inode[i].i_nlinks = inode_count[i];
1138 changed = 1;
1139 }
1140 }
1141 }
1142 for (i = get_first_zone(); i < get_nzones(); i++) {
1143 if (zone_in_use(i) == zone_count[i])
1144 continue;
1145 if (!zone_count[i]) {
1146 if (bad_zone(i))
1147 continue;
1148 printf(_("Zone %lu: marked in use, no file uses it."),
1149 i);
1150 if (ask(_("Unmark"), 1))
1151 unmark_zone(i);
1152 continue;
1153 }
1154 if (zone_in_use(i))
1155 printf(_("Zone %lu: in use, counted=%d\n"),
1156 i, zone_count[i]);
1157 else
1158 printf(_("Zone %lu: not in use, counted=%d\n"),
1159 i, zone_count[i]);
1160 }
1161 }
1162
1163 static void
1164 check_counts2(void) {
1165 unsigned long i;
1166
1167 for (i = 1; i <= get_ninodes(); i++) {
1168 if (!inode_in_use(i) && Inode2[i].i_mode && warn_mode) {
1169 printf(_("Inode %lu mode not cleared."), i);
1170 if (ask(_("Clear"), 1)) {
1171 Inode2[i].i_mode = 0;
1172 changed = 1;
1173 }
1174 }
1175 if (!inode_count[i]) {
1176 if (!inode_in_use(i))
1177 continue;
1178 printf(_("Inode %lu not used, marked used in the bitmap."), i);
1179 if (ask(_("Clear"), 1))
1180 unmark_inode(i);
1181 continue;
1182 }
1183 if (!inode_in_use(i)) {
1184 printf(_("Inode %lu used, marked unused in the bitmap."), i);
1185 if (ask(_("Set"), 1))
1186 mark_inode(i);
1187 }
1188 if (Inode2[i].i_nlinks != inode_count[i]) {
1189 printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."),
1190 i, Inode2[i].i_mode, Inode2[i].i_nlinks,
1191 inode_count[i]);
1192 if (ask(_("Set i_nlinks to count"), 1)) {
1193 Inode2[i].i_nlinks = inode_count[i];
1194 changed = 1;
1195 }
1196 }
1197 }
1198 for (i = get_first_zone(); i < get_nzones(); i++) {
1199 if (zone_in_use(i) == zone_count[i])
1200 continue;
1201 if (!zone_count[i]) {
1202 if (bad_zone(i))
1203 continue;
1204 printf(_("Zone %lu: marked in use, no file uses it."),
1205 i);
1206 if (ask(_("Unmark"), 1))
1207 unmark_zone(i);
1208 continue;
1209 }
1210 if (zone_in_use(i))
1211 printf(_("Zone %lu: in use, counted=%d\n"),
1212 i, zone_count[i]);
1213 else
1214 printf(_("Zone %lu: not in use, counted=%d\n"),
1215 i, zone_count[i]);
1216 }
1217 }
1218
1219 static void
1220 check(void) {
1221 memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count));
1222 memset(zone_count, 0, get_nzones() * sizeof(*zone_count));
1223 check_zones(ROOT_INO);
1224 recursive_check(ROOT_INO);
1225 check_counts();
1226 }
1227
1228 static void
1229 check2(void) {
1230 memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count));
1231 memset(zone_count, 0, get_nzones() * sizeof(*zone_count));
1232 check_zones2(ROOT_INO);
1233 recursive_check2(ROOT_INO);
1234 check_counts2();
1235 }
1236
1237 int
1238 main(int argc, char **argv) {
1239 struct termios tmp;
1240 int count;
1241 int retcode = 0;
1242
1243 setlocale(LC_ALL, "");
1244 bindtextdomain(PACKAGE, LOCALEDIR);
1245 textdomain(PACKAGE);
1246 atexit(close_stdout);
1247
1248 if (argc == 2 &&
1249 (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))) {
1250 printf(UTIL_LINUX_VERSION);
1251 exit(FSCK_EX_OK);
1252 }
1253
1254 if (INODE_SIZE * MINIX_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
1255 die(_("bad inode size"));
1256 if (INODE2_SIZE * MINIX2_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
1257 die(_("bad v2 inode size"));
1258
1259 while (argc-- > 1) {
1260 argv++;
1261 if (argv[0][0] != '-') {
1262 if (device_name)
1263 usage();
1264 else
1265 device_name = argv[0];
1266 } else
1267 while (*++argv[0])
1268 switch (argv[0][0]) {
1269 case 'l':
1270 list = 1;
1271 break;
1272 case 'a':
1273 automatic = 1;
1274 repair = 1;
1275 break;
1276 case 'r':
1277 automatic = 0;
1278 repair = 1;
1279 break;
1280 case 'v':
1281 verbose = 1;
1282 break;
1283 case 's':
1284 show = 1;
1285 break;
1286 case 'm':
1287 warn_mode = 1;
1288 break;
1289 case 'f':
1290 force = 1;
1291 break;
1292 default:
1293 usage();
1294 }
1295 }
1296 if (!device_name)
1297 usage();
1298 check_mount(); /* trying to check a mounted filesystem? */
1299 if (repair && !automatic) {
1300 if (!isatty(0) || !isatty(1))
1301 die(_("need terminal for interactive repairs"));
1302 }
1303 IN = open(device_name, repair ? O_RDWR : O_RDONLY);
1304 if (IN < 0)
1305 die(_("unable to open '%s': %s"), device_name, strerror(errno));
1306 for (count = 0; count < 3; count++)
1307 sync();
1308 read_superblock();
1309
1310 /* Determine whether or not we should continue with the checking. This
1311 * is based on the status of the filesystem valid and error flags and
1312 * whether or not the -f switch was specified on the command line. */
1313 if (!(Super.s_state & MINIX_ERROR_FS) &&
1314 (Super.s_state & MINIX_VALID_FS) && !force) {
1315 if (repair)
1316 printf(_("%s is clean, no check.\n"), device_name);
1317 return retcode;
1318 } else if (force)
1319 printf(_("Forcing filesystem check on %s.\n"), device_name);
1320 else if (repair)
1321 printf(_("Filesystem on %s is dirty, needs checking.\n"),
1322 device_name);
1323
1324 read_tables();
1325
1326 /* Restore the terminal state on fatal signals. We don't do this for
1327 * SIGALRM, SIGUSR1 or SIGUSR2. */
1328 signal(SIGINT, fatalsig);
1329 signal(SIGQUIT, fatalsig);
1330 signal(SIGTERM, fatalsig);
1331
1332 if (repair && !automatic) {
1333 tcgetattr(0, &termios);
1334 tmp = termios;
1335 tmp.c_lflag &= ~(ICANON | ECHO);
1336 tcsetattr(0, TCSANOW, &tmp);
1337 termios_set = 1;
1338 }
1339
1340 if (fs_version == 2) {
1341 check_root2();
1342 check2();
1343 } else {
1344 check_root();
1345 check();
1346 }
1347 if (verbose) {
1348 unsigned long i, free;
1349
1350 for (i = 1, free = 0; i <= get_ninodes(); i++)
1351 if (!inode_in_use(i))
1352 free++;
1353 printf(_("\n%6ld inodes used (%ld%%)\n"),
1354 (get_ninodes() - free),
1355 100 * (get_ninodes() - free) / get_ninodes());
1356 for (i = get_first_zone(), free = 0; i < get_nzones(); i++)
1357 if (!zone_in_use(i))
1358 free++;
1359 printf(_("%6ld zones used (%ld%%)\n"), (get_nzones() - free),
1360 100 * (get_nzones() - free) / get_nzones());
1361 printf(_("\n%6d regular files\n"
1362 "%6d directories\n"
1363 "%6d character device files\n"
1364 "%6d block device files\n"
1365 "%6d links\n"
1366 "%6d symbolic links\n"
1367 "------\n"
1368 "%6d files\n"),
1369 regular, directory, chardev, blockdev,
1370 links - 2 * directory + 1, symlinks,
1371 total - 2 * directory + 1);
1372 }
1373 if (changed) {
1374 write_tables();
1375 printf(_("----------------------------\n"
1376 "FILE SYSTEM HAS BEEN CHANGED\n"
1377 "----------------------------\n"));
1378 for (count = 0; count < 3; count++)
1379 sync();
1380 } else if (repair)
1381 write_super_block();
1382
1383 if (repair && !automatic)
1384 tcsetattr(0, TCSANOW, &termios);
1385
1386 if (changed)
1387 retcode += 3;
1388 if (errors_uncorrected)
1389 retcode += 4;
1390 return retcode;
1391 }