2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * fsck.minix.c - a file system consistency checker for Linux.
11 * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
12 * as per the GNU copyleft.
15 * 09.11.91 - made the first rudimentary functions
17 * 10.11.91 - updated, does checking, no repairs yet.
18 * Sent out to the mailing-list for testing.
20 * 14.11.91 - Testing seems to have gone well. Added some
21 * correction-code, and changed some functions.
23 * 15.11.91 - More correction code. Hopefully it notices most
24 * cases now, and tries to do something about them.
26 * 16.11.91 - More corrections (thanks to Mika Jalava). Most
27 * things seem to work now. Yeah, sure.
30 * 19.04.92 - Had to start over again from this old version, as a
31 * kernel bug ate my enhanced fsck in February.
33 * 28.02.93 - added support for different directory entry sizes..
35 * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
36 * super-block information
38 * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
39 * to that required by fsutil
41 * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
42 * Added support for file system valid flag. Also
43 * added program_version variable and output of
44 * program name and version number when program
47 * 30.10.94 - added support for v2 filesystem
48 * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
50 * 10.12.94 - added test to prevent checking of mounted fs adapted
51 * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
52 * program. (Daniel Quinlan, quinlan@yggdrasil.com)
54 * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such
55 * for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
57 * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
58 * (Russell King). He made them for ARM. It would seem
59 * that the ARM is powerful enough to do this in C whereas
60 * i386 and m64k must use assembly to get it fast >:-)
61 * This should make minix fsck systemindependent.
62 * (janl@math.uio.no, Nicolai Langfeldt)
64 * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler
65 * warnings. Added mc68k bitops from
66 * Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
68 * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by
71 * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
72 * - added Native Language Support
74 * 2008-04-06 James Youngman <jay@gnu.org>
75 * - Issue better error message if we fail to open the device.
76 * - Restore terminal state if we get a fatal signal.
79 * I've had no time to add comments - hopefully the function names
80 * are comments enough. As with all file system checkers, this assumes
81 * the file system is quiescent - don't use it on a mounted device
82 * unless you can be sure nobody is writing to it (and remember that the
83 * kernel can write to it when it searches for files).
101 #include "exitcodes.h"
102 #include "minix_programs.h"
104 #include "pathnames.h"
106 #include "ismounted.h"
108 #include "closestream.h"
110 #include "strutils.h"
113 #define YESNO_LENGTH 64
115 /* Global variables used in minix_programs.h inline functions */
117 char *super_block_buffer
;
119 static char *inode_buffer
;
121 #define Inode (((struct minix_inode *) inode_buffer) - 1)
122 #define Inode2 (((struct minix2_inode *) inode_buffer) - 1)
124 static char *device_name
;
125 static int device_fd
;
126 static int repair
, automatic
, verbose
, list
, show
, warn_mode
, force
;
127 static int directory
, regular
, blockdev
, chardev
, links
, symlinks
, total
;
129 static int changed
; /* flags if the filesystem has been changed */
130 static int errors_uncorrected
; /* flag if some error was not corrected */
131 static size_t dirsize
= 16;
132 static size_t namelen
= 14;
133 static struct termios termios
;
134 static volatile sig_atomic_t termios_set
;
138 static int name_depth
;
139 static char name_list
[MAX_DEPTH
][MINIX_NAME_MAX
+ 1];
141 /* Copy of the previous, just for error reporting - see get_current_name. This
142 * is a waste of 12kB or so. */
143 static char current_name
[MAX_DEPTH
* (MINIX_NAME_MAX
+ 1) + 1];
145 static unsigned char *inode_count
= NULL
;
146 static unsigned char *zone_count
= NULL
;
148 static void recursive_check(unsigned int ino
);
149 static void recursive_check2(unsigned int ino
);
151 static char *inode_map
;
152 static char *zone_map
;
154 #define inode_in_use(x) (isset(inode_map,(x)) != 0)
155 #define zone_in_use(x) (isset(zone_map,(x)-get_first_zone()+1) != 0)
157 #define mark_inode(x) (setbit(inode_map,(x)),changed=1)
158 #define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
160 #define mark_zone(x) (setbit(zone_map,(x)-get_first_zone()+1),changed=1)
161 #define unmark_zone(x) (clrbit(zone_map,(x)-get_first_zone()+1),changed=1)
166 tcsetattr(STDIN_FILENO
, TCSANOW
, &termios
);
171 /* We received a fatal signal. Reset the terminal. Also reset the
172 * signal handler and re-send the signal, so that the parent process
173 * knows which signal actually caused our death. */
174 signal(sig
, SIG_DFL
);
179 static void __attribute__((__noreturn__
))
185 static void __attribute__((__noreturn__
))
188 fputs(USAGE_HEADER
, out
);
189 fprintf(out
, _(" %s [options] <device>\n"), program_invocation_short_name
);
190 fputs(USAGE_SEPARATOR
, out
);
191 fputs(_("Check the consistency of a Minix filesystem.\n"), out
);
192 fputs(USAGE_OPTIONS
, out
);
193 fputs(_(" -l, --list list all filenames\n"), out
);
194 fputs(_(" -a, --auto automatic repair\n"), out
);
195 fputs(_(" -r, --repair interactive repair\n"), out
);
196 fputs(_(" -v, --verbose be verbose\n"), out
);
197 fputs(_(" -s, --super output super-block information\n"), out
);
198 fputs(_(" -m, --uncleared activate mode not cleared warnings\n"), out
);
199 fputs(_(" -f, --force force check\n"), out
);
200 fputs(USAGE_SEPARATOR
, out
);
201 fprintf(out
, USAGE_HELP_OPTIONS(18));
202 fprintf(out
, USAGE_MAN_TAIL("fsck.minix(8)"));
206 static void die(const char *fmt
, ...)
207 __attribute__ ((__format__(__printf__
, 1, 2)));
210 die(const char *fmt
, ...) {
213 fprintf(stderr
, UTIL_LINUX_VERSION
);
215 vfprintf(stderr
, fmt
, ap
);
218 leave(FSCK_EX_ERROR
);
221 /* This simply goes through the file-name data and prints out the current file. */
223 get_current_name(void) {
228 while (i
< name_depth
) {
241 ask(const char *string
, int def
) {
243 char input
[YESNO_LENGTH
];
247 errors_uncorrected
= 1;
253 errors_uncorrected
= 1;
256 /* TRANSLATORS: these yes no questions uses rpmatch(), and should be
258 printf(def
? _("%s (y/n)? ") : _("%s (n/y)? "), string
);
260 ignore_result( fgets(input
, YESNO_LENGTH
, stdin
) );
261 resp
= rpmatch(input
);
263 case RPMATCH_INVALID
:
278 errors_uncorrected
= 1;
283 /* Make certain that we aren't checking a filesystem that is on a mounted
284 * partition. Code adapted from e2fsck, Copyright (C) 1993, 1994 Theodore
285 * Ts'o. Also licensed under GPL. */
290 if (!is_mounted(device_name
))
293 printf(_("%s is mounted. "), device_name
);
294 if (isatty(STDIN_FILENO
) && isatty(STDOUT_FILENO
))
295 cont
= ask(_("Do you really want to continue"), 0);
299 printf(_("check aborted.\n"));
305 static int is_valid_zone_nr(unsigned short nr
)
307 if (nr
< get_first_zone())
309 if (nr
>= get_nzones())
314 /* check_zone_nr checks to see that *nr is a valid zone nr. If it isn't, it
315 * will possibly be repaired. Check_zone_nr sets *corrected if an error was
316 * corrected, and returns the zone (0 for no zone or a bad zone-number). */
318 check_zone_nr(unsigned short *nr
, int *corrected
) {
322 if (*nr
< get_first_zone()) {
324 printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name
);
325 } else if (*nr
>= get_nzones()) {
327 printf(_("Zone nr >= ZONES in file `%s'."), current_name
);
331 if (ask(_("Remove block"), 1)) {
339 check_zone_nr2(unsigned int *nr
, int *corrected
) {
343 if (*nr
< get_first_zone()) {
345 printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name
);
346 } else if (*nr
>= get_nzones()) {
348 printf(_("Zone nr >= ZONES in file `%s'."), current_name
);
352 if (ask(_("Remove block"), 1)) {
359 /* read-block reads block nr into the buffer at addr. */
361 read_block(unsigned int nr
, char *addr
) {
363 memset(addr
, 0, MINIX_BLOCK_SIZE
);
366 if (MINIX_BLOCK_SIZE
* nr
!= lseek(device_fd
, MINIX_BLOCK_SIZE
* nr
, SEEK_SET
)) {
368 printf(_("Read error: unable to seek to block in file '%s'\n"),
370 memset(addr
, 0, MINIX_BLOCK_SIZE
);
371 errors_uncorrected
= 1;
372 } else if (MINIX_BLOCK_SIZE
!= read(device_fd
, addr
, MINIX_BLOCK_SIZE
)) {
374 printf(_("Read error: bad block in file '%s'\n"), current_name
);
375 memset(addr
, 0, MINIX_BLOCK_SIZE
);
376 errors_uncorrected
= 1;
380 /* write_block writes block nr to disk. */
382 write_block(unsigned int nr
, char *addr
) {
385 if (nr
< get_first_zone() || nr
>= get_nzones()) {
386 printf(_("Internal error: trying to write bad block\n"
387 "Write request ignored\n"));
388 errors_uncorrected
= 1;
391 if (MINIX_BLOCK_SIZE
* nr
!= lseek(device_fd
, MINIX_BLOCK_SIZE
* nr
, SEEK_SET
))
392 die(_("seek failed in write_block"));
393 if (MINIX_BLOCK_SIZE
!= write(device_fd
, addr
, MINIX_BLOCK_SIZE
)) {
395 printf(_("Write error: bad block in file '%s'\n"),
397 errors_uncorrected
= 1;
401 /* map-block calculates the absolute block nr of a block in a file. It sets
402 * 'changed' if the inode has needed changing, and re-writes any indirect
403 * blocks with errors. */
405 map_block(struct minix_inode
*inode
, unsigned int blknr
) {
406 unsigned short ind
[MINIX_BLOCK_SIZE
>> 1];
407 unsigned short dind
[MINIX_BLOCK_SIZE
>> 1];
408 int blk_chg
, block
, result
;
412 return check_zone_nr(inode
->i_zone
+ blknr
, &changed
);
415 block
= check_zone_nr(inode
->i_zone
+ 7, &changed
);
416 read_block(block
, (char *)ind
);
418 result
= check_zone_nr(blknr
+ ind
, &blk_chg
);
420 write_block(block
, (char *)ind
);
424 block
= check_zone_nr(inode
->i_zone
+ 8, &changed
);
425 read_block(block
, (char *)dind
);
428 if (ARRAY_SIZE(dind
) <= range
) {
429 printf(_("Warning: block out of range\n"));
432 result
= check_zone_nr(dind
+ range
, &blk_chg
);
434 write_block(block
, (char *)dind
);
436 read_block(block
, (char *)ind
);
438 result
= check_zone_nr(ind
+ (blknr
% 512), &blk_chg
);
440 write_block(block
, (char *)ind
);
445 map_block2(struct minix2_inode
*inode
, unsigned int blknr
) {
446 unsigned int ind
[MINIX_BLOCK_SIZE
>> 2];
447 unsigned int dind
[MINIX_BLOCK_SIZE
>> 2];
448 unsigned int tind
[MINIX_BLOCK_SIZE
>> 2];
449 int blk_chg
, block
, result
;
452 return check_zone_nr2(inode
->i_zone
+ blknr
, &changed
);
455 block
= check_zone_nr2(inode
->i_zone
+ 7, &changed
);
456 read_block(block
, (char *)ind
);
458 result
= check_zone_nr2(blknr
+ ind
, &blk_chg
);
460 write_block(block
, (char *)ind
);
464 if (blknr
< 256 * 256) {
465 block
= check_zone_nr2(inode
->i_zone
+ 8, &changed
);
466 read_block(block
, (char *)dind
);
468 result
= check_zone_nr2(dind
+ blknr
/ 256, &blk_chg
);
470 write_block(block
, (char *)dind
);
472 read_block(block
, (char *)ind
);
474 result
= check_zone_nr2(ind
+ blknr
% 256, &blk_chg
);
476 write_block(block
, (char *)ind
);
480 block
= check_zone_nr2(inode
->i_zone
+ 9, &changed
);
481 read_block(block
, (char *)tind
);
483 result
= check_zone_nr2(tind
+ blknr
/ (256 * 256), &blk_chg
);
485 write_block(block
, (char *)tind
);
487 read_block(block
, (char *)dind
);
489 result
= check_zone_nr2(dind
+ (blknr
/ 256) % 256, &blk_chg
);
491 write_block(block
, (char *)dind
);
493 read_block(block
, (char *)ind
);
495 result
= check_zone_nr2(ind
+ blknr
% 256, &blk_chg
);
497 write_block(block
, (char *)ind
);
502 write_super_block(void) {
503 /* v3 super block does not track state */
506 /* Set the state of the filesystem based on whether or not there are
507 * uncorrected errors. The filesystem valid flag is unconditionally
508 * set if we get this far. */
509 Super
.s_state
|= MINIX_VALID_FS
;
510 if (errors_uncorrected
)
511 Super
.s_state
|= MINIX_ERROR_FS
;
513 Super
.s_state
&= ~MINIX_ERROR_FS
;
515 if (MINIX_BLOCK_SIZE
!= lseek(device_fd
, MINIX_BLOCK_SIZE
, SEEK_SET
))
516 die(_("seek failed in write_super_block"));
517 if (MINIX_BLOCK_SIZE
!= write(device_fd
, super_block_buffer
, MINIX_BLOCK_SIZE
))
518 die(_("unable to write super-block"));
523 unsigned long buffsz
= get_inode_buffer_size();
524 unsigned long imaps
= get_nimaps();
525 unsigned long zmaps
= get_nzmaps();
529 if (write_all(device_fd
, inode_map
, imaps
* MINIX_BLOCK_SIZE
))
530 die(_("Unable to write inode map"));
532 if (write_all(device_fd
, zone_map
, zmaps
* MINIX_BLOCK_SIZE
))
533 die(_("Unable to write zone map"));
535 if (write_all(device_fd
, inode_buffer
, buffsz
))
536 die(_("Unable to write inodes"));
542 char blk
[MINIX_BLOCK_SIZE
];
545 if (fs_version
== 2 || fs_version
== 3)
546 block
= Inode2
[ROOT_INO
].i_zone
[0];
548 block
= Inode
[ROOT_INO
].i_zone
[0];
549 read_block(block
, blk
);
551 for (size
= 16; size
< MINIX_BLOCK_SIZE
; size
<<= 1) {
552 if (strcmp(blk
+ size
+ 2, "..") == 0) {
562 read_superblock(void) {
563 if (MINIX_BLOCK_SIZE
!= lseek(device_fd
, MINIX_BLOCK_SIZE
, SEEK_SET
))
564 die(_("seek failed"));
566 super_block_buffer
= calloc(1, MINIX_BLOCK_SIZE
);
567 if (!super_block_buffer
)
568 die(_("unable to alloc buffer for superblock"));
570 if (MINIX_BLOCK_SIZE
!= read(device_fd
, super_block_buffer
, MINIX_BLOCK_SIZE
))
571 die(_("unable to read super block"));
572 if (Super
.s_magic
== MINIX_SUPER_MAGIC
) {
576 } else if (Super
.s_magic
== MINIX_SUPER_MAGIC2
) {
580 } else if (Super
.s_magic
== MINIX2_SUPER_MAGIC
) {
584 } else if (Super
.s_magic
== MINIX2_SUPER_MAGIC2
) {
588 } else if (Super3
.s_magic
== MINIX3_SUPER_MAGIC
) {
593 die(_("bad magic number in super-block"));
594 if (get_zone_size() != 0 || MINIX_BLOCK_SIZE
!= 1024)
595 die(_("Only 1k blocks/zones supported"));
596 if (get_ninodes() == 0 || get_ninodes() == UINT32_MAX
)
597 die(_("bad s_ninodes field in super-block"));
598 if (get_nimaps() * MINIX_BLOCK_SIZE
* 8 < get_ninodes() + 1)
599 die(_("bad s_imap_blocks field in super-block"));
600 if (get_first_zone() > (off_t
) get_nzones())
601 die(_("bad s_firstdatazone field in super-block"));
602 if (get_nzmaps() * MINIX_BLOCK_SIZE
* 8 <
603 get_nzones() - get_first_zone() + 1)
604 die(_("bad s_zmap_blocks field in super-block"));
609 unsigned long inodes
= get_ninodes();
610 size_t buffsz
= get_inode_buffer_size();
611 off_t norm_first_zone
= first_zone_data();
612 off_t first_zone
= get_first_zone();
613 unsigned long zones
= get_nzones();
614 unsigned long imaps
= get_nimaps();
615 unsigned long zmaps
= get_nzmaps();
618 inode_map
= malloc(imaps
* MINIX_BLOCK_SIZE
);
620 die(_("Unable to allocate buffer for inode map"));
621 zone_map
= malloc(zmaps
* MINIX_BLOCK_SIZE
);
623 die(_("Unable to allocate buffer for zone map"));
624 inode_buffer
= malloc(buffsz
);
626 die(_("Unable to allocate buffer for inodes"));
627 inode_count
= calloc(1, inodes
+ 1);
629 die(_("Unable to allocate buffer for inode count"));
630 zone_count
= calloc(1, zones
);
632 die(_("Unable to allocate buffer for zone count"));
634 rc
= read(device_fd
, inode_map
, imaps
* MINIX_BLOCK_SIZE
);
635 if (rc
< 0 || imaps
* MINIX_BLOCK_SIZE
!= (size_t) rc
)
636 die(_("Unable to read inode map"));
638 rc
= read(device_fd
, zone_map
, zmaps
* MINIX_BLOCK_SIZE
);
639 if (rc
< 0 || zmaps
* MINIX_BLOCK_SIZE
!= (size_t) rc
)
640 die(_("Unable to read zone map"));
642 rc
= read(device_fd
, inode_buffer
, buffsz
);
643 if (rc
< 0 || buffsz
!= (size_t) rc
)
644 die(_("Unable to read inodes"));
645 if (norm_first_zone
!= first_zone
) {
646 printf(_("Warning: Firstzone != Norm_firstzone\n"));
647 errors_uncorrected
= 1;
651 printf(_("%ld inodes\n"), inodes
);
652 printf(_("%ld blocks\n"), zones
);
653 printf(_("Firstdatazone=%jd (%jd)\n"),
654 (intmax_t)first_zone
, (intmax_t)norm_first_zone
);
655 printf(_("Zonesize=%d\n"), MINIX_BLOCK_SIZE
<< get_zone_size());
656 printf(_("Maxsize=%zu\n"), get_max_size());
658 printf(_("Filesystem state=%d\n"), Super
.s_state
);
659 printf(_("namelen=%zd\n\n"), namelen
);
663 static struct minix_inode
*
664 get_inode(unsigned int nr
) {
665 struct minix_inode
*inode
;
667 if (!nr
|| nr
> get_ninodes())
671 if (!inode_count
[nr
]) {
672 if (!inode_in_use(nr
)) {
674 printf(_("Inode %d marked unused, "
675 "but used for file '%s'\n"), nr
, current_name
);
677 if (ask(_("Mark in use"), 1))
680 errors_uncorrected
= 1;
683 if (S_ISDIR(inode
->i_mode
))
685 else if (S_ISREG(inode
->i_mode
))
687 else if (S_ISCHR(inode
->i_mode
))
689 else if (S_ISBLK(inode
->i_mode
))
691 else if (S_ISLNK(inode
->i_mode
))
693 else if (S_ISSOCK(inode
->i_mode
))
695 else if (S_ISFIFO(inode
->i_mode
))
699 printf(_("The file `%s' has mode %05o\n"),
700 current_name
, inode
->i_mode
);
705 if (!++inode_count
[nr
]) {
706 printf(_("Warning: inode count too big.\n"));
708 errors_uncorrected
= 1;
713 static struct minix2_inode
*
714 get_inode2(unsigned int nr
) {
715 struct minix2_inode
*inode
;
717 if (!nr
|| nr
> get_ninodes())
721 if (!inode_count
[nr
]) {
722 if (!inode_in_use(nr
)) {
724 printf(_("Inode %d marked unused, "
725 "but used for file '%s'\n"), nr
, current_name
);
727 if (ask(_("Mark in use"), 1))
730 errors_uncorrected
= 1;
733 if (S_ISDIR(inode
->i_mode
))
735 else if (S_ISREG(inode
->i_mode
))
737 else if (S_ISCHR(inode
->i_mode
))
739 else if (S_ISBLK(inode
->i_mode
))
741 else if (S_ISLNK(inode
->i_mode
))
743 else if (S_ISSOCK(inode
->i_mode
)) ;
744 else if (S_ISFIFO(inode
->i_mode
)) ;
747 printf(_("The file `%s' has mode %05o\n"),
748 current_name
, inode
->i_mode
);
752 if (!++inode_count
[nr
]) {
753 printf(_("Warning: inode count too big.\n"));
755 errors_uncorrected
= 1;
762 struct minix_inode
*inode
= Inode
+ ROOT_INO
;
764 if (!inode
|| !S_ISDIR(inode
->i_mode
))
765 die(_("root inode isn't a directory"));
770 struct minix2_inode
*inode
= Inode2
+ ROOT_INO
;
772 if (!inode
|| !S_ISDIR(inode
->i_mode
))
773 die(_("root inode isn't a directory"));
777 add_zone(unsigned short *znr
, int *corrected
) {
780 block
= check_zone_nr(znr
, corrected
);
783 if (zone_count
[block
]) {
785 printf(_("Block has been used before. Now in file `%s'."),
787 if (ask(_("Clear"), 1)) {
795 if (!zone_in_use(block
)) {
797 printf(_("Block %d in file `%s' is marked not in use."),
798 block
, current_name
);
799 if (ask(_("Correct"), 1))
802 if (!++zone_count
[block
])
808 add_zone2(unsigned int *znr
, int *corrected
) {
811 block
= check_zone_nr2(znr
, corrected
);
814 if (zone_count
[block
]) {
816 printf(_("Block has been used before. Now in file `%s'."),
818 if (ask(_("Clear"), 1)) {
826 if (!zone_in_use(block
)) {
828 printf(_("Block %d in file `%s' is marked not in use."),
829 block
, current_name
);
830 if (ask(_("Correct"), 1))
833 if (!++zone_count
[block
])
839 add_zone_ind(unsigned short *znr
, int *corrected
) {
840 static char blk
[MINIX_BLOCK_SIZE
];
844 block
= add_zone(znr
, corrected
);
847 read_block(block
, blk
);
848 for (i
= 0; i
< (MINIX_BLOCK_SIZE
>> 1); i
++)
849 add_zone(i
+ (unsigned short *)blk
, &chg_blk
);
851 write_block(block
, blk
);
855 add_zone_ind2(unsigned int *znr
, int *corrected
) {
856 static char blk
[MINIX_BLOCK_SIZE
];
860 block
= add_zone2(znr
, corrected
);
863 read_block(block
, blk
);
864 for (i
= 0; i
< MINIX_BLOCK_SIZE
>> 2; i
++)
865 add_zone2(i
+ (unsigned int *)blk
, &chg_blk
);
867 write_block(block
, blk
);
871 add_zone_dind(unsigned short *znr
, int *corrected
) {
872 static char blk
[MINIX_BLOCK_SIZE
];
876 block
= add_zone(znr
, corrected
);
879 read_block(block
, blk
);
880 for (i
= 0; i
< (MINIX_BLOCK_SIZE
>> 1); i
++)
881 add_zone_ind(i
+ (unsigned short *)blk
, &blk_chg
);
883 write_block(block
, blk
);
887 add_zone_dind2(unsigned int *znr
, int *corrected
) {
888 static char blk
[MINIX_BLOCK_SIZE
];
892 block
= add_zone2(znr
, corrected
);
895 read_block(block
, blk
);
896 for (i
= 0; i
< MINIX_BLOCK_SIZE
>> 2; i
++)
897 add_zone_ind2(i
+ (unsigned int *)blk
, &blk_chg
);
899 write_block(block
, blk
);
903 add_zone_tind2(unsigned int *znr
, int *corrected
) {
904 static char blk
[MINIX_BLOCK_SIZE
];
908 block
= add_zone2(znr
, corrected
);
911 read_block(block
, blk
);
912 for (i
= 0; i
< MINIX_BLOCK_SIZE
>> 2; i
++)
913 add_zone_dind2(i
+ (unsigned int *)blk
, &blk_chg
);
915 write_block(block
, blk
);
919 check_zones(unsigned int i
) {
920 struct minix_inode
*inode
;
922 if (!i
|| i
> get_ninodes())
924 if (inode_count
[i
] > 1) /* have we counted this file already? */
927 if (!S_ISDIR(inode
->i_mode
) && !S_ISREG(inode
->i_mode
) &&
928 !S_ISLNK(inode
->i_mode
))
930 for (i
= 0; i
< 7; i
++)
931 add_zone(i
+ inode
->i_zone
, &changed
);
932 add_zone_ind(7 + inode
->i_zone
, &changed
);
933 add_zone_dind(8 + inode
->i_zone
, &changed
);
937 check_zones2(unsigned int i
) {
938 struct minix2_inode
*inode
;
940 if (!i
|| i
> get_ninodes())
942 if (inode_count
[i
] > 1) /* have we counted this file already? */
945 if (!S_ISDIR(inode
->i_mode
) && !S_ISREG(inode
->i_mode
)
946 && !S_ISLNK(inode
->i_mode
))
948 for (i
= 0; i
< 7; i
++)
949 add_zone2(i
+ inode
->i_zone
, &changed
);
950 add_zone_ind2(7 + inode
->i_zone
, &changed
);
951 add_zone_dind2(8 + inode
->i_zone
, &changed
);
952 add_zone_tind2(9 + inode
->i_zone
, &changed
);
956 check_file(struct minix_inode
*dir
, unsigned int offset
) {
957 static char blk
[MINIX_BLOCK_SIZE
+ 2];
958 struct minix_inode
*inode
;
963 block
= map_block(dir
, offset
/ MINIX_BLOCK_SIZE
);
964 read_block(block
, blk
);
965 name
= blk
+ (offset
% MINIX_BLOCK_SIZE
) + 2;
966 ino
= *(unsigned short *)(name
- 2);
967 if (ino
> get_ninodes()) {
969 printf(_("The directory '%s' contains a bad inode number "
970 "for file '%.*s'."), current_name
, (int)namelen
, name
);
971 if (ask(_(" Remove"), 1)) {
972 *(unsigned short *)(name
- 2) = 0;
973 write_block(block
, blk
);
977 if (name_depth
< MAX_DEPTH
)
978 xstrncpy(name_list
[name_depth
], name
, namelen
);
982 inode
= get_inode(ino
);
985 if (!inode
|| strcmp(".", name
) != 0) {
987 printf(_("%s: bad directory: '.' isn't first\n"),
989 errors_uncorrected
= 1;
993 if (offset
== dirsize
) {
994 if (!inode
|| strcmp("..", name
) != 0) {
996 printf(_("%s: bad directory: '..' isn't second\n"),
998 errors_uncorrected
= 1;
1004 if (name_depth
< MAX_DEPTH
)
1005 xstrncpy(name_list
[name_depth
], name
, namelen
);
1011 printf("%6d %07o %3d ", ino
,
1012 inode
->i_mode
, inode
->i_nlinks
);
1014 printf("%s", current_name
);
1015 if (S_ISDIR(inode
->i_mode
))
1021 if (inode
&& S_ISDIR(inode
->i_mode
))
1022 recursive_check(ino
);
1027 check_file2(struct minix2_inode
*dir
, unsigned int offset
) {
1028 static char blk
[MINIX_BLOCK_SIZE
+ 4];
1029 struct minix2_inode
*inode
;
1033 const int version_offset
= fs_version
== 3 ? 4 : 2;
1035 block
= map_block2(dir
, offset
/ MINIX_BLOCK_SIZE
);
1036 read_block(block
, blk
);
1037 name
= blk
+ (offset
% MINIX_BLOCK_SIZE
) + version_offset
;
1038 ino
= version_offset
== 4 ? *(uint32_t *)(name
- version_offset
)
1039 : *(uint16_t *)(name
- version_offset
);
1040 if (ino
> get_ninodes()) {
1042 printf(_("The directory '%s' contains a bad inode number "
1043 "for file '%.*s'."), current_name
, (int)namelen
, name
);
1044 if (ask(_(" Remove"), 1)) {
1045 memset(name
- version_offset
, 0, version_offset
);
1046 write_block(block
, blk
);
1050 if (name_depth
< MAX_DEPTH
)
1051 xstrncpy(name_list
[name_depth
], name
, namelen
);
1055 inode
= get_inode2(ino
);
1058 if (!inode
|| strcmp(".", name
) != 0) {
1060 printf(_("%s: bad directory: '.' isn't first\n"),
1062 errors_uncorrected
= 1;
1066 if (offset
== dirsize
) {
1067 if (!inode
|| strcmp("..", name
) != 0) {
1069 printf(_("%s: bad directory: '..' isn't second\n"),
1071 errors_uncorrected
= 1;
1080 printf("%6ju %07o %3d ", (uintmax_t)ino
, inode
->i_mode
,
1083 printf("%s", current_name
);
1084 if (S_ISDIR(inode
->i_mode
))
1090 if (inode
&& S_ISDIR(inode
->i_mode
))
1091 recursive_check2(ino
);
1096 recursive_check(unsigned int ino
) {
1097 struct minix_inode
*dir
;
1101 if (!S_ISDIR(dir
->i_mode
))
1102 die(_("internal error"));
1103 if (dir
->i_size
< 2 * dirsize
) {
1105 printf(_("%s: bad directory: size < 32"), current_name
);
1106 errors_uncorrected
= 1;
1109 if ((!repair
|| automatic
) && !is_valid_zone_nr(*dir
->i_zone
)) {
1111 printf(_("%s: bad directory: invalid i_zone, use --repair to fix\n"), current_name
);
1114 for (offset
= 0; offset
< dir
->i_size
; offset
+= dirsize
)
1115 check_file(dir
, offset
);
1119 recursive_check2(unsigned int ino
) {
1120 struct minix2_inode
*dir
;
1124 if (!S_ISDIR(dir
->i_mode
))
1125 die(_("internal error"));
1126 if (dir
->i_size
< 2 * dirsize
) {
1128 printf(_("%s: bad directory: size < 32"), current_name
);
1129 errors_uncorrected
= 1;
1131 for (offset
= 0; offset
< dir
->i_size
; offset
+= dirsize
)
1132 check_file2(dir
, offset
);
1139 if (MINIX_BLOCK_SIZE
* i
!= lseek(device_fd
, MINIX_BLOCK_SIZE
* i
, SEEK_SET
))
1140 die(_("seek failed in bad_zone"));
1141 return (MINIX_BLOCK_SIZE
!= read(device_fd
, buffer
, MINIX_BLOCK_SIZE
));
1145 check_counts(void) {
1148 for (i
= 1; i
<= get_ninodes(); i
++) {
1149 if (!inode_in_use(i
) && Inode
[i
].i_mode
&& warn_mode
) {
1150 printf(_("Inode %lu mode not cleared."), i
);
1151 if (ask(_("Clear"), 1)) {
1152 Inode
[i
].i_mode
= 0;
1156 if (!inode_count
[i
]) {
1157 if (!inode_in_use(i
))
1159 printf(_("Inode %lu not used, marked used in the bitmap."), i
);
1160 if (ask(_("Clear"), 1))
1164 if (!inode_in_use(i
)) {
1165 printf(_("Inode %lu used, marked unused in the bitmap."), i
);
1166 if (ask(_("Set"), 1))
1169 if (Inode
[i
].i_nlinks
!= inode_count
[i
]) {
1170 printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."),
1171 i
, Inode
[i
].i_mode
, Inode
[i
].i_nlinks
,
1173 if (ask(_("Set i_nlinks to count"), 1)) {
1174 Inode
[i
].i_nlinks
= inode_count
[i
];
1179 for (i
= get_first_zone(); i
< get_nzones(); i
++) {
1180 if (zone_in_use(i
) == zone_count
[i
])
1182 if (!zone_count
[i
]) {
1185 printf(_("Zone %lu: marked in use, no file uses it."),
1187 if (ask(_("Unmark"), 1))
1192 printf(_("Zone %lu: in use, counted=%d\n"),
1195 printf(_("Zone %lu: not in use, counted=%d\n"),
1201 check_counts2(void) {
1204 for (i
= 1; i
<= get_ninodes(); i
++) {
1205 if (!inode_in_use(i
) && Inode2
[i
].i_mode
&& warn_mode
) {
1206 printf(_("Inode %lu mode not cleared."), i
);
1207 if (ask(_("Clear"), 1)) {
1208 Inode2
[i
].i_mode
= 0;
1212 if (!inode_count
[i
]) {
1213 if (!inode_in_use(i
))
1215 printf(_("Inode %lu not used, marked used in the bitmap."), i
);
1216 if (ask(_("Clear"), 1))
1220 if (!inode_in_use(i
)) {
1221 printf(_("Inode %lu used, marked unused in the bitmap."), i
);
1222 if (ask(_("Set"), 1))
1225 if (Inode2
[i
].i_nlinks
!= inode_count
[i
]) {
1226 printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."),
1227 i
, Inode2
[i
].i_mode
, Inode2
[i
].i_nlinks
,
1229 if (ask(_("Set i_nlinks to count"), 1)) {
1230 Inode2
[i
].i_nlinks
= inode_count
[i
];
1235 for (i
= get_first_zone(); i
< get_nzones(); i
++) {
1236 if (zone_in_use(i
) == zone_count
[i
])
1238 if (!zone_count
[i
]) {
1241 printf(_("Zone %lu: marked in use, no file uses it."),
1243 if (ask(_("Unmark"), 1))
1248 printf(_("Zone %lu: in use, counted=%d\n"),
1251 printf(_("Zone %lu: not in use, counted=%d\n"),
1258 memset(inode_count
, 0, (get_ninodes() + 1) * sizeof(*inode_count
));
1259 memset(zone_count
, 0, get_nzones() * sizeof(*zone_count
));
1260 check_zones(ROOT_INO
);
1261 recursive_check(ROOT_INO
);
1267 memset(inode_count
, 0, (get_ninodes() + 1) * sizeof(*inode_count
));
1268 memset(zone_count
, 0, get_nzones() * sizeof(*zone_count
));
1269 check_zones2(ROOT_INO
);
1270 recursive_check2(ROOT_INO
);
1275 main(int argc
, char **argv
) {
1278 int retcode
= FSCK_EX_OK
;
1280 static const struct option longopts
[] = {
1281 {"list", no_argument
, NULL
, 'l'},
1282 {"auto", no_argument
, NULL
, 'a'},
1283 {"repair", no_argument
, NULL
, 'r'},
1284 {"verbose", no_argument
, NULL
, 'v'},
1285 {"super", no_argument
, NULL
, 's'},
1286 {"uncleared", no_argument
, NULL
, 'm'},
1287 {"force", no_argument
, NULL
, 'f'},
1288 {"version", no_argument
, NULL
, 'V'},
1289 {"help", no_argument
, NULL
, 'h'},
1293 setlocale(LC_ALL
, "");
1294 bindtextdomain(PACKAGE
, LOCALEDIR
);
1295 textdomain(PACKAGE
);
1296 close_stdout_atexit();
1298 strutils_set_exitcode(FSCK_EX_USAGE
);
1300 if (INODE_SIZE
* MINIX_INODES_PER_BLOCK
!= MINIX_BLOCK_SIZE
)
1301 die(_("bad inode size"));
1302 if (INODE2_SIZE
* MINIX2_INODES_PER_BLOCK
!= MINIX_BLOCK_SIZE
)
1303 die(_("bad v2 inode size"));
1305 while ((i
= getopt_long(argc
, argv
, "larvsmfVh", longopts
, NULL
)) != -1)
1331 print_version(FSCK_EX_OK
);
1335 errtryhelp(FSCK_EX_USAGE
);
1340 device_name
= argv
[0];
1342 warnx(_("no device specified"));
1343 errtryhelp(FSCK_EX_USAGE
);
1345 check_mount(); /* trying to check a mounted filesystem? */
1346 if (repair
&& !automatic
&& (!isatty(STDIN_FILENO
) || !isatty(STDOUT_FILENO
)))
1347 die(_("need terminal for interactive repairs"));
1349 device_fd
= open(device_name
, repair
? O_RDWR
: O_RDONLY
);
1351 die(_("cannot open %s: %s"), device_name
, strerror(errno
));
1352 for (count
= 0; count
< 3; count
++)
1356 /* Determine whether or not we should continue with the checking. This
1357 * is based on the status of the filesystem valid and error flags and
1358 * whether or not the -f switch was specified on the command line. */
1359 if (fs_version
< 3 && !(Super
.s_state
& MINIX_ERROR_FS
) &&
1360 (Super
.s_state
& MINIX_VALID_FS
) && !force
) {
1362 printf(_("%s is clean, no check.\n"), device_name
);
1367 printf(_("Forcing filesystem check on %s.\n"), device_name
);
1369 printf(_("Filesystem on %s is dirty, needs checking.\n"),
1374 /* Restore the terminal state on fatal signals. We don't do this for
1375 * SIGALRM, SIGUSR1 or SIGUSR2. */
1376 signal(SIGINT
, fatalsig
);
1377 signal(SIGQUIT
, fatalsig
);
1378 signal(SIGTERM
, fatalsig
);
1380 if (repair
&& !automatic
) {
1381 tcgetattr(STDIN_FILENO
, &termios
);
1383 tmp
.c_lflag
&= ~(ICANON
| ECHO
);
1384 tcsetattr(STDIN_FILENO
, TCSANOW
, &tmp
);
1388 if (fs_version
== 2 || fs_version
== 3) {
1396 unsigned long inode
, free
;
1398 for (inode
= 1, free
= 0; inode
<= get_ninodes(); inode
++)
1399 if (!inode_in_use(inode
))
1401 printf(_("\n%6ld inodes used (%ld%%)\n"),
1402 (get_ninodes() - free
),
1403 100 * (get_ninodes() - free
) / get_ninodes());
1404 for (inode
= get_first_zone(), free
= 0; inode
< get_nzones(); inode
++)
1405 if (!zone_in_use(inode
))
1407 printf(_("%6ld zones used (%ld%%)\n"), (get_nzones() - free
),
1408 100 * (get_nzones() - free
) / get_nzones());
1409 printf(_("\n%6d regular files\n"
1411 "%6d character device files\n"
1412 "%6d block device files\n"
1414 "%6d symbolic links\n"
1417 regular
, directory
, chardev
, blockdev
,
1418 links
- 2 * directory
+ 1, symlinks
,
1419 total
- 2 * directory
+ 1);
1423 printf(_("----------------------------\n"
1424 "FILE SYSTEM HAS BEEN CHANGED\n"
1425 "----------------------------\n"));
1426 for (count
= 0; count
< 3; count
++)
1429 write_super_block();
1431 if (repair
&& !automatic
)
1432 tcsetattr(STDIN_FILENO
, TCSANOW
, &termios
);
1434 if (close_fd(device_fd
) != 0)
1435 err(FSCK_EX_ERROR
, _("write failed"));
1438 if (errors_uncorrected
)