source: MondoRescue/branches/3.3/mindi-busybox/util-linux/fsck_minix.c@ 3625

Last change on this file since 3625 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 32.8 KB
RevLine 
[821]1/* vi: set sw=4 ts=4: */
2/*
3 * fsck.c - a file system consistency checker for Linux.
4 *
[1765]5 * (C) 1991, 1992 Linus Torvalds.
6 *
[2725]7 * Licensed under GPLv2, see file LICENSE in this source tree.
[821]8 */
9
10/*
11 * 09.11.91 - made the first rudimentary functions
12 *
13 * 10.11.91 - updated, does checking, no repairs yet.
14 * Sent out to the mailing-list for testing.
15 *
[3232]16 * 14.11.91 - Testing seems to have gone well. Added some
[821]17 * correction-code, and changed some functions.
18 *
19 * 15.11.91 - More correction code. Hopefully it notices most
20 * cases now, and tries to do something about them.
21 *
22 * 16.11.91 - More corrections (thanks to Mika Jalava). Most
23 * things seem to work now. Yeah, sure.
24 *
[3232]25 * 19.04.92 - Had to start over again from this old version, as a
[821]26 * kernel bug ate my enhanced fsck in february.
27 *
[3232]28 * 28.02.93 - added support for different directory entry sizes..
[821]29 *
30 * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
[2725]31 * superblock information
[821]32 *
33 * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
34 * to that required by fsutil
35 *
36 * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
[3232]37 * Added support for file system valid flag. Also
38 * added program_version variable and output of
39 * program name and version number when program
40 * is executed.
[821]41 *
[3232]42 * 30.10.94 - added support for v2 filesystem
43 * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
[821]44 *
[3232]45 * 10.12.94 - added test to prevent checking of mounted fs adapted
46 * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
47 * program. (Daniel Quinlan, quinlan@yggdrasil.com)
[821]48 *
49 * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such
[3232]50 * for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
[821]51 *
52 * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
53 * (Russell King). He made them for ARM. It would seem
[3232]54 * that the ARM is powerful enough to do this in C whereas
[821]55 * i386 and m64k must use assembly to get it fast >:-)
[3232]56 * This should make minix fsck system-independent.
57 * (janl@math.uio.no, Nicolai Langfeldt)
[821]58 *
59 * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler
60 * warnings. Added mc68k bitops from
[3232]61 * Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
[821]62 *
63 * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by
64 * Andreas Schwab.
65 *
[2725]66 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
[821]67 * - added Native Language Support
68 *
69 *
70 * I've had no time to add comments - hopefully the function names
71 * are comments enough. As with all file system checkers, this assumes
72 * the file system is quiescent - don't use it on a mounted device
73 * unless you can be sure nobody is writing to it (and remember that the
74 * kernel can write to it when it searches for files).
75 *
76 * Usage: fsck [-larvsm] device
77 * -l for a listing of all the filenames
78 * -a for automatic repairs (not implemented)
79 * -r for repairs (interactive) (not implemented)
80 * -v for verbose (tells how many files)
[2725]81 * -s for superblock info
[821]82 * -m for minix-like "mode not cleared" warnings
83 * -f force filesystem check even if filesystem marked as valid
84 *
85 * The device may be a block device or a image of one, but this isn't
86 * enforced (but it's not much fun on a character device :-).
87 */
88
[3232]89//usage:#define fsck_minix_trivial_usage
90//usage: "[-larvsmf] BLOCKDEV"
91//usage:#define fsck_minix_full_usage "\n\n"
92//usage: "Check MINIX filesystem\n"
93//usage: "\n -l List all filenames"
94//usage: "\n -r Perform interactive repairs"
95//usage: "\n -a Perform automatic repairs"
96//usage: "\n -v Verbose"
97//usage: "\n -s Output superblock information"
98//usage: "\n -m Show \"mode not cleared\" warnings"
99//usage: "\n -f Force file system check"
100
[821]101#include <mntent.h>
[1765]102#include "libbb.h"
103#include "minix.h"
[821]104
[1765]105#ifndef BLKGETSIZE
106#define BLKGETSIZE _IO(0x12,96) /* return device size */
107#endif
[821]108
[2725]109struct BUG_bad_inode_size {
110 char BUG_bad_inode1_size[(INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
111#if ENABLE_FEATURE_MINIX2
112 char BUG_bad_inode2_size[(INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
113#endif
114};
115
[821]116enum {
[1765]117#ifdef UNUSED
118 MINIX1_LINK_MAX = 250,
[821]119 MINIX2_LINK_MAX = 65530,
120 MINIX_I_MAP_SLOTS = 8,
121 MINIX_Z_MAP_SLOTS = 64,
[1765]122 MINIX_V1 = 0x0001, /* original minix fs */
123 MINIX_V2 = 0x0002, /* minix V2 fs */
124#endif
125 MINIX_NAME_MAX = 255, /* # chars in a file name */
126};
[821]127
[2725]128
[1765]129#if !ENABLE_FEATURE_MINIX2
130enum { version2 = 0 };
131#endif
[821]132
[1765]133enum { MAX_DEPTH = 32 };
[821]134
[2725]135enum { dev_fd = 3 };
136
[1765]137struct globals {
138#if ENABLE_FEATURE_MINIX2
139 smallint version2;
140#endif
141 smallint changed; /* is filesystem modified? */
142 smallint errors_uncorrected; /* flag if some error was not corrected */
143 smallint termios_set;
144 smallint dirsize;
145 smallint namelen;
[2725]146 const char *device_name;
[1765]147 int directory, regular, blockdev, chardev, links, symlinks, total;
148 char *inode_buffer;
[821]149
[1765]150 char *inode_map;
151 char *zone_map;
[821]152
[1765]153 unsigned char *inode_count;
154 unsigned char *zone_count;
[821]155
[1765]156 /* File-name data */
157 int name_depth;
158 char *name_component[MAX_DEPTH+1];
[821]159
[1765]160 /* Bigger stuff */
161 struct termios sv_termios;
[2725]162 char superblock_buffer[BLOCK_SIZE];
[1765]163 char add_zone_ind_blk[BLOCK_SIZE];
164 char add_zone_dind_blk[BLOCK_SIZE];
[2725]165 IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];)
[1765]166 char check_file_blk[BLOCK_SIZE];
[821]167
[1765]168 /* File-name data */
169 char current_name[MAX_DEPTH * MINIX_NAME_MAX];
170};
171#define G (*ptr_to_globals)
172#if ENABLE_FEATURE_MINIX2
173#define version2 (G.version2 )
[821]174#endif
[1765]175#define changed (G.changed )
176#define errors_uncorrected (G.errors_uncorrected )
177#define termios_set (G.termios_set )
178#define dirsize (G.dirsize )
179#define namelen (G.namelen )
180#define device_name (G.device_name )
181#define directory (G.directory )
182#define regular (G.regular )
183#define blockdev (G.blockdev )
184#define chardev (G.chardev )
185#define links (G.links )
186#define symlinks (G.symlinks )
187#define total (G.total )
188#define inode_buffer (G.inode_buffer )
189#define inode_map (G.inode_map )
190#define zone_map (G.zone_map )
191#define inode_count (G.inode_count )
192#define zone_count (G.zone_count )
193#define name_depth (G.name_depth )
194#define name_component (G.name_component )
195#define sv_termios (G.sv_termios )
[2725]196#define superblock_buffer (G.superblock_buffer )
[1765]197#define add_zone_ind_blk (G.add_zone_ind_blk )
198#define add_zone_dind_blk (G.add_zone_dind_blk )
199#define add_zone_tind_blk (G.add_zone_tind_blk )
200#define check_file_blk (G.check_file_blk )
201#define current_name (G.current_name )
202#define INIT_G() do { \
[2725]203 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
[1765]204 dirsize = 16; \
205 namelen = 14; \
206 current_name[0] = '/'; \
207 /*current_name[1] = '\0';*/ \
208 name_component[0] = &current_name[0]; \
209} while (0)
[821]210
[2725]211
212#define OPTION_STR "larvsmf"
213enum {
214 OPT_l = (1 << 0),
215 OPT_a = (1 << 1),
216 OPT_r = (1 << 2),
217 OPT_v = (1 << 3),
218 OPT_s = (1 << 4),
219 OPT_w = (1 << 5),
220 OPT_f = (1 << 6),
221};
222#define OPT_list (option_mask32 & OPT_l)
223#define OPT_automatic (option_mask32 & OPT_a)
224#define OPT_repair (option_mask32 & OPT_r)
225#define OPT_verbose (option_mask32 & OPT_v)
226#define OPT_show (option_mask32 & OPT_s)
227#define OPT_warn_mode (option_mask32 & OPT_w)
228#define OPT_force (option_mask32 & OPT_f)
229/* non-automatic repairs requested? */
230#define OPT_manual ((option_mask32 & (OPT_a|OPT_r)) == OPT_r)
231
232
[1765]233#define Inode1 (((struct minix1_inode *) inode_buffer)-1)
234#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
[821]235
[2725]236#define Super (*(struct minix_superblock *)(superblock_buffer))
[821]237
[1765]238#if ENABLE_FEATURE_MINIX2
239# define ZONES ((unsigned)(version2 ? Super.s_zones : Super.s_nzones))
[821]240#else
[1765]241# define ZONES ((unsigned)(Super.s_nzones))
[821]242#endif
[1765]243#define INODES ((unsigned)Super.s_ninodes)
244#define IMAPS ((unsigned)Super.s_imap_blocks)
245#define ZMAPS ((unsigned)Super.s_zmap_blocks)
246#define FIRSTZONE ((unsigned)Super.s_firstdatazone)
247#define ZONESIZE ((unsigned)Super.s_log_zone_size)
248#define MAXSIZE ((unsigned)Super.s_max_size)
249#define MAGIC (Super.s_magic)
[821]250
[1765]251/* gcc likes this more (code is smaller) than macro variant */
252static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
253{
254 return (size + n-1) / n;
255}
[821]256
[2725]257#if !ENABLE_FEATURE_MINIX2
258#define INODE_BLOCKS div_roundup(INODES, MINIX1_INODES_PER_BLOCK)
[821]259#else
[2725]260#define INODE_BLOCKS div_roundup(INODES, \
261 (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK))
[821]262#endif
263
[2725]264#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
265#define NORM_FIRSTZONE (2 + IMAPS + ZMAPS + INODE_BLOCKS)
[821]266
[1765]267/* Before you ask "where they come from?": */
268/* setbit/clrbit are supplied by sys/param.h */
[821]269
[1765]270static int minix_bit(const char *a, unsigned i)
[821]271{
[1765]272 return (a[i >> 3] & (1<<(i & 7)));
[821]273}
274
[1765]275static void minix_setbit(char *a, unsigned i)
[821]276{
[1765]277 setbit(a, i);
278 changed = 1;
[821]279}
[1765]280static void minix_clrbit(char *a, unsigned i)
[821]281{
[1765]282 clrbit(a, i);
283 changed = 1;
[821]284}
285
[1765]286/* Note: do not assume 0/1, it is 0/nonzero */
287#define zone_in_use(x) (minix_bit(zone_map,(x)-FIRSTZONE+1))
288#define inode_in_use(x) (minix_bit(inode_map,(x)))
[821]289
[1765]290#define mark_inode(x) (minix_setbit(inode_map,(x)))
291#define unmark_inode(x) (minix_clrbit(inode_map,(x)))
[821]292
[1765]293#define mark_zone(x) (minix_setbit(zone_map,(x)-FIRSTZONE+1))
294#define unmark_zone(x) (minix_clrbit(zone_map,(x)-FIRSTZONE+1))
295
296
297static void recursive_check(unsigned ino);
298#if ENABLE_FEATURE_MINIX2
299static void recursive_check2(unsigned ino);
300#endif
301
[2725]302static void die(const char *str) NORETURN;
[1765]303static void die(const char *str)
[821]304{
[1765]305 if (termios_set)
[2725]306 tcsetattr_stdin_TCSANOW(&sv_termios);
[1765]307 bb_error_msg_and_die("%s", str);
[821]308}
309
310static void push_filename(const char *name)
311{
312 // /dir/dir/dir/file
313 // ^ ^ ^
314 // [0] [1] [2] <-name_component[i]
315 if (name_depth < MAX_DEPTH) {
316 int len;
317 char *p = name_component[name_depth];
318 *p++ = '/';
319 len = sprintf(p, "%.*s", namelen, name);
320 name_component[name_depth + 1] = p + len;
321 }
322 name_depth++;
323}
324
[1765]325static void pop_filename(void)
326{
[821]327 name_depth--;
328 if (name_depth < MAX_DEPTH) {
329 *name_component[name_depth] = '\0';
330 if (!name_depth) {
331 current_name[0] = '/';
332 current_name[1] = '\0';
333 }
334 }
335}
336
337static int ask(const char *string, int def)
338{
339 int c;
340
[2725]341 if (!OPT_repair) {
342 bb_putchar('\n');
[821]343 errors_uncorrected = 1;
344 return 0;
345 }
[2725]346 if (OPT_automatic) {
347 bb_putchar('\n');
[821]348 if (!def)
349 errors_uncorrected = 1;
350 return def;
351 }
352 printf(def ? "%s (y/n)? " : "%s (n/y)? ", string);
353 for (;;) {
[2725]354 fflush_all();
[1765]355 c = getchar();
356 if (c == EOF) {
[821]357 if (!def)
358 errors_uncorrected = 1;
359 return def;
360 }
[2725]361 if (c == '\n')
362 break;
363 c |= 0x20; /* tolower */
364 if (c == 'y') {
[821]365 def = 1;
366 break;
[2725]367 }
368 if (c == 'n') {
[821]369 def = 0;
370 break;
[2725]371 }
[821]372 }
373 if (def)
[3621]374 puts("y");
[821]375 else {
[3621]376 puts("n");
[821]377 errors_uncorrected = 1;
378 }
379 return def;
380}
381
382/*
383 * Make certain that we aren't checking a filesystem that is on a
384 * mounted partition. Code adapted from e2fsck, Copyright (C) 1993,
385 * 1994 Theodore Ts'o. Also licensed under GPL.
386 */
387static void check_mount(void)
388{
[2725]389 if (find_mount_point(device_name, 0)) {
390 int cont;
391#if ENABLE_FEATURE_MTAB_SUPPORT
392 /*
393 * If the root is mounted read-only, then /etc/mtab is
394 * probably not correct; so we won't issue a warning based on
395 * it.
396 */
397 int fd = open(bb_path_mtab_file, O_RDWR);
[821]398
[2725]399 if (fd < 0 && errno == EROFS)
400 return;
401 close(fd);
402#endif
403 printf("%s is mounted. ", device_name);
404 cont = 0;
405 if (isatty(0) && isatty(1))
406 cont = ask("Do you really want to continue", 0);
407 if (!cont) {
[3621]408 puts("Check aborted");
[2725]409 exit(EXIT_SUCCESS);
410 }
[821]411 }
412}
413
414/*
415 * check_zone_nr checks to see that *nr is a valid zone nr. If it
416 * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
417 * if an error was corrected, and returns the zone (0 for no zone
418 * or a bad zone-number).
419 */
[1765]420static int check_zone_nr2(uint32_t *nr, smallint *corrected)
[821]421{
422 const char *msg;
423 if (!*nr)
424 return 0;
425 if (*nr < FIRSTZONE)
426 msg = "< FIRSTZONE";
427 else if (*nr >= ZONES)
428 msg = ">= ZONES";
429 else
430 return *nr;
431 printf("Zone nr %s in file '%s'. ", msg, current_name);
432 if (ask("Remove block", 1)) {
433 *nr = 0;
434 *corrected = 1;
435 }
436 return 0;
437}
438
[1765]439static int check_zone_nr(uint16_t *nr, smallint *corrected)
[821]440{
441 uint32_t nr32 = *nr;
442 int r = check_zone_nr2(&nr32, corrected);
443 *nr = (uint16_t)nr32;
444 return r;
445}
446
447/*
448 * read-block reads block nr into the buffer at addr.
449 */
[2725]450static void read_block(unsigned nr, void *addr)
[821]451{
452 if (!nr) {
453 memset(addr, 0, BLOCK_SIZE);
454 return;
455 }
[2725]456 xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
457 if (BLOCK_SIZE != full_read(dev_fd, addr, BLOCK_SIZE)) {
458 printf("%s: bad block %u in file '%s'\n",
459 bb_msg_read_error, nr, current_name);
[821]460 errors_uncorrected = 1;
461 memset(addr, 0, BLOCK_SIZE);
462 }
463}
464
465/*
466 * write_block writes block nr to disk.
467 */
[2725]468static void write_block(unsigned nr, void *addr)
[821]469{
470 if (!nr)
471 return;
472 if (nr < FIRSTZONE || nr >= ZONES) {
[3621]473 puts("Internal error: trying to write bad block\n"
474 "Write request ignored");
[821]475 errors_uncorrected = 1;
476 return;
477 }
[2725]478 xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
479 if (BLOCK_SIZE != full_write(dev_fd, addr, BLOCK_SIZE)) {
480 printf("%s: bad block %u in file '%s'\n",
481 bb_msg_write_error, nr, current_name);
[821]482 errors_uncorrected = 1;
483 }
484}
485
486/*
487 * map_block calculates the absolute block nr of a block in a file.
488 * It sets 'changed' if the inode has needed changing, and re-writes
489 * any indirect blocks with errors.
490 */
[1765]491static int map_block(struct minix1_inode *inode, unsigned blknr)
[821]492{
493 uint16_t ind[BLOCK_SIZE >> 1];
[1765]494 int block, result;
495 smallint blk_chg;
[821]496
497 if (blknr < 7)
498 return check_zone_nr(inode->i_zone + blknr, &changed);
499 blknr -= 7;
500 if (blknr < 512) {
501 block = check_zone_nr(inode->i_zone + 7, &changed);
[2725]502 goto common;
[821]503 }
504 blknr -= 512;
505 block = check_zone_nr(inode->i_zone + 8, &changed);
[2725]506 read_block(block, ind); /* double indirect */
[821]507 blk_chg = 0;
[2725]508 result = check_zone_nr(&ind[blknr / 512], &blk_chg);
[821]509 if (blk_chg)
[2725]510 write_block(block, ind);
[821]511 block = result;
[2725]512 common:
513 read_block(block, ind);
[821]514 blk_chg = 0;
[2725]515 result = check_zone_nr(&ind[blknr % 512], &blk_chg);
[821]516 if (blk_chg)
[2725]517 write_block(block, ind);
[821]518 return result;
519}
520
[1765]521#if ENABLE_FEATURE_MINIX2
522static int map_block2(struct minix2_inode *inode, unsigned blknr)
[821]523{
524 uint32_t ind[BLOCK_SIZE >> 2];
[1765]525 int block, result;
526 smallint blk_chg;
[821]527
528 if (blknr < 7)
529 return check_zone_nr2(inode->i_zone + blknr, &changed);
530 blknr -= 7;
531 if (blknr < 256) {
532 block = check_zone_nr2(inode->i_zone + 7, &changed);
[2725]533 goto common2;
[821]534 }
535 blknr -= 256;
[2725]536 if (blknr < 256 * 256) {
[821]537 block = check_zone_nr2(inode->i_zone + 8, &changed);
[2725]538 goto common1;
[821]539 }
540 blknr -= 256 * 256;
541 block = check_zone_nr2(inode->i_zone + 9, &changed);
[2725]542 read_block(block, ind); /* triple indirect */
[821]543 blk_chg = 0;
[2725]544 result = check_zone_nr2(&ind[blknr / (256 * 256)], &blk_chg);
[821]545 if (blk_chg)
[2725]546 write_block(block, ind);
[821]547 block = result;
[2725]548 common1:
549 read_block(block, ind); /* double indirect */
[821]550 blk_chg = 0;
[2725]551 result = check_zone_nr2(&ind[(blknr / 256) % 256], &blk_chg);
[821]552 if (blk_chg)
[2725]553 write_block(block, ind);
[821]554 block = result;
[2725]555 common2:
556 read_block(block, ind);
[821]557 blk_chg = 0;
[2725]558 result = check_zone_nr2(&ind[blknr % 256], &blk_chg);
[821]559 if (blk_chg)
[2725]560 write_block(block, ind);
[821]561 return result;
562}
563#endif
564
[2725]565static void write_superblock(void)
[821]566{
567 /*
568 * Set the state of the filesystem based on whether or not there
569 * are uncorrected errors. The filesystem valid flag is
570 * unconditionally set if we get this far.
571 */
[1765]572 Super.s_state |= MINIX_VALID_FS | MINIX_ERROR_FS;
573 if (!errors_uncorrected)
[821]574 Super.s_state &= ~MINIX_ERROR_FS;
575
[2725]576 xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
577 if (BLOCK_SIZE != full_write(dev_fd, superblock_buffer, BLOCK_SIZE))
578 die("can't write superblock");
[821]579}
580
581static void write_tables(void)
582{
[2725]583 write_superblock();
[821]584
[1765]585 if (IMAPS * BLOCK_SIZE != write(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
[2725]586 die("can't write inode map");
[1765]587 if (ZMAPS * BLOCK_SIZE != write(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
[2725]588 die("can't write zone map");
[1765]589 if (INODE_BUFFER_SIZE != write(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
[2725]590 die("can't write inodes");
[821]591}
592
593static void get_dirsize(void)
594{
595 int block;
596 char blk[BLOCK_SIZE];
597 int size;
598
[1765]599#if ENABLE_FEATURE_MINIX2
[821]600 if (version2)
[1765]601 block = Inode2[MINIX_ROOT_INO].i_zone[0];
[821]602 else
603#endif
[1765]604 block = Inode1[MINIX_ROOT_INO].i_zone[0];
[821]605 read_block(block, blk);
606 for (size = 16; size < BLOCK_SIZE; size <<= 1) {
607 if (strcmp(blk + size + 2, "..") == 0) {
608 dirsize = size;
609 namelen = size - 2;
610 return;
611 }
612 }
613 /* use defaults */
614}
615
616static void read_superblock(void)
617{
[2725]618 xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
619 if (BLOCK_SIZE != full_read(dev_fd, superblock_buffer, BLOCK_SIZE))
620 die("can't read superblock");
[821]621 /* already initialized to:
622 namelen = 14;
623 dirsize = 16;
624 version2 = 0;
625 */
[1765]626 if (MAGIC == MINIX1_SUPER_MAGIC) {
627 } else if (MAGIC == MINIX1_SUPER_MAGIC2) {
[821]628 namelen = 30;
629 dirsize = 32;
[1765]630#if ENABLE_FEATURE_MINIX2
[821]631 } else if (MAGIC == MINIX2_SUPER_MAGIC) {
632 version2 = 1;
633 } else if (MAGIC == MINIX2_SUPER_MAGIC2) {
634 namelen = 30;
635 dirsize = 32;
636 version2 = 1;
637#endif
638 } else
[2725]639 die("bad magic number in superblock");
[821]640 if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
[1765]641 die("only 1k blocks/zones supported");
[821]642 if (IMAPS * BLOCK_SIZE * 8 < INODES + 1)
[2725]643 die("bad s_imap_blocks field in superblock");
[821]644 if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1)
[2725]645 die("bad s_zmap_blocks field in superblock");
[821]646}
647
648static void read_tables(void)
649{
650 inode_map = xzalloc(IMAPS * BLOCK_SIZE);
651 zone_map = xzalloc(ZMAPS * BLOCK_SIZE);
652 inode_buffer = xmalloc(INODE_BUFFER_SIZE);
653 inode_count = xmalloc(INODES + 1);
654 zone_count = xmalloc(ZONES);
[1765]655 if (IMAPS * BLOCK_SIZE != read(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
[2725]656 die("can't read inode map");
[1765]657 if (ZMAPS * BLOCK_SIZE != read(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
[2725]658 die("can't read zone map");
[1765]659 if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
[2725]660 die("can't read inodes");
[821]661 if (NORM_FIRSTZONE != FIRSTZONE) {
[3621]662 puts("warning: firstzone!=norm_firstzone");
[821]663 errors_uncorrected = 1;
664 }
665 get_dirsize();
[2725]666 if (OPT_show) {
[1765]667 printf("%u inodes\n"
668 "%u blocks\n"
669 "Firstdatazone=%u (%u)\n"
670 "Zonesize=%u\n"
671 "Maxsize=%u\n"
672 "Filesystem state=%u\n"
673 "namelen=%u\n\n",
[821]674 INODES,
675 ZONES,
676 FIRSTZONE, NORM_FIRSTZONE,
677 BLOCK_SIZE << ZONESIZE,
678 MAXSIZE,
679 Super.s_state,
680 namelen);
681 }
682}
683
[2725]684static void get_inode_common(unsigned nr, uint16_t i_mode)
[821]685{
686 total++;
687 if (!inode_count[nr]) {
688 if (!inode_in_use(nr)) {
[3621]689 printf("Inode %u is marked as 'unused', but it is used "
[821]690 "for file '%s'\n", nr, current_name);
[2725]691 if (OPT_repair) {
[821]692 if (ask("Mark as 'in use'", 1))
693 mark_inode(nr);
[1765]694 else
695 errors_uncorrected = 1;
[821]696 }
697 }
[2725]698 if (S_ISDIR(i_mode))
[821]699 directory++;
[2725]700 else if (S_ISREG(i_mode))
[821]701 regular++;
[2725]702 else if (S_ISCHR(i_mode))
[821]703 chardev++;
[2725]704 else if (S_ISBLK(i_mode))
[821]705 blockdev++;
[2725]706 else if (S_ISLNK(i_mode))
[821]707 symlinks++;
[2725]708 else if (S_ISSOCK(i_mode));
709 else if (S_ISFIFO(i_mode));
[821]710 else {
[2725]711 printf("%s has mode %05o\n", current_name, i_mode);
[821]712 }
713 } else
714 links++;
715 if (!++inode_count[nr]) {
[3621]716 puts("Warning: inode count too big");
[821]717 inode_count[nr]--;
718 errors_uncorrected = 1;
719 }
[2725]720}
721
722static struct minix1_inode *get_inode(unsigned nr)
723{
724 struct minix1_inode *inode;
725
726 if (!nr || nr > INODES)
727 return NULL;
728 inode = Inode1 + nr;
729 get_inode_common(nr, inode->i_mode);
[821]730 return inode;
731}
732
[1765]733#if ENABLE_FEATURE_MINIX2
734static struct minix2_inode *get_inode2(unsigned nr)
[821]735{
736 struct minix2_inode *inode;
737
738 if (!nr || nr > INODES)
739 return NULL;
740 inode = Inode2 + nr;
[2725]741 get_inode_common(nr, inode->i_mode);
[821]742 return inode;
743}
744#endif
745
746static void check_root(void)
747{
[1765]748 struct minix1_inode *inode = Inode1 + MINIX_ROOT_INO;
[821]749
750 if (!inode || !S_ISDIR(inode->i_mode))
[1765]751 die("root inode isn't a directory");
[821]752}
753
[1765]754#if ENABLE_FEATURE_MINIX2
[821]755static void check_root2(void)
756{
[1765]757 struct minix2_inode *inode = Inode2 + MINIX_ROOT_INO;
[821]758
759 if (!inode || !S_ISDIR(inode->i_mode))
[1765]760 die("root inode isn't a directory");
[821]761}
[1765]762#else
763void check_root2(void);
[821]764#endif
765
[2725]766static int add_zone_common(int block, smallint *corrected)
[821]767{
768 if (!block)
769 return 0;
770 if (zone_count[block]) {
771 printf("Already used block is reused in file '%s'. ",
772 current_name);
773 if (ask("Clear", 1)) {
774 block = 0;
775 *corrected = 1;
[2725]776 return -1; /* "please zero out *znr" */
[821]777 }
778 }
779 if (!zone_in_use(block)) {
780 printf("Block %d in file '%s' is marked as 'unused'. ",
781 block, current_name);
782 if (ask("Correct", 1))
783 mark_zone(block);
784 }
785 if (!++zone_count[block])
786 zone_count[block]--;
787 return block;
788}
789
[2725]790static int add_zone(uint16_t *znr, smallint *corrected)
791{
792 int block;
793
794 block = check_zone_nr(znr, corrected);
795 block = add_zone_common(block, corrected);
796 if (block == -1) {
797 *znr = 0;
798 block = 0;
799 }
800 return block;
801}
802
[1765]803#if ENABLE_FEATURE_MINIX2
804static int add_zone2(uint32_t *znr, smallint *corrected)
[821]805{
806 int block;
807
808 block = check_zone_nr2(znr, corrected);
[2725]809 block = add_zone_common(block, corrected);
810 if (block == -1) {
811 *znr = 0;
812 block = 0;
[821]813 }
814 return block;
815}
816#endif
817
[1765]818static void add_zone_ind(uint16_t *znr, smallint *corrected)
[821]819{
[1765]820 int i;
[821]821 int block;
[1765]822 smallint chg_blk = 0;
[821]823
824 block = add_zone(znr, corrected);
825 if (!block)
826 return;
[1765]827 read_block(block, add_zone_ind_blk);
[821]828 for (i = 0; i < (BLOCK_SIZE >> 1); i++)
[1765]829 add_zone(i + (uint16_t *) add_zone_ind_blk, &chg_blk);
[821]830 if (chg_blk)
[1765]831 write_block(block, add_zone_ind_blk);
[821]832}
833
[1765]834#if ENABLE_FEATURE_MINIX2
835static void add_zone_ind2(uint32_t *znr, smallint *corrected)
[821]836{
[1765]837 int i;
[821]838 int block;
[1765]839 smallint chg_blk = 0;
[821]840
841 block = add_zone2(znr, corrected);
842 if (!block)
843 return;
[1765]844 read_block(block, add_zone_ind_blk);
[821]845 for (i = 0; i < BLOCK_SIZE >> 2; i++)
[1765]846 add_zone2(i + (uint32_t *) add_zone_ind_blk, &chg_blk);
[821]847 if (chg_blk)
[1765]848 write_block(block, add_zone_ind_blk);
[821]849}
850#endif
851
[1765]852static void add_zone_dind(uint16_t *znr, smallint *corrected)
[821]853{
[1765]854 int i;
[821]855 int block;
[1765]856 smallint chg_blk = 0;
[821]857
858 block = add_zone(znr, corrected);
859 if (!block)
860 return;
[1765]861 read_block(block, add_zone_dind_blk);
[821]862 for (i = 0; i < (BLOCK_SIZE >> 1); i++)
[1765]863 add_zone_ind(i + (uint16_t *) add_zone_dind_blk, &chg_blk);
864 if (chg_blk)
865 write_block(block, add_zone_dind_blk);
[821]866}
867
[1765]868#if ENABLE_FEATURE_MINIX2
869static void add_zone_dind2(uint32_t *znr, smallint *corrected)
[821]870{
[1765]871 int i;
[821]872 int block;
[1765]873 smallint chg_blk = 0;
[821]874
875 block = add_zone2(znr, corrected);
876 if (!block)
877 return;
[1765]878 read_block(block, add_zone_dind_blk);
[821]879 for (i = 0; i < BLOCK_SIZE >> 2; i++)
[1765]880 add_zone_ind2(i + (uint32_t *) add_zone_dind_blk, &chg_blk);
881 if (chg_blk)
882 write_block(block, add_zone_dind_blk);
[821]883}
884
[1765]885static void add_zone_tind2(uint32_t *znr, smallint *corrected)
[821]886{
[1765]887 int i;
[821]888 int block;
[1765]889 smallint chg_blk = 0;
[821]890
891 block = add_zone2(znr, corrected);
892 if (!block)
893 return;
[1765]894 read_block(block, add_zone_tind_blk);
[821]895 for (i = 0; i < BLOCK_SIZE >> 2; i++)
[1765]896 add_zone_dind2(i + (uint32_t *) add_zone_tind_blk, &chg_blk);
897 if (chg_blk)
898 write_block(block, add_zone_tind_blk);
[821]899}
900#endif
901
[1765]902static void check_zones(unsigned i)
[821]903{
[1765]904 struct minix1_inode *inode;
[821]905
906 if (!i || i > INODES)
907 return;
908 if (inode_count[i] > 1) /* have we counted this file already? */
909 return;
[1765]910 inode = Inode1 + i;
[2725]911 if (!S_ISDIR(inode->i_mode)
912 && !S_ISREG(inode->i_mode)
913 && !S_ISLNK(inode->i_mode)
914 ) {
915 return;
916 }
[821]917 for (i = 0; i < 7; i++)
918 add_zone(i + inode->i_zone, &changed);
919 add_zone_ind(7 + inode->i_zone, &changed);
920 add_zone_dind(8 + inode->i_zone, &changed);
921}
922
[1765]923#if ENABLE_FEATURE_MINIX2
924static void check_zones2(unsigned i)
[821]925{
926 struct minix2_inode *inode;
927
928 if (!i || i > INODES)
929 return;
930 if (inode_count[i] > 1) /* have we counted this file already? */
931 return;
932 inode = Inode2 + i;
933 if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
934 && !S_ISLNK(inode->i_mode))
935 return;
936 for (i = 0; i < 7; i++)
937 add_zone2(i + inode->i_zone, &changed);
938 add_zone_ind2(7 + inode->i_zone, &changed);
939 add_zone_dind2(8 + inode->i_zone, &changed);
940 add_zone_tind2(9 + inode->i_zone, &changed);
941}
942#endif
943
[1765]944static void check_file(struct minix1_inode *dir, unsigned offset)
[821]945{
[1765]946 struct minix1_inode *inode;
[821]947 int ino;
948 char *name;
949 int block;
950
951 block = map_block(dir, offset / BLOCK_SIZE);
[1765]952 read_block(block, check_file_blk);
953 name = check_file_blk + (offset % BLOCK_SIZE) + 2;
[821]954 ino = *(uint16_t *) (name - 2);
955 if (ino > INODES) {
956 printf("%s contains a bad inode number for file '%.*s'. ",
957 current_name, namelen, name);
958 if (ask("Remove", 1)) {
959 *(uint16_t *) (name - 2) = 0;
[1765]960 write_block(block, check_file_blk);
[821]961 }
962 ino = 0;
963 }
964 push_filename(name);
965 inode = get_inode(ino);
966 pop_filename();
967 if (!offset) {
[1765]968 if (inode && LONE_CHAR(name, '.'))
[821]969 return;
[1765]970 printf("%s: bad directory: '.' isn't first\n", current_name);
971 errors_uncorrected = 1;
[821]972 }
973 if (offset == dirsize) {
[1765]974 if (inode && strcmp("..", name) == 0)
[821]975 return;
[1765]976 printf("%s: bad directory: '..' isn't second\n", current_name);
977 errors_uncorrected = 1;
[821]978 }
979 if (!inode)
980 return;
981 push_filename(name);
[2725]982 if (OPT_list) {
983 if (OPT_verbose)
[821]984 printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
985 printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
986 }
987 check_zones(ino);
988 if (inode && S_ISDIR(inode->i_mode))
989 recursive_check(ino);
990 pop_filename();
991}
992
[1765]993#if ENABLE_FEATURE_MINIX2
994static void check_file2(struct minix2_inode *dir, unsigned offset)
[821]995{
996 struct minix2_inode *inode;
997 int ino;
998 char *name;
999 int block;
1000
1001 block = map_block2(dir, offset / BLOCK_SIZE);
[1765]1002 read_block(block, check_file_blk);
1003 name = check_file_blk + (offset % BLOCK_SIZE) + 2;
[821]1004 ino = *(uint16_t *) (name - 2);
1005 if (ino > INODES) {
1006 printf("%s contains a bad inode number for file '%.*s'. ",
1007 current_name, namelen, name);
1008 if (ask("Remove", 1)) {
1009 *(uint16_t *) (name - 2) = 0;
[1765]1010 write_block(block, check_file_blk);
[821]1011 }
1012 ino = 0;
1013 }
1014 push_filename(name);
1015 inode = get_inode2(ino);
1016 pop_filename();
1017 if (!offset) {
[1765]1018 if (inode && LONE_CHAR(name, '.'))
[821]1019 return;
[1765]1020 printf("%s: bad directory: '.' isn't first\n", current_name);
1021 errors_uncorrected = 1;
[821]1022 }
1023 if (offset == dirsize) {
[1765]1024 if (inode && strcmp("..", name) == 0)
[821]1025 return;
[1765]1026 printf("%s: bad directory: '..' isn't second\n", current_name);
1027 errors_uncorrected = 1;
[821]1028 }
1029 if (!inode)
1030 return;
1031 push_filename(name);
[2725]1032 if (OPT_list) {
1033 if (OPT_verbose)
[821]1034 printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
1035 printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
1036 }
1037 check_zones2(ino);
1038 if (inode && S_ISDIR(inode->i_mode))
1039 recursive_check2(ino);
1040 pop_filename();
1041}
1042#endif
1043
[1765]1044static void recursive_check(unsigned ino)
[821]1045{
[1765]1046 struct minix1_inode *dir;
1047 unsigned offset;
[821]1048
[1765]1049 dir = Inode1 + ino;
[821]1050 if (!S_ISDIR(dir->i_mode))
[1765]1051 die("internal error");
[821]1052 if (dir->i_size < 2 * dirsize) {
1053 printf("%s: bad directory: size<32", current_name);
1054 errors_uncorrected = 1;
1055 }
1056 for (offset = 0; offset < dir->i_size; offset += dirsize)
1057 check_file(dir, offset);
1058}
1059
[1765]1060#if ENABLE_FEATURE_MINIX2
1061static void recursive_check2(unsigned ino)
[821]1062{
1063 struct minix2_inode *dir;
[1765]1064 unsigned offset;
[821]1065
1066 dir = Inode2 + ino;
1067 if (!S_ISDIR(dir->i_mode))
[1765]1068 die("internal error");
[821]1069 if (dir->i_size < 2 * dirsize) {
1070 printf("%s: bad directory: size<32", current_name);
1071 errors_uncorrected = 1;
1072 }
1073 for (offset = 0; offset < dir->i_size; offset += dirsize)
1074 check_file2(dir, offset);
1075}
1076#endif
1077
1078static int bad_zone(int i)
1079{
[1765]1080 char buffer[BLOCK_SIZE];
[821]1081
[2725]1082 xlseek(dev_fd, BLOCK_SIZE * i, SEEK_SET);
1083 return (BLOCK_SIZE != full_read(dev_fd, buffer, BLOCK_SIZE));
[821]1084}
1085
1086static void check_counts(void)
1087{
1088 int i;
1089
1090 for (i = 1; i <= INODES; i++) {
[2725]1091 if (OPT_warn_mode && Inode1[i].i_mode && !inode_in_use(i)) {
[821]1092 printf("Inode %d has non-zero mode. ", i);
1093 if (ask("Clear", 1)) {
[1765]1094 Inode1[i].i_mode = 0;
[821]1095 changed = 1;
1096 }
1097 }
1098 if (!inode_count[i]) {
1099 if (!inode_in_use(i))
1100 continue;
1101 printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
1102 if (ask("Clear", 1))
1103 unmark_inode(i);
1104 continue;
1105 }
1106 if (!inode_in_use(i)) {
1107 printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
1108 if (ask("Set", 1))
1109 mark_inode(i);
1110 }
[1765]1111 if (Inode1[i].i_nlinks != inode_count[i]) {
[821]1112 printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
[1765]1113 i, Inode1[i].i_mode, Inode1[i].i_nlinks,
1114 inode_count[i]);
[821]1115 if (ask("Set i_nlinks to count", 1)) {
[1765]1116 Inode1[i].i_nlinks = inode_count[i];
[821]1117 changed = 1;
1118 }
1119 }
1120 }
1121 for (i = FIRSTZONE; i < ZONES; i++) {
[1765]1122 if ((zone_in_use(i) != 0) == zone_count[i])
[821]1123 continue;
1124 if (!zone_count[i]) {
1125 if (bad_zone(i))
1126 continue;
1127 printf("Zone %d is marked 'in use', but no file uses it. ", i);
1128 if (ask("Unmark", 1))
1129 unmark_zone(i);
1130 continue;
1131 }
1132 printf("Zone %d: %sin use, counted=%d\n",
[3232]1133 i, zone_in_use(i) ? "" : "not ", zone_count[i]);
[821]1134 }
1135}
1136
[1765]1137#if ENABLE_FEATURE_MINIX2
[821]1138static void check_counts2(void)
1139{
1140 int i;
1141
1142 for (i = 1; i <= INODES; i++) {
[2725]1143 if (OPT_warn_mode && Inode2[i].i_mode && !inode_in_use(i)) {
[821]1144 printf("Inode %d has non-zero mode. ", i);
1145 if (ask("Clear", 1)) {
1146 Inode2[i].i_mode = 0;
1147 changed = 1;
1148 }
1149 }
1150 if (!inode_count[i]) {
1151 if (!inode_in_use(i))
1152 continue;
1153 printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
1154 if (ask("Clear", 1))
1155 unmark_inode(i);
1156 continue;
1157 }
1158 if (!inode_in_use(i)) {
1159 printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
1160 if (ask("Set", 1))
1161 mark_inode(i);
1162 }
1163 if (Inode2[i].i_nlinks != inode_count[i]) {
1164 printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
[1765]1165 i, Inode2[i].i_mode, Inode2[i].i_nlinks,
1166 inode_count[i]);
[821]1167 if (ask("Set i_nlinks to count", 1)) {
1168 Inode2[i].i_nlinks = inode_count[i];
1169 changed = 1;
1170 }
1171 }
1172 }
1173 for (i = FIRSTZONE; i < ZONES; i++) {
[1765]1174 if ((zone_in_use(i) != 0) == zone_count[i])
[821]1175 continue;
1176 if (!zone_count[i]) {
1177 if (bad_zone(i))
1178 continue;
1179 printf("Zone %d is marked 'in use', but no file uses it. ", i);
1180 if (ask("Unmark", 1))
1181 unmark_zone(i);
1182 continue;
1183 }
1184 printf("Zone %d: %sin use, counted=%d\n",
[3232]1185 i, zone_in_use(i) ? "" : "not ", zone_count[i]);
[821]1186 }
1187}
1188#endif
1189
1190static void check(void)
1191{
1192 memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
1193 memset(zone_count, 0, ZONES * sizeof(*zone_count));
[1765]1194 check_zones(MINIX_ROOT_INO);
1195 recursive_check(MINIX_ROOT_INO);
[821]1196 check_counts();
1197}
1198
[1765]1199#if ENABLE_FEATURE_MINIX2
[821]1200static void check2(void)
1201{
1202 memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
1203 memset(zone_count, 0, ZONES * sizeof(*zone_count));
[1765]1204 check_zones2(MINIX_ROOT_INO);
1205 recursive_check2(MINIX_ROOT_INO);
[821]1206 check_counts2();
1207}
[1765]1208#else
1209void check2(void);
[821]1210#endif
1211
[2725]1212int fsck_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1213int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
[821]1214{
1215 struct termios tmp;
1216 int retcode = 0;
1217
[1765]1218 xfunc_error_retval = 8;
[821]1219
[1765]1220 INIT_G();
1221
[2725]1222 opt_complementary = "=1:ar"; /* one argument; -a assumes -r */
1223 getopt32(argv, OPTION_STR);
1224 argv += optind;
1225 device_name = argv[0];
[1765]1226
[2725]1227 check_mount(); /* trying to check a mounted filesystem? */
1228 if (OPT_manual) {
[821]1229 if (!isatty(0) || !isatty(1))
[1765]1230 die("need terminal for interactive repairs");
[821]1231 }
[2725]1232 xmove_fd(xopen(device_name, OPT_repair ? O_RDWR : O_RDONLY), dev_fd);
[1765]1233
1234 /*sync(); paranoia? */
[821]1235 read_superblock();
1236
1237 /*
1238 * Determine whether or not we should continue with the checking.
1239 * This is based on the status of the filesystem valid and error
1240 * flags and whether or not the -f switch was specified on the
1241 * command line.
1242 */
[1765]1243 printf("%s: %s\n", applet_name, bb_banner);
1244
1245 if (!(Super.s_state & MINIX_ERROR_FS)
[2725]1246 && (Super.s_state & MINIX_VALID_FS) && !OPT_force
[1765]1247 ) {
[2725]1248 if (OPT_repair)
[821]1249 printf("%s is clean, check is skipped\n", device_name);
[1765]1250 return 0;
[2725]1251 } else if (OPT_force)
[821]1252 printf("Forcing filesystem check on %s\n", device_name);
[2725]1253 else if (OPT_repair)
[821]1254 printf("Filesystem on %s is dirty, needs checking\n",
[3232]1255 device_name);
[821]1256
1257 read_tables();
1258
[2725]1259 if (OPT_manual) {
[1765]1260 tcgetattr(0, &sv_termios);
1261 tmp = sv_termios;
[821]1262 tmp.c_lflag &= ~(ICANON | ECHO);
[2725]1263 tcsetattr_stdin_TCSANOW(&tmp);
[821]1264 termios_set = 1;
1265 }
[1765]1266
[821]1267 if (version2) {
1268 check_root2();
1269 check2();
[1765]1270 } else {
[821]1271 check_root();
1272 check();
1273 }
[1765]1274
[2725]1275 if (OPT_verbose) {
[821]1276 int i, free_cnt;
1277
1278 for (i = 1, free_cnt = 0; i <= INODES; i++)
1279 if (!inode_in_use(i))
1280 free_cnt++;
[1765]1281 printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt),
[3232]1282 100 * (INODES - free_cnt) / INODES);
[821]1283 for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++)
1284 if (!zone_in_use(i))
1285 free_cnt++;
[1765]1286 printf("%6u zones used (%u%%)\n\n"
[3232]1287 "%6u regular files\n"
1288 "%6u directories\n"
1289 "%6u character device files\n"
1290 "%6u block device files\n"
1291 "%6u links\n"
1292 "%6u symbolic links\n"
1293 "------\n"
1294 "%6u files\n",
1295 (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES,
1296 regular, directory, chardev, blockdev,
1297 links - 2 * directory + 1, symlinks,
1298 total - 2 * directory + 1);
[821]1299 }
1300 if (changed) {
1301 write_tables();
[3621]1302 puts("FILE SYSTEM HAS BEEN CHANGED");
[821]1303 sync();
[2725]1304 } else if (OPT_repair)
1305 write_superblock();
[821]1306
[2725]1307 if (OPT_manual)
1308 tcsetattr_stdin_TCSANOW(&sv_termios);
[821]1309
1310 if (changed)
1311 retcode += 3;
1312 if (errors_uncorrected)
1313 retcode += 4;
1314 return retcode;
1315}
Note: See TracBrowser for help on using the repository browser.