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