1 /* Hacks for version 1.6 */
3 #define INODES_PER_BLOCK V2_INODES_PER_BLOCK(block_size)
4 #define INODE_SIZE ((int) V2_INODE_SIZE)
5 #define WORDS_PER_BLOCK (block_size / (int) sizeof(bitchunk_t))
6 #define MAX_ZONES (V2_NR_DZONES+V2_INDIRECTS(block_size)+(long)V2_INDIRECTS(block_size)*V2_INDIRECTS(block_size))
7 #define NR_DZONE_NUM V2_NR_DZONES
8 #define NR_INDIRECTS V2_INDIRECTS(block_size)
9 #define NR_ZONE_NUMS V2_NR_TZONES
10 #define ZONE_NUM_SIZE V2_ZONE_NUM_SIZE
12 #define block_nr block_t
13 #define d_inode d2_inode
15 #define dir_struct struct direct
16 #define i_mode d2_mode
17 #define i_nlinks d2_nlinks
18 #define i_size d2_size
19 #define i_zone d2_zone
20 #define zone_nr zone_t
22 /* fsck - file system checker Author: Robbert van Renesse */
24 /* Modified by Norbert Schlenker
25 * Removed vestiges of standalone/DOS versions:
26 * - various unused variables and buffers removed
27 * - now uses library functions rather than private internal routines
28 * - bytewise structure copies replaced by structure assignment
29 * - fixed one bug with 14 character file names
30 * - other small tweaks for speed
32 * Modified by Lars Fredriksen at the request of Andy Tanenbaum, 90-03-10.
33 * Removed -m option, by which fsck could be told to make a file
34 * system on a 360K floppy. The code had limited utility, was buggy,
35 * and failed due to a bug in the ACK C compiler. Use mkfs instead!
38 #include <sys/types.h>
47 #include <minix/config.h>
48 #include <minix/const.h>
49 #include <minix/type.h>
50 #include <minix/u64.h>
51 #include "../../servers/mfs/const.h"
52 #include "../../servers/mfs/inode.h"
53 #include "../../servers/mfs/type.h"
54 #include <minix/fslib.h>
63 unsigned int fs_version
= 2, block_size
= 0;
65 #define BITSHIFT 4 /* = log2(#bits(int)) */
67 #define MAXPRINT 80 /* max. number of error lines in chkmap */
68 #define CINDIR 128 /* number of indirect zno's read at a time */
69 #define CDIRECT 1 /* number of dir entries read at a time */
71 /* Macros for handling bitmaps. Now bit_t is long, these are bulky and the
72 * type demotions produce a lot of lint. The explicit demotion in POWEROFBIT
73 * is for efficiency and assumes 2's complement ints. Lint should be clever
74 * enough not to warn about it since BITMASK is small, but isn't. (It would
75 * be easier to get right if bit_t was was unsigned (long) since then there
76 * would be no danger from wierd sign representations. Lint doesn't know
77 * we only use non-negative bit numbers.) There will usually be an implicit
78 * demotion when WORDOFBIT is used as an array index. This should be safe
79 * since memory for bitmaps will run out first.
81 #define BITMASK ((1 << BITSHIFT) - 1)
82 #define WORDOFBIT(b) ((b) >> BITSHIFT)
83 #define POWEROFBIT(b) (1 << ((int) (b) & BITMASK))
84 #define setbit(w, b) (w[WORDOFBIT(b)] |= POWEROFBIT(b))
85 #define clrbit(w, b) (w[WORDOFBIT(b)] &= ~POWEROFBIT(b))
86 #define bitset(w, b) (w[WORDOFBIT(b)] & POWEROFBIT(b))
88 #define ZONE_CT 360 /* default zones (when making file system) */
89 #define INODE_CT 95 /* default inodes (when making file system) */
91 #include "../../servers/mfs/super.h"
92 static struct super_block sb
;
94 #define STICKY_BIT 01000 /* not defined anywhere else */
96 /* Ztob gives the block address of a zone
97 * btoa64 gives the byte address of a block
99 #define ztob(z) ((block_nr) (z) << sb.s_log_zone_size)
100 #define btoa64(b) (mul64u(b, block_size))
101 #define SCALE ((int) ztob(1)) /* # blocks in a zone */
102 #define FIRST ((zone_nr) sb.s_firstdatazone) /* as the name says */
104 /* # blocks of each type */
105 #define N_IMAP (sb.s_imap_blocks)
106 #define N_ZMAP (sb.s_zmap_blocks)
107 #define N_ILIST ((sb.s_ninodes+INODES_PER_BLOCK-1) / INODES_PER_BLOCK)
108 #define N_DATA (sb.s_zones - FIRST)
110 /* Block address of each type */
111 #define OFFSET_SUPER_BLOCK SUPER_BLOCK_BYTES
113 #define BLK_ZMAP (BLK_IMAP + N_IMAP)
114 #define BLK_ILIST (BLK_ZMAP + N_ZMAP)
115 #define BLK_FIRST ztob(FIRST)
116 #define ZONE_SIZE ((int) ztob(block_size))
117 #define NLEVEL (NR_ZONE_NUMS - NR_DZONE_NUM + 1)
119 /* Byte address of a zone */
120 #define INDCHUNK ((int) (CINDIR * ZONE_NUM_SIZE))
121 #define DIRCHUNK ((int) (CDIRECT * DIR_ENTRY_SIZE))
123 char *prog
, *fsck_device
; /* program name (fsck), device name */
124 int firstcnterr
; /* is this the first inode ref cnt error? */
125 bitchunk_t
*imap
, *spec_imap
; /* inode bit maps */
126 bitchunk_t
*zmap
, *spec_zmap
; /* zone bit maps */
127 bitchunk_t
*dirmap
; /* directory (inode) bit map */
128 char *rwbuf
; /* one block buffer cache */
129 block_nr thisblk
; /* block in buffer cache */
130 char *nullbuf
; /* null buffer */
131 nlink_t
*count
; /* inode count */
132 int changed
; /* has the diskette been written to? */
135 struct stack
*st_next
;
139 int dev
; /* file descriptor of the device */
144 /* Counters for each type of inode/zone. */
145 int nfreeinode
, nregular
, ndirectory
, nblkspec
, ncharspec
, nbadinode
;
146 int npipe
, nsyml
, ztype
[NLEVEL
];
149 int repair
, automatic
, listing
, listsuper
; /* flags */
150 int firstlist
; /* has the listing header been printed? */
151 unsigned part_offset
; /* sector offset for this partition */
152 char answer
[] = "Answer questions with y or n. Then hit RETURN";
154 _PROTOTYPE(int main
, (int argc
, char **argv
));
155 _PROTOTYPE(void initvars
, (void));
156 _PROTOTYPE(void fatal
, (char *s
));
157 _PROTOTYPE(int eoln
, (int c
));
158 _PROTOTYPE(int yes
, (char *question
));
159 _PROTOTYPE(int atoo
, (char *s
));
160 _PROTOTYPE(int input
, (char *buf
, int size
));
161 _PROTOTYPE(char *alloc
, (unsigned nelem
, unsigned elsize
));
162 _PROTOTYPE(void printname
, (char *s
));
163 _PROTOTYPE(void printrec
, (struct stack
*sp
));
164 _PROTOTYPE(void printpath
, (int mode
, int nlcr
));
165 _PROTOTYPE(void devopen
, (void));
166 _PROTOTYPE(void devclose
, (void));
167 _PROTOTYPE(void devio
, (block_nr bno
, int dir
));
168 _PROTOTYPE(void devread
, (long block
, long offset
, char *buf
, int size
));
169 _PROTOTYPE(void devwrite
, (long block
, long offset
, char *buf
, int size
));
170 _PROTOTYPE(void pr
, (char *fmt
, int cnt
, char *s
, char *p
));
171 _PROTOTYPE(void lpr
, (char *fmt
, long cnt
, char *s
, char *p
));
172 _PROTOTYPE(bit_nr getnumber
, (char *s
));
173 _PROTOTYPE(char **getlist
, (char ***argv
, char *type
));
174 _PROTOTYPE(void lsuper
, (void));
175 _PROTOTYPE(void getsuper
, (void));
176 _PROTOTYPE(void chksuper
, (void));
177 _PROTOTYPE(void lsi
, (char **clist
));
178 _PROTOTYPE(bitchunk_t
*allocbitmap
, (int nblk
));
179 _PROTOTYPE(void loadbitmap
, (bitchunk_t
*bitmap
, block_nr bno
, int nblk
));
180 _PROTOTYPE(void dumpbitmap
, (bitchunk_t
*bitmap
, block_nr bno
, int nblk
));
181 _PROTOTYPE(void fillbitmap
, (bitchunk_t
*bitmap
, bit_nr lwb
, bit_nr upb
, char **list
));
182 _PROTOTYPE(void freebitmap
, (bitchunk_t
*p
));
183 _PROTOTYPE(void getbitmaps
, (void));
184 _PROTOTYPE(void putbitmaps
, (void));
185 _PROTOTYPE(void chkword
, (unsigned w1
, unsigned w2
, bit_nr bit
, char *type
, int *n
, int *report
, bit_t
));
186 _PROTOTYPE(void chkmap
, (bitchunk_t
*cmap
, bitchunk_t
*dmap
, bit_nr bit
, block_nr blkno
, int nblk
, char *type
));
187 _PROTOTYPE(void chkilist
, (void));
188 _PROTOTYPE(void getcount
, (void));
189 _PROTOTYPE(void counterror
, (ino_t ino
));
190 _PROTOTYPE(void chkcount
, (void));
191 _PROTOTYPE(void freecount
, (void));
192 _PROTOTYPE(void printperm
, (mode_t mode
, int shift
, int special
, int overlay
));
193 _PROTOTYPE(void list
, (ino_t ino
, d_inode
*ip
));
194 _PROTOTYPE(int Remove
, (dir_struct
*dp
));
195 _PROTOTYPE(void make_printable_name
, (char *dst
, char *src
, int n
));
196 _PROTOTYPE(int chkdots
, (ino_t ino
, off_t pos
, dir_struct
*dp
, ino_t exp
));
197 _PROTOTYPE(int chkname
, (ino_t ino
, dir_struct
*dp
));
198 _PROTOTYPE(int chkentry
, (ino_t ino
, off_t pos
, dir_struct
*dp
));
199 _PROTOTYPE(int chkdirzone
, (ino_t ino
, d_inode
*ip
, off_t pos
, zone_nr zno
));
200 _PROTOTYPE(int chksymlinkzone
, (ino_t ino
, d_inode
*ip
, off_t pos
,
202 _PROTOTYPE(void errzone
, (char *mess
, zone_nr zno
, int level
, off_t pos
));
203 _PROTOTYPE(int markzone
, (zone_nr zno
, int level
, off_t pos
));
204 _PROTOTYPE(int chkindzone
, (ino_t ino
, d_inode
*ip
, off_t
*pos
, zone_nr zno
, int level
));
205 _PROTOTYPE(off_t jump
, (int level
));
206 _PROTOTYPE(int zonechk
, (ino_t ino
, d_inode
*ip
, off_t
*pos
, zone_nr zno
, int level
));
207 _PROTOTYPE(int chkzones
, (ino_t ino
, d_inode
*ip
, off_t
*pos
, zone_nr
*zlist
, int len
, int level
));
208 _PROTOTYPE(int chkfile
, (ino_t ino
, d_inode
*ip
));
209 _PROTOTYPE(int chkdirectory
, (ino_t ino
, d_inode
*ip
));
210 _PROTOTYPE(int chklink
, (ino_t ino
, d_inode
*ip
));
211 _PROTOTYPE(int chkspecial
, (ino_t ino
, d_inode
*ip
));
212 _PROTOTYPE(int chkmode
, (ino_t ino
, d_inode
*ip
));
213 _PROTOTYPE(int chkinode
, (ino_t ino
, d_inode
*ip
));
214 _PROTOTYPE(int descendtree
, (dir_struct
*dp
));
215 _PROTOTYPE(void chktree
, (void));
216 _PROTOTYPE(void printtotal
, (void));
217 _PROTOTYPE(void chkdev
, (char *f
, char **clist
, char **ilist
, char **zlist
));
219 /* Initialize the variables used by this program. */
224 nregular
= ndirectory
= nblkspec
= ncharspec
= nbadinode
= npipe
= nsyml
= 0;
225 for (level
= 0; level
< NLEVEL
; level
++) ztype
[level
] = 0;
232 /* Print the string `s' and exit. */
236 printf("%s\nfatal\n", s
);
240 /* Test for end of line. */
244 return(c
== EOF
|| c
== '\n' || c
== '\r');
247 /* Ask a question and get the answer unless automatic is set. */
251 register int c
, answerchar
;
258 printf("%s? ", question
);
259 if(!note
) { printf("(y=yes, n=no, q=quit, A=for yes to all) "); note
= 1; }
265 if ((c
= answerchar
= getchar()) == 'q' || c
== 'Q') exit(1);
266 if(c
== 'A') { automatic
= 1; c
= 'y'; }
267 while (!eoln(c
)) c
= getchar();
268 return !(answerchar
== 'n' || answerchar
== 'N');
271 /* Convert string to integer. Representation is octal. */
277 while ('0' <= *s
&& *s
< '8') {
284 /* If repairing the file system, print a prompt and get a string from user. */
289 register char *p
= buf
;
304 while (!eoln(getchar()));
310 /* Allocate some memory and zero it. */
311 char *alloc(nelem
, elsize
)
312 unsigned nelem
, elsize
;
316 if ((p
= (char *)malloc((size_t)nelem
* elsize
)) == 0) {
317 fprintf(stderr
, "Tried to allocate %dkB\n",
319 fatal("out of memory");
321 memset((void *) p
, 0, (size_t)nelem
* elsize
);
325 /* Print the name in a directory entry. */
329 register n
= NAME_MAX
;
333 if ((c
= *s
) == 0) break;
334 if (!isprint(c
)) c
= '?';
340 /* Print the pathname given by a linked list pointed to by `sp'. The
341 * names are in reverse order.
343 void printrec(struct stack
*sp
)
345 if (sp
->st_next
!= 0) {
346 printrec(sp
->st_next
);
348 printname(sp
->st_dir
->d_name
);
352 /* Print the current pathname. */
353 void printpath(mode
, nlcr
)
357 if (ftop
->st_next
== 0)
363 printf(" (ino = %u, ", ftop
->st_dir
->d_inum
);
366 printf(" (ino = %u)", ftop
->st_dir
->d_inum
);
369 if (nlcr
) printf("\n");
372 /* Open the device. */
375 if ((dev
= open(fsck_device
, repair
? O_RDWR
: O_RDONLY
)) < 0) {
377 fatal("couldn't open device to fsck");
381 /* Close the device. */
384 if (close(dev
) != 0) {
390 /* Read or write a block. */
397 if(!block_size
) fatal("devio() with unknown block size");
398 if (dir
== READING
&& bno
== thisblk
) return;
402 printf("%s at block %5d\n", dir
== READING
? "reading " : "writing", bno
);
404 r
= lseek64(dev
, btoa64(bno
), SEEK_SET
, NULL
);
406 fatal("lseek64 failed");
407 if (dir
== READING
) {
408 if (read(dev
, rwbuf
, block_size
) == block_size
)
411 if (write(dev
, rwbuf
, block_size
) == block_size
)
415 printf("%s: can't %s block %ld (error = 0x%x)\n", prog
,
416 dir
== READING
? "read" : "write", (long) bno
, errno
);
417 if (dir
== READING
) {
418 printf("Continuing with a zero-filled block.\n");
419 memset(rwbuf
, 0, block_size
);
425 /* Read `size' bytes from the disk starting at block 'block' and
428 void devread(block
, offset
, buf
, size
)
434 if(!block_size
) fatal("devread() with unknown block size");
435 if (offset
>= block_size
)
437 block
+= offset
/block_size
;
438 offset
%= block_size
;
440 devio(block
, READING
);
441 memmove(buf
, &rwbuf
[offset
], size
);
444 /* Write `size' bytes to the disk starting at block 'block' and
447 void devwrite(block
, offset
, buf
, size
)
453 if(!block_size
) fatal("devwrite() with unknown block size");
454 if (!repair
) fatal("internal error (devwrite)");
455 if (offset
>= block_size
)
457 block
+= offset
/block_size
;
458 offset
%= block_size
;
460 if (size
!= block_size
) devio(block
, READING
);
461 memmove(&rwbuf
[offset
], buf
, size
);
462 devio(block
, WRITING
);
466 /* Print a string with either a singular or a plural pronoun. */
467 void pr(fmt
, cnt
, s
, p
)
471 printf(fmt
, cnt
, cnt
== 1 ? s
: p
);
474 /* Same as above, but with a long argument */
475 void lpr(fmt
, cnt
, s
, p
)
479 printf(fmt
, cnt
, cnt
== 1 ? s
: p
);
482 /* Convert string to number. */
486 register bit_nr n
= 0;
491 n
= (n
<< 1) + (n
<< 3) + *s
++ - '0';
492 return (*s
== '\0') ? n
: NO_BIT
;
495 /* See if the list pointed to by `argv' contains numbers. */
496 char **getlist(argv
, type
)
499 register char **list
= *argv
;
502 while (getnumber(**argv
) != NO_BIT
) {
507 printf("warning: no %s numbers given\n", type
);
513 /* Make a listing of the super block. If `repair' is set, ask the user
521 /* Most of the following atol's enrage lint, for good reason. */
522 printf("ninodes = %u", sb
.s_ninodes
);
523 if (input(buf
, 80)) sb
.s_ninodes
= atol(buf
);
524 printf("nzones = %ld", sb
.s_zones
);
525 if (input(buf
, 80)) sb
.s_zones
= atol(buf
);
526 printf("imap_blocks = %u", sb
.s_imap_blocks
);
527 if (input(buf
, 80)) sb
.s_imap_blocks
= atol(buf
);
528 printf("zmap_blocks = %u", sb
.s_zmap_blocks
);
529 if (input(buf
, 80)) sb
.s_zmap_blocks
= atol(buf
);
530 printf("firstdatazone = %u", sb
.s_firstdatazone_old
);
531 if (input(buf
, 80)) sb
.s_firstdatazone_old
= atol(buf
);
532 printf("log_zone_size = %u", sb
.s_log_zone_size
);
533 if (input(buf
, 80)) sb
.s_log_zone_size
= atol(buf
);
534 printf("maxsize = %ld", sb
.s_max_size
);
535 if (input(buf
, 80)) sb
.s_max_size
= atol(buf
);
536 printf("block size = %ld", sb
.s_block_size
);
537 if (input(buf
, 80)) sb
.s_block_size
= atol(buf
);
539 devwrite(0, OFFSET_SUPER_BLOCK
, (char *) &sb
, sizeof(sb
));
542 } while (yes("Do you want to try again"));
546 /* Get the super block from either disk or user. Do some initial checks. */
549 if(lseek(dev
, OFFSET_SUPER_BLOCK
, SEEK_SET
) < 0) {
551 fatal("couldn't seek to super block.");
553 if(read(dev
, &sb
, sizeof(sb
)) != sizeof(sb
)) {
554 fatal("couldn't read super block.");
556 if (listsuper
) lsuper();
557 if (sb
.s_magic
== SUPER_MAGIC
) fatal("Cannot handle V1 file systems");
558 if (sb
.s_magic
== SUPER_V2
) {
560 block_size
= /* STATIC_BLOCK_SIZE */ 8192;
561 } else if(sb
.s_magic
== SUPER_V3
) {
563 block_size
= sb
.s_block_size
;
565 fatal("bad magic number in super block");
567 if (sb
.s_ninodes
<= 0) fatal("no inodes");
568 if (sb
.s_zones
<= 0) fatal("no zones");
569 if (sb
.s_imap_blocks
<= 0) fatal("no imap");
570 if (sb
.s_zmap_blocks
<= 0) fatal("no zmap");
571 if (sb
.s_firstdatazone
!= 0 && sb
.s_firstdatazone
<= 4)
572 fatal("first data zone too small");
573 if (sb
.s_log_zone_size
< 0) fatal("zone size < block size");
574 if (sb
.s_max_size
<= 0) {
575 printf("warning: invalid max file size %ld\n", sb
.s_max_size
);
576 sb
.s_max_size
= LONG_MAX
;
580 /* Check the super block for reasonable contents. */
584 register off_t maxsize
;
586 n
= bitmapsize((bit_t
) sb
.s_ninodes
+ 1, block_size
);
587 if (sb
.s_magic
!= SUPER_V2
&& sb
.s_magic
!= SUPER_V3
)
588 fatal("bad magic number in super block");
589 if (sb
.s_imap_blocks
< n
) {
590 printf("need %d bocks for inode bitmap; only have %d\n",
591 n
, sb
.s_imap_blocks
);
592 fatal("too few imap blocks");
594 if (sb
.s_imap_blocks
!= n
) {
595 pr("warning: expected %d imap_block%s", n
, "", "s");
596 printf(" instead of %d\n", sb
.s_imap_blocks
);
598 n
= bitmapsize((bit_t
) sb
.s_zones
, block_size
);
599 if (sb
.s_zmap_blocks
< n
) fatal("too few zmap blocks");
600 if (sb
.s_zmap_blocks
!= n
) {
601 pr("warning: expected %d zmap_block%s", n
, "", "s");
602 printf(" instead of %d\n", sb
.s_zmap_blocks
);
604 if (sb
.s_log_zone_size
>= 8 * sizeof(block_nr
))
605 fatal("log_zone_size too large");
606 if (sb
.s_log_zone_size
> 8) printf("warning: large log_zone_size (%d)\n",
608 sb
.s_firstdatazone
= (BLK_ILIST
+ N_ILIST
+ SCALE
- 1) >> sb
.s_log_zone_size
;
609 if (sb
.s_firstdatazone_old
!= 0) {
610 if (sb
.s_firstdatazone_old
>= sb
.s_zones
)
611 fatal("first data zone too large");
612 if (sb
.s_firstdatazone_old
< sb
.s_firstdatazone
)
613 fatal("first data zone too small");
614 if (sb
.s_firstdatazone_old
!= sb
.s_firstdatazone
) {
615 printf("warning: expected first data zone to be %u ",
617 printf("instead of %u\n", sb
.s_firstdatazone_old
);
618 sb
.s_firstdatazone
= sb
.s_firstdatazone_old
;
621 maxsize
= MAX_FILE_POS
;
622 if (((maxsize
- 1) >> sb
.s_log_zone_size
) / block_size
>= MAX_ZONES
)
623 maxsize
= ((long) MAX_ZONES
* block_size
) << sb
.s_log_zone_size
;
626 if (sb
.s_max_size
!= maxsize
) {
627 printf("warning: expected max size to be %ld ", maxsize
);
628 printf("instead of %ld\n", sb
.s_max_size
);
632 int inoblock(int inn
)
634 return div64u(mul64u(inn
- 1, INODE_SIZE
), block_size
) + BLK_ILIST
;
639 return rem64u(mul64u(inn
- 1, INODE_SIZE
), block_size
);
642 /* Make a listing of the inodes given by `clist'. If `repair' is set, ask
643 * the user for changes.
650 d_inode inode
, *ip
= &inode
;
653 if (clist
== 0) return;
654 while ((bit
= getnumber(*clist
++)) != NO_BIT
) {
655 setbit(spec_imap
, bit
);
658 devread(inoblock(ino
), inooff(ino
), (char *) ip
, INODE_SIZE
);
659 printf("inode %u:\n", ino
);
660 printf(" mode = %6o", ip
->i_mode
);
661 if (input(buf
, 80)) ip
->i_mode
= atoo(buf
);
662 printf(" nlinks = %6u", ip
->i_nlinks
);
663 if (input(buf
, 80)) ip
->i_nlinks
= atol(buf
);
664 printf(" size = %6ld", ip
->i_size
);
665 if (input(buf
, 80)) ip
->i_size
= atol(buf
);
666 if (yes("Write this back")) {
667 devwrite(inoblock(ino
), inooff(ino
), (char *) ip
,
671 } while (yes("Do you want to change it again"));
675 /* Allocate `nblk' blocks worth of bitmap. */
676 bitchunk_t
*allocbitmap(nblk
)
679 register bitchunk_t
*bitmap
;
681 bitmap
= (bitchunk_t
*) alloc((unsigned) nblk
, block_size
);
686 /* Load the bitmap starting at block `bno' from disk. */
687 void loadbitmap(bitmap
, bno
, nblk
)
693 register bitchunk_t
*p
;
696 for (i
= 0; i
< nblk
; i
++, bno
++, p
+= WORDS_PER_BLOCK
)
697 devread(bno
, 0, (char *) p
, block_size
);
701 /* Write the bitmap starting at block `bno' to disk. */
702 void dumpbitmap(bitmap
, bno
, nblk
)
708 register bitchunk_t
*p
= bitmap
;
710 for (i
= 0; i
< nblk
; i
++, bno
++, p
+= WORDS_PER_BLOCK
)
711 devwrite(bno
, 0, (char *) p
, block_size
);
714 /* Set the bits given by `list' in the bitmap. */
715 void fillbitmap(bitmap
, lwb
, upb
, list
)
722 if (list
== 0) return;
723 while ((bit
= getnumber(*list
++)) != NO_BIT
)
724 if (bit
< lwb
|| bit
>= upb
) {
725 if (bitmap
== spec_imap
)
726 printf("inode number %ld ", bit
);
728 printf("zone number %ld ", bit
);
729 printf("out of range (ignored)\n");
731 setbit(bitmap
, bit
- lwb
+ 1);
734 /* Deallocate the bitmap `p'. */
741 /* Get all the bitmaps used by this program. */
744 imap
= allocbitmap(N_IMAP
);
745 zmap
= allocbitmap(N_ZMAP
);
746 spec_imap
= allocbitmap(N_IMAP
);
747 spec_zmap
= allocbitmap(N_ZMAP
);
748 dirmap
= allocbitmap(N_IMAP
);
751 /* Release all the space taken by the bitmaps. */
756 freebitmap(spec_imap
);
757 freebitmap(spec_zmap
);
761 /* `w1' and `w2' are differing words from two bitmaps that should be
762 * identical. Print what's the matter with them.
764 void chkword(w1
, w2
, bit
, type
, n
, report
, phys
)
771 for (; (w1
| w2
); w1
>>= 1, w2
>>= 1, bit
++, phys
++)
772 if ((w1
^ w2
) & 1 && ++(*n
) % MAXPRINT
== 0 && *report
&&
773 (!repair
|| automatic
|| yes("stop this listing")))
776 if ((w1
& 1) && !(w2
& 1))
777 printf("%s %ld is missing\n", type
, bit
);
778 else if (!(w1
& 1) && (w2
& 1))
779 printf("%s %ld is not free\n", type
, bit
);
782 /* Check if the given (correct) bitmap is identical with the one that is
783 * on the disk. If not, ask if the disk should be repaired.
785 void chkmap(cmap
, dmap
, bit
, blkno
, nblk
, type
)
786 bitchunk_t
*cmap
, *dmap
;
792 register bitchunk_t
*p
= dmap
, *q
= cmap
;
793 int report
= 1, nerr
= 0;
794 int w
= nblk
* WORDS_PER_BLOCK
;
797 printf("Checking %s map\n", type
);
798 loadbitmap(dmap
, blkno
, nblk
);
800 if (*p
!= *q
) chkword(*p
, *q
, bit
, type
, &nerr
, &report
, phys
);
803 bit
+= 8 * sizeof(bitchunk_t
);
804 phys
+= 8 * sizeof(bitchunk_t
);
807 if ((!repair
|| automatic
) && !report
) printf("etc. ");
808 if (nerr
> MAXPRINT
|| nerr
> 10) printf("%d errors found. ", nerr
);
809 if (nerr
!= 0 && yes("install a new map")) dumpbitmap(cmap
, blkno
, nblk
);
810 if (nerr
> 0) printf("\n");
813 /* See if the inodes that aren't allocated are cleared. */
816 register ino_t ino
= 1;
819 printf("Checking inode list\n");
821 if (!bitset(imap
, (bit_nr
) ino
)) {
822 devread(inoblock(ino
), inooff(ino
), (char *) &mode
,
824 if (mode
!= I_NOT_ALLOC
) {
825 printf("mode inode %u not cleared", ino
);
826 if (yes(". clear")) devwrite(inoblock(ino
),
827 inooff(ino
), nullbuf
, INODE_SIZE
);
830 while (++ino
<= sb
.s_ninodes
&& ino
!= 0);
834 /* Allocate an array to maintain the inode reference counts in. */
837 count
= (nlink_t
*) alloc((unsigned) (sb
.s_ninodes
+ 1), sizeof(nlink_t
));
840 /* The reference count for inode `ino' is wrong. Ask if it should be adjusted. */
841 void counterror(ino_t ino
)
846 printf("INODE NLINK COUNT\n");
849 devread(inoblock(ino
), inooff(ino
), (char *) &inode
, INODE_SIZE
);
850 count
[ino
] += inode
.i_nlinks
; /* it was already subtracted; add it back */
851 printf("%5u %5u %5u", ino
, (unsigned) inode
.i_nlinks
, count
[ino
]);
852 if (yes(" adjust")) {
853 if ((inode
.i_nlinks
= count
[ino
]) == 0) {
854 fatal("internal error (counterror)");
855 inode
.i_mode
= I_NOT_ALLOC
;
856 clrbit(imap
, (bit_nr
) ino
);
858 devwrite(inoblock(ino
), inooff(ino
), (char *) &inode
, INODE_SIZE
);
862 /* Check if the reference count of the inodes are correct. The array `count'
863 * is maintained as follows: an entry indexed by the inode number is
864 * incremented each time a link is found; when the inode is read the link
865 * count in there is substracted from the corresponding entry in `count'.
866 * Thus, when the whole file system has been traversed, all the entries
873 for (ino
= 1; ino
<= sb
.s_ninodes
&& ino
!= 0; ino
++)
874 if (count
[ino
] != 0) counterror(ino
);
875 if (!firstcnterr
) printf("\n");
878 /* Deallocate the `count' array. */
881 free((char *) count
);
884 /* Print the inode permission bits given by mode and shift. */
885 void printperm(mode_t mode
, int shift
, int special
, int overlay
)
887 if (mode
>> shift
& R_BIT
)
891 if (mode
>> shift
& W_BIT
)
898 if (mode
>> shift
& X_BIT
)
904 /* List the given inode. */
905 void list(ino_t ino
, d_inode
*ip
)
909 printf(" inode permission link size name\n");
912 switch (ip
->i_mode
& I_TYPE
) {
913 case I_REGULAR
: putchar('-'); break;
914 case I_DIRECTORY
: putchar('d'); break;
915 case I_CHAR_SPECIAL
: putchar('c'); break;
916 case I_BLOCK_SPECIAL
: putchar('b'); break;
917 case I_NAMED_PIPE
: putchar('p'); break;
918 #ifdef I_SYMBOLIC_LINK
919 case I_SYMBOLIC_LINK
: putchar('l'); break;
921 default: putchar('?');
923 printperm(ip
->i_mode
, 6, I_SET_UID_BIT
, 's');
924 printperm(ip
->i_mode
, 3, I_SET_GID_BIT
, 's');
925 printperm(ip
->i_mode
, 0, STICKY_BIT
, 't');
926 printf(" %3u ", ip
->i_nlinks
);
927 switch (ip
->i_mode
& I_TYPE
) {
929 case I_BLOCK_SPECIAL
:
930 printf(" %2x,%2x ", (dev_t
) ip
->i_zone
[0] >> MAJOR
& 0xFF,
931 (dev_t
) ip
->i_zone
[0] >> MINOR
& 0xFF);
933 default: printf("%7ld ", ip
->i_size
);
938 /* Remove an entry from a directory if ok with the user.
939 * Don't name the function remove() - that is owned by ANSI, and chaos results
940 * when it is a macro.
942 int Remove(dir_struct
*dp
)
944 setbit(spec_imap
, (bit_nr
) dp
->d_inum
);
945 if (yes(". remove entry")) {
947 memset((void *) dp
, 0, sizeof(dir_struct
));
953 /* Convert string so that embedded control characters are printable. */
954 void make_printable_name(dst
, src
, n
)
961 while (--n
>= 0 && (c
= *src
++) != '\0') {
962 if (isprint(c
) && c
!= '\\')
968 *dst
++ = '\\'; break;
980 *dst
++ = '0' + ((c
>> 6) & 03);
981 *dst
++ = '0' + ((c
>> 3) & 07);
982 *dst
++ = '0' + (c
& 07);
989 /* See if the `.' or `..' entry is as expected. */
990 int chkdots(ino_t ino
, off_t pos
, dir_struct
*dp
, ino_t exp
)
992 char printable_name
[4 * NAME_MAX
+ 1];
994 if (dp
->d_inum
!= exp
) {
995 make_printable_name(printable_name
, dp
->d_name
, sizeof(dp
->d_name
));
996 printf("bad %s in ", printable_name
);
998 printf("%s is linked to %u ", printable_name
, dp
->d_inum
);
999 printf("instead of %u)", exp
);
1000 setbit(spec_imap
, (bit_nr
) ino
);
1001 setbit(spec_imap
, (bit_nr
) dp
->d_inum
);
1002 setbit(spec_imap
, (bit_nr
) exp
);
1003 if (yes(". repair")) {
1004 count
[dp
->d_inum
]--;
1009 } else if (pos
!= (dp
->d_name
[1] ? DIR_ENTRY_SIZE
: 0)) {
1010 make_printable_name(printable_name
, dp
->d_name
, sizeof(dp
->d_name
));
1011 printf("warning: %s has offset %ld in ", printable_name
, pos
);
1013 printf("%s is linked to %u)\n", printable_name
, dp
->d_inum
);
1014 setbit(spec_imap
, (bit_nr
) ino
);
1015 setbit(spec_imap
, (bit_nr
) dp
->d_inum
);
1016 setbit(spec_imap
, (bit_nr
) exp
);
1021 /* Check the name in a directory entry. */
1022 int chkname(ino_t ino
, dir_struct
*dp
)
1024 register n
= NAME_MAX
+ 1;
1025 register char *p
= dp
->d_name
;
1028 printf("null name found in ");
1030 setbit(spec_imap
, (bit_nr
) ino
);
1031 if (Remove(dp
)) return(0);
1033 while (*p
!= '\0' && --n
!= 0)
1035 printf("found a '/' in entry of directory ");
1037 setbit(spec_imap
, (bit_nr
) ino
);
1038 printf("entry = '");
1039 printname(dp
->d_name
);
1041 if (Remove(dp
)) return(0);
1047 /* Check a directory entry. Here the routine `descendtree' is called
1048 * recursively to check the file or directory pointed to by the entry.
1050 int chkentry(ino_t ino
, off_t pos
, dir_struct
*dp
)
1052 if (dp
->d_inum
< ROOT_INODE
|| dp
->d_inum
> sb
.s_ninodes
) {
1053 printf("bad inode found in directory ");
1055 printf("ino found = %u, ", dp
->d_inum
);
1057 printname(dp
->d_name
);
1059 if (yes(". remove entry")) {
1060 memset((void *) dp
, 0, sizeof(dir_struct
));
1065 if ((unsigned) count
[dp
->d_inum
] == SHRT_MAX
) {
1066 printf("too many links to ino %u\n", dp
->d_inum
);
1067 printf("discovered at entry '");
1068 printname(dp
->d_name
);
1069 printf("' in directory ");
1071 if (Remove(dp
)) return(0);
1073 count
[dp
->d_inum
]++;
1074 if (strcmp(dp
->d_name
, ".") == 0) {
1075 ftop
->st_presence
|= DOT
;
1076 return(chkdots(ino
, pos
, dp
, ino
));
1078 if (strcmp(dp
->d_name
, "..") == 0) {
1079 ftop
->st_presence
|= DOTDOT
;
1080 return(chkdots(ino
, pos
, dp
, ino
== ROOT_INODE
? ino
:
1081 ftop
->st_next
->st_dir
->d_inum
));
1083 if (!chkname(ino
, dp
)) return(0);
1084 if (bitset(dirmap
, (bit_nr
) dp
->d_inum
)) {
1085 printf("link to directory discovered in ");
1088 printname(dp
->d_name
);
1089 printf("', dir ino = %u)", dp
->d_inum
);
1092 return(descendtree(dp
));
1095 /* Check a zone of a directory by checking all the entries in the zone.
1096 * The zone is split up into chunks to not allocate too much stack.
1098 int chkdirzone(ino_t ino
, d_inode
*ip
, off_t pos
, zone_nr zno
)
1100 dir_struct dirblk
[CDIRECT
];
1101 register dir_struct
*dp
;
1103 long block
= ztob(zno
);
1104 register long offset
= 0;
1105 register off_t size
= 0;
1106 n
= SCALE
* (NR_DIR_ENTRIES(block_size
) / CDIRECT
);
1109 devread(block
, offset
, (char *) dirblk
, DIRCHUNK
);
1111 for (dp
= dirblk
; dp
< &dirblk
[CDIRECT
]; dp
++) {
1112 if (dp
->d_inum
!= NO_ENTRY
&& !chkentry(ino
, pos
, dp
))
1114 pos
+= DIR_ENTRY_SIZE
;
1115 if (dp
->d_inum
!= NO_ENTRY
) size
= pos
;
1117 if (dirty
) devwrite(block
, offset
, (char *) dirblk
, DIRCHUNK
);
1122 if (size
> ip
->i_size
) {
1123 printf("size not updated of directory ");
1125 if (yes(". extend")) {
1126 setbit(spec_imap
, (bit_nr
) ino
);
1128 devwrite(inoblock(ino
), inooff(ino
), (char *) ip
, INODE_SIZE
);
1135 int chksymlinkzone(ino_t ino
, d_inode
*ip
, off_t pos
, zone_nr zno
)
1139 char target
[PATH_MAX
+1];
1141 if (ip
->i_size
> PATH_MAX
)
1142 fatal("chksymlinkzone: fsck program inconsistency\n");
1144 devread(block
, 0, target
, ip
->i_size
);
1145 target
[ip
->i_size
]= '\0';
1146 len
= strlen(target
);
1147 if (len
!= ip
->i_size
)
1149 printf("bad size in symbolic link (%d instead of %d) ",
1152 if (yes(". update")) {
1153 setbit(spec_imap
, (bit_nr
) ino
);
1155 devwrite(inoblock(ino
), inooff(ino
),
1156 (char *) ip
, INODE_SIZE
);
1162 /* There is something wrong with the given zone. Print some details. */
1163 void errzone(mess
, zno
, level
, pos
)
1169 printf("%s zone in ", mess
);
1171 printf("zno = %ld, type = ", zno
);
1173 case 0: printf("DATA"); break;
1174 case 1: printf("SINGLE INDIRECT"); break;
1175 case 2: printf("DOUBLE INDIRECT"); break;
1176 default: printf("VERY INDIRECT");
1178 printf(", pos = %ld)\n", pos
);
1181 /* Found the given zone in the given inode. Check it, and if ok, mark it
1182 * in the zone bitmap.
1184 int markzone(zno
, level
, pos
)
1189 register bit_nr bit
= (bit_nr
) zno
- FIRST
+ 1;
1192 if (zno
< FIRST
|| zno
>= sb
.s_zones
) {
1193 errzone("out-of-range", zno
, level
, pos
);
1196 if (bitset(zmap
, bit
)) {
1197 setbit(spec_zmap
, bit
);
1198 errzone("duplicate", zno
, level
, pos
);
1202 if (bitset(spec_zmap
, bit
)) errzone("found", zno
, level
, pos
);
1207 /* Check an indirect zone by checking all of its entries.
1208 * The zone is split up into chunks to not allocate too much stack.
1210 int chkindzone(ino_t ino
, d_inode
*ip
, off_t
*pos
, zone_nr zno
, int level
)
1212 zone_nr indirect
[CINDIR
];
1213 register n
= NR_INDIRECTS
/ CINDIR
;
1214 long block
= ztob(zno
);
1215 register long offset
= 0;
1218 devread(block
, offset
, (char *) indirect
, INDCHUNK
);
1219 if (!chkzones(ino
, ip
, pos
, indirect
, CINDIR
, level
- 1)) return(0);
1221 } while (--n
&& *pos
< ip
->i_size
);
1225 /* Return the size of a gap in the file, represented by a null zone number
1226 * at some level of indirection.
1231 off_t power
= ZONE_SIZE
;
1234 power
*= NR_INDIRECTS
;
1239 /* Check a zone, which may be either a normal data zone, a directory zone,
1240 * or an indirect zone.
1242 int zonechk(ino_t ino
, d_inode
*ip
, off_t
*pos
, zone_nr zno
, int level
)
1245 if ((ip
->i_mode
& I_TYPE
) == I_DIRECTORY
&&
1246 !chkdirzone(ino
, ip
, *pos
, zno
))
1248 if ((ip
->i_mode
& I_TYPE
) == I_SYMBOLIC_LINK
&&
1249 !chksymlinkzone(ino
, ip
, *pos
, zno
))
1254 return chkindzone(ino
, ip
, pos
, zno
, level
);
1257 /* Check a list of zones given by `zlist'. */
1258 int chkzones(ino_t ino
, d_inode
*ip
, off_t
*pos
, zone_nr
*zlist
,
1263 /* The check on the position in the next loop is commented out, since FS
1264 * now requires valid zone numbers in each level that is necessary and FS
1265 * always deleted all the zones in the double indirect block.
1267 for (i
= 0; i
< len
/* && *pos < ip->i_size */ ; i
++)
1268 if (zlist
[i
] == NO_ZONE
)
1269 *pos
+= jump(level
);
1270 else if (!markzone(zlist
[i
], level
, *pos
)) {
1271 *pos
+= jump(level
);
1273 } else if (!zonechk(ino
, ip
, pos
, zlist
[i
], level
))
1278 /* Check a file or a directory. */
1279 int chkfile(ino_t ino
, d_inode
*ip
)
1281 register ok
, i
, level
;
1284 ok
= chkzones(ino
, ip
, &pos
, &ip
->i_zone
[0], NR_DZONE_NUM
, 0);
1285 for (i
= NR_DZONE_NUM
, level
= 1; i
< NR_ZONE_NUMS
; i
++, level
++)
1286 ok
&= chkzones(ino
, ip
, &pos
, &ip
->i_zone
[i
], 1, level
);
1290 /* Check a directory by checking the contents. Check if . and .. are present. */
1291 int chkdirectory(ino_t ino
, d_inode
*ip
)
1295 setbit(dirmap
, (bit_nr
) ino
);
1296 ok
= chkfile(ino
, ip
);
1297 if (!(ftop
->st_presence
& DOT
)) {
1298 printf(". missing in ");
1302 if (!(ftop
->st_presence
& DOTDOT
)) {
1303 printf(".. missing in ");
1310 #ifdef I_SYMBOLIC_LINK
1312 /* Check the validity of a symbolic link. */
1313 int chklink(ino_t ino
, d_inode
*ip
)
1317 ok
= chkfile(ino
, ip
);
1318 if (ip
->i_size
<= 0 || ip
->i_size
> block_size
) {
1319 if (ip
->i_size
== 0)
1320 printf("empty symbolic link ");
1322 printf("symbolic link too large (size %ld) ", ip
->i_size
);
1331 /* Check the validity of a special file. */
1332 int chkspecial(ino_t ino
, d_inode
*ip
)
1337 if ((dev_t
) ip
->i_zone
[0] == NO_DEV
) {
1338 printf("illegal device number %ld for special file ", ip
->i_zone
[0]);
1343 /* FS will not use the remaining "zone numbers" but 1.6.11++ will panic if
1344 * they are nonzero, since this should not happen.
1346 for (i
= 1; i
< NR_ZONE_NUMS
; i
++)
1347 if (ip
->i_zone
[i
] != NO_ZONE
) {
1348 printf("nonzero zone number %ld for special file ",
1356 /* Check the mode and contents of an inode. */
1357 int chkmode(ino_t ino
, d_inode
*ip
)
1359 switch (ip
->i_mode
& I_TYPE
) {
1362 return chkfile(ino
, ip
);
1365 return chkdirectory(ino
, ip
);
1366 case I_BLOCK_SPECIAL
:
1368 return chkspecial(ino
, ip
);
1369 case I_CHAR_SPECIAL
:
1371 return chkspecial(ino
, ip
);
1374 return chkfile(ino
, ip
);
1375 #ifdef I_SYMBOLIC_LINK
1376 case I_SYMBOLIC_LINK
:
1378 return chklink(ino
, ip
);
1382 printf("bad mode of ");
1384 printf("mode = %o)", ip
->i_mode
);
1389 /* Check an inode. */
1390 int chkinode(ino_t ino
, d_inode
*ip
)
1392 if (ino
== ROOT_INODE
&& (ip
->i_mode
& I_TYPE
) != I_DIRECTORY
) {
1393 printf("root inode is not a directory ");
1394 printf("(ino = %u, mode = %o)\n", ino
, ip
->i_mode
);
1397 if (ip
->i_nlinks
== 0) {
1398 printf("link count zero of ");
1403 setbit(imap
, (bit_nr
) ino
);
1404 if ((unsigned) ip
->i_nlinks
> SHRT_MAX
) {
1405 printf("link count too big in ");
1407 printf("cnt = %u)\n", (unsigned) ip
->i_nlinks
);
1408 count
[ino
] -= SHRT_MAX
;
1409 setbit(spec_imap
, (bit_nr
) ino
);
1411 count
[ino
] -= (unsigned) ip
->i_nlinks
;
1413 return chkmode(ino
, ip
);
1416 /* Check the directory entry pointed to by dp, by checking the inode. */
1421 register ino_t ino
= dp
->d_inum
;
1428 if (bitset(spec_imap
, (bit_nr
) ino
)) {
1429 printf("found inode %u: ", ino
);
1432 visited
= bitset(imap
, (bit_nr
) ino
);
1433 if (!visited
|| listing
) {
1434 devread(inoblock(ino
), inooff(ino
), (char *) &inode
, INODE_SIZE
);
1435 if (listing
) list(ino
, &inode
);
1436 if (!visited
&& !chkinode(ino
, &inode
)) {
1437 setbit(spec_imap
, (bit_nr
) ino
);
1438 if (yes("remove")) {
1439 count
[ino
] += inode
.i_nlinks
- 1;
1440 clrbit(imap
, (bit_nr
) ino
);
1441 devwrite(inoblock(ino
), inooff(ino
),
1442 nullbuf
, INODE_SIZE
);
1443 memset((void *) dp
, 0, sizeof(dir_struct
));
1444 ftop
= ftop
->st_next
;
1449 ftop
= ftop
->st_next
;
1453 /* Check the file system tree. */
1458 nfreeinode
= sb
.s_ninodes
;
1460 dir
.d_inum
= ROOT_INODE
;
1462 if (!descendtree(&dir
)) fatal("bad root inode");
1466 /* Print the totals of all the objects found. */
1469 printf("blocksize = %5d ", block_size
);
1470 printf("zonesize = %5d\n", ZONE_SIZE
);
1472 pr("%8u Regular file%s\n", nregular
, "", "s");
1473 pr("%8u Director%s\n", ndirectory
, "y", "ies");
1474 pr("%8u Block special file%s\n", nblkspec
, "", "s");
1475 pr("%8u Character special file%s\n", ncharspec
, "", "s");
1476 if (nbadinode
!= 0) pr("%6u Bad inode%s\n", nbadinode
, "", "s");
1477 pr("%8u Free inode%s\n", nfreeinode
, "", "s");
1478 pr("%8u Named pipe%s\n", npipe
, "", "s");
1479 pr("%8u Symbolic link%s\n", nsyml
, "", "s");
1480 /* Don't print some fields.
1482 pr("%8u Data zone%s\n", ztype[0], "", "s");
1483 pr("%8u Single indirect zone%s\n", ztype[1], "", "s");
1484 pr("%8u Double indirect zone%s\n", ztype[2], "", "s");
1486 lpr("%8ld Free zone%s\n", nfreezone
, "", "s");
1489 /* Check the device which name is given by `f'. The inodes listed by `clist'
1490 * should be listed separately, and the inodes listed by `ilist' and the zones
1491 * listed by `zlist' should be watched for while checking the file system.
1494 void chkdev(f
, clist
, ilist
, zlist
)
1495 char *f
, **clist
, **ilist
, **zlist
;
1497 if (automatic
) repair
= 1;
1505 if(block_size
< _MIN_BLOCK_SIZE
)
1506 fatal("funny block size");
1508 if(!(rwbuf
= malloc(block_size
))) fatal("couldn't allocate fs buf (1)");
1509 if(!(nullbuf
= malloc(block_size
))) fatal("couldn't allocate fs buf (2)");
1510 memset(nullbuf
, 0, block_size
);
1518 fillbitmap(spec_imap
, (bit_nr
) 1, (bit_nr
) sb
.s_ninodes
+ 1, ilist
);
1519 fillbitmap(spec_zmap
, (bit_nr
) FIRST
, (bit_nr
) sb
.s_zones
, zlist
);
1523 chkmap(zmap
, spec_zmap
, (bit_nr
) FIRST
- 1, BLK_ZMAP
, N_ZMAP
, "zone");
1525 chkmap(imap
, spec_imap
, (bit_nr
) 0, BLK_IMAP
, N_IMAP
, "inode");
1533 if (changed
) printf("----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n");
1536 int main(argc
, argv
)
1540 register char **clist
= 0, **ilist
= 0, **zlist
= 0;
1542 register devgiven
= 0;
1545 if ((1 << BITSHIFT
) != 8 * sizeof(bitchunk_t
)) {
1546 printf("Fsck was compiled with the wrong BITSHIFT!\n");
1552 while ((arg
= *argv
++) != 0)
1553 if (arg
[0] == '-' && arg
[1] != 0 && arg
[2] == 0) switch (arg
[1]) {
1554 case 'a': automatic
^= 1; break;
1556 clist
= getlist(&argv
, "inode");
1559 ilist
= getlist(&argv
, "inode");
1562 zlist
= getlist(&argv
, "zone");
1564 case 'r': repair
^= 1; break;
1565 case 'l': listing
^= 1; break;
1566 case 's': listsuper
^= 1; break;
1568 printf("%s: unknown flag '%s'\n", prog
, arg
);
1571 chkdev(arg
, clist
, ilist
, zlist
);
1578 printf("Usage: fsck [-acilrsz] file\n");