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