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