Improve the process for GNU tools
[minix3.git] / minix / commands / fsck.mfs / fsck.c
blobfbffc83148d3748931797c783c72f24754e5ae0f
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
11 #define bit_nr bit_t
12 #define block_nr block_t
13 #define d_inode d2_inode
14 #define d_inum mfs_d_ino
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>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <minix/config.h>
47 #include <minix/const.h>
48 #include <minix/type.h>
49 #include <minix/ipc.h>
50 #include "mfs/const.h"
51 #include "mfs/inode.h"
52 #include "mfs/type.h"
53 #include "mfs/mfsdir.h"
54 #include <minix/fslib.h>
55 #include <stdio.h>
56 #include <sys/stat.h>
57 #include <dirent.h>
59 #include "exitvalues.h"
61 #undef N_DATA
63 unsigned int fs_version = 2, block_size = 0;
65 #define BITSHIFT 5 /* = 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 "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) ((u64_t)(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
112 #define BLK_IMAP 2
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? */
133 struct stack {
134 dir_struct *st_dir;
135 struct stack *st_next;
136 char st_presence;
137 } *ftop;
139 int dev; /* file descriptor of the device */
141 #define DOT 1
142 #define DOTDOT 2
144 /* Counters for each type of inode/zone. */
145 int nfreeinode, nregular, ndirectory, nblkspec, ncharspec, nbadinode;
146 int nsock, npipe, nsyml, ztype[NLEVEL];
147 long nfreezone;
149 int repair, notrepaired = 0, automatic, listing, listsuper; /* flags */
150 int preen = 0, markdirty = 0;
151 int firstlist; /* has the listing header been printed? */
152 unsigned part_offset; /* sector offset for this partition */
153 char answer[] = "Answer questions with y or n. Then hit RETURN";
155 int main(int argc, char **argv);
156 void initvars(void);
157 void fatal(char *s);
158 int eoln(int c);
159 int yes(char *question);
160 int atoo(char *s);
161 int input(char *buf, int size);
162 char *alloc(unsigned nelem, unsigned elsize);
163 void printname(char *s);
164 void printrec(struct stack *sp);
165 void printpath(int mode, int nlcr);
166 void devopen(void);
167 void devclose(void);
168 void devio(block_nr bno, int dir);
169 void devread(long block, long offset, char *buf, int size);
170 void devwrite(long block, long offset, char *buf, int size);
171 void pr(char *fmt, int cnt, char *s, char *p);
172 void lpr(char *fmt, long cnt, char *s, char *p);
173 bit_nr getnumber(char *s);
174 char **getlist(char ***argv, char *type);
175 void lsuper(void);
176 #define SUPER_GET 0
177 #define SUPER_PUT 1
178 void rw_super(int mode);
179 void chksuper(void);
180 void lsi(char **clist);
181 bitchunk_t *allocbitmap(int nblk);
182 void loadbitmap(bitchunk_t *bitmap, block_nr bno, int nblk);
183 void dumpbitmap(bitchunk_t *bitmap, block_nr bno, int nblk);
184 void fillbitmap(bitchunk_t *bitmap, bit_nr lwb, bit_nr upb, char
185 **list);
186 void freebitmap(bitchunk_t *p);
187 void getbitmaps(void);
188 void putbitmaps(void);
189 void chkword(unsigned w1, unsigned w2, bit_nr bit, char *type, int *n,
190 int *report, bit_t);
191 void chkmap(bitchunk_t *cmap, bitchunk_t *dmap, bit_nr bit, block_nr
192 blkno, int nblk, char *type);
193 void chkilist(void);
194 void getcount(void);
195 void counterror(ino_t ino);
196 void chkcount(void);
197 void freecount(void);
198 void printperm(mode_t mode, int shift, int special, int overlay);
199 void list(ino_t ino, d_inode *ip);
200 int Remove(dir_struct *dp);
201 void make_printable_name(char *dst, char *src, int n);
202 int chkdots(ino_t ino, off_t pos, dir_struct *dp, ino_t exp);
203 int chkname(ino_t ino, dir_struct *dp);
204 int chkentry(ino_t ino, off_t pos, dir_struct *dp);
205 int chkdirzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno);
206 int chksymlinkzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno);
207 void errzone(char *mess, zone_nr zno, int level, off_t pos);
208 int markzone(zone_nr zno, int level, off_t pos);
209 int chkindzone(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int
210 level);
211 off_t jump(int level);
212 int zonechk(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level);
213 int chkzones(ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist, int
214 len, int level);
215 int chkfile(ino_t ino, d_inode *ip);
216 int chkdirectory(ino_t ino, d_inode *ip);
217 int chklink(ino_t ino, d_inode *ip);
218 int chkspecial(ino_t ino, d_inode *ip);
219 int chkmode(ino_t ino, d_inode *ip);
220 int chkinode(ino_t ino, d_inode *ip);
221 int descendtree(dir_struct *dp);
222 void chktree(void);
223 void printtotal(void);
224 void chkdev(char *f, char **clist, char **ilist, char **zlist);
226 /* Initialize the variables used by this program. */
227 void initvars()
229 register int level;
231 nregular = ndirectory = nblkspec = ncharspec =
232 nbadinode = nsock = npipe = nsyml = 0;
233 for (level = 0; level < NLEVEL; level++) ztype[level] = 0;
234 changed = 0;
235 thisblk = NO_BLOCK;
236 firstlist = 1;
237 firstcnterr = 1;
240 /* Print the string `s' and exit. */
241 void fatal(s)
242 char *s;
244 printf("%s\nfatal\n", s);
245 exit(FSCK_EXIT_CHECK_FAILED);
248 /* Test for end of line. */
249 int eoln(c)
250 int c;
252 return(c == EOF || c == '\n' || c == '\r');
255 /* Ask a question and get the answer unless automatic is set. */
256 int yes(question)
257 char *question;
259 register int c, answerchar;
260 static int note = 0;
261 int yes;
263 if (!repair) {
264 printf("\n");
265 return(0);
267 printf("%s? ", question);
268 if(!note) { printf("(y=yes, n=no, q=quit, A=for yes to all) "); note = 1; }
269 if (automatic) {
270 printf("yes\n");
271 return(1);
273 fflush(stdout);
274 if ((c = answerchar = getchar()) == 'q' || c == 'Q') exit(FSCK_EXIT_CHECK_FAILED);
275 if(c == 'A') { automatic = 1; c = 'y'; }
276 while (!eoln(c)) c = getchar();
277 yes = !(answerchar == 'n' || answerchar == 'N');
278 if(!yes) notrepaired = 1;
279 return yes;
282 /* Convert string to integer. Representation is octal. */
283 int atoo(s)
284 register char *s;
286 register int n = 0;
288 while ('0' <= *s && *s < '8') {
289 n <<= 3;
290 n += *s++ - '0';
292 return n;
295 /* If repairing the file system, print a prompt and get a string from user. */
296 int input(buf, size)
297 char *buf;
298 int size;
300 register char *p = buf;
302 printf("\n");
303 if (repair) {
304 printf("--> ");
305 fflush(stdout);
306 while (--size) {
307 *p = getchar();
308 if (eoln(*p)) {
309 *p = 0;
310 return(p > buf);
312 p++;
314 *p = 0;
315 while (!eoln(getchar()));
316 return(1);
318 return(0);
321 /* Allocate some memory and zero it. */
322 char *alloc(nelem, elsize)
323 unsigned nelem, elsize;
325 char *p;
327 if ((p = (char *)malloc((size_t)nelem * elsize)) == 0) {
328 fprintf(stderr, "Tried to allocate %dkB\n",
329 nelem*elsize/1024);
330 fatal("out of memory");
332 memset((void *) p, 0, (size_t)nelem * elsize);
333 return(p);
336 /* Print the name in a directory entry. */
337 void printname(s)
338 char *s;
340 register int n = MFS_NAME_MAX;
341 int c;
343 do {
344 if ((c = *s) == 0) break;
345 if (!isprint(c)) c = '?';
346 putchar(c);
347 s++;
348 } while (--n);
351 /* Print the pathname given by a linked list pointed to by `sp'. The
352 * names are in reverse order.
354 void printrec(struct stack *sp)
356 if (sp->st_next != 0) {
357 printrec(sp->st_next);
358 putchar('/');
359 printname(sp->st_dir->mfs_d_name);
363 /* Print the current pathname. */
364 void printpath(mode, nlcr)
365 int mode;
366 int nlcr;
368 if (ftop->st_next == 0)
369 putchar('/');
370 else
371 printrec(ftop);
372 switch (mode) {
373 case 1:
374 printf(" (ino = %u, ", ftop->st_dir->d_inum);
375 break;
376 case 2:
377 printf(" (ino = %u)", ftop->st_dir->d_inum);
378 break;
380 if (nlcr) printf("\n");
383 /* Open the device. */
384 void devopen()
386 if ((dev = open(fsck_device,
387 (repair || markdirty) ? O_RDWR : O_RDONLY)) < 0) {
388 perror(fsck_device);
389 fatal("couldn't open device to fsck");
393 /* Close the device. */
394 void devclose()
396 if (close(dev) != 0) {
397 perror("close");
398 fatal("");
402 /* Read or write a block. */
403 void devio(bno, dir)
404 block_nr bno;
405 int dir;
407 off_t r;
409 if(!block_size) fatal("devio() with unknown block size");
410 if (dir == READING && bno == thisblk) return;
411 thisblk = bno;
413 #if 0
414 printf("%s at block %5d\n", dir == READING ? "reading " : "writing", bno);
415 #endif
416 r= lseek(dev, btoa64(bno), SEEK_SET);
417 if (r == (off_t)-1)
418 fatal("lseek failed");
419 if (dir == READING) {
420 if (read(dev, rwbuf, block_size) == block_size)
421 return;
422 } else {
423 if (write(dev, rwbuf, block_size) == block_size)
424 return;
427 printf("%s: can't %s block %ld (error = 0x%x)\n", prog,
428 dir == READING ? "read" : "write", (long) bno, errno);
429 if (dir == READING) {
430 printf("Continuing with a zero-filled block.\n");
431 memset(rwbuf, 0, block_size);
432 return;
434 fatal("");
437 /* Read `size' bytes from the disk starting at block 'block' and
438 * byte `offset'.
440 void devread(block, offset, buf, size)
441 long block;
442 long offset;
443 char *buf;
444 int size;
446 if(!block_size) fatal("devread() with unknown block size");
447 if (offset >= block_size)
449 block += offset/block_size;
450 offset %= block_size;
452 devio(block, READING);
453 memmove(buf, &rwbuf[offset], size);
456 /* Write `size' bytes to the disk starting at block 'block' and
457 * byte `offset'.
459 void devwrite(block, offset, buf, size)
460 long block;
461 long offset;
462 char *buf;
463 int size;
465 if(!block_size) fatal("devwrite() with unknown block size");
466 if (!repair) fatal("internal error (devwrite)");
467 if (offset >= block_size)
469 block += offset/block_size;
470 offset %= block_size;
472 if (size != block_size) devio(block, READING);
473 memmove(&rwbuf[offset], buf, size);
474 devio(block, WRITING);
475 changed = 1;
478 /* Print a string with either a singular or a plural pronoun. */
479 void pr(fmt, cnt, s, p)
480 char *fmt, *s, *p;
481 int cnt;
483 printf(fmt, cnt, cnt == 1 ? s : p);
486 /* Same as above, but with a long argument */
487 void lpr(fmt, cnt, s, p)
488 char *fmt, *s, *p;
489 long cnt;
491 printf(fmt, cnt, cnt == 1 ? s : p);
494 /* Convert string to number. */
495 bit_nr getnumber(s)
496 register char *s;
498 register bit_nr n = 0;
500 if (s == NULL)
501 return NO_BIT;
502 while (isdigit(*s))
503 n = (n << 1) + (n << 3) + *s++ - '0';
504 return (*s == '\0') ? n : NO_BIT;
507 /* See if the list pointed to by `argv' contains numbers. */
508 char **getlist(argv, type)
509 char ***argv, *type;
511 register char **list = *argv;
512 register int empty = 1;
514 while (getnumber(**argv) != NO_BIT) {
515 (*argv)++;
516 empty = 0;
518 if (empty) {
519 printf("warning: no %s numbers given\n", type);
520 return(NULL);
522 return(list);
525 /* Make a listing of the super block. If `repair' is set, ask the user
526 * for changes.
528 void lsuper()
530 char buf[80];
532 do {
533 /* Most of the following atol's enrage lint, for good reason. */
534 printf("ninodes = %u", sb.s_ninodes);
535 if (input(buf, 80)) sb.s_ninodes = atol(buf);
536 printf("nzones = %d", sb.s_zones);
537 if (input(buf, 80)) sb.s_zones = atol(buf);
538 printf("imap_blocks = %u", sb.s_imap_blocks);
539 if (input(buf, 80)) sb.s_imap_blocks = atol(buf);
540 printf("zmap_blocks = %u", sb.s_zmap_blocks);
541 if (input(buf, 80)) sb.s_zmap_blocks = atol(buf);
542 printf("firstdatazone = %u", sb.s_firstdatazone_old);
543 if (input(buf, 80)) sb.s_firstdatazone_old = atol(buf);
544 printf("log_zone_size = %u", sb.s_log_zone_size);
545 if (input(buf, 80)) sb.s_log_zone_size = atol(buf);
546 printf("maxsize = %d", sb.s_max_size);
547 if (input(buf, 80)) sb.s_max_size = atol(buf);
548 printf("block size = %d", sb.s_block_size);
549 if (input(buf, 80)) sb.s_block_size = atol(buf);
550 if (yes("ok now")) {
551 devwrite(0, OFFSET_SUPER_BLOCK, (char *) &sb, sizeof(sb));
552 return;
554 printf("flags = ");
555 if(sb.s_flags & MFSFLAG_CLEAN) printf("CLEAN "); else printf("DIRTY ");
556 printf("\n");
557 } while (yes("Do you want to try again"));
558 if (repair) exit(FSCK_EXIT_OK);
561 /* Get the super block from either disk or user. Do some initial checks. */
562 void rw_super(int put)
564 if(lseek(dev, OFFSET_SUPER_BLOCK, SEEK_SET) < 0) {
565 perror("lseek");
566 fatal("couldn't seek to super block.");
568 if(put == SUPER_PUT) {
569 if(write(dev, &sb, sizeof(sb)) != sizeof(sb)) {
570 fatal("couldn't write super block.");
572 return;
574 if(read(dev, &sb, sizeof(sb)) != sizeof(sb)) {
575 fatal("couldn't read super block.");
577 if (listsuper) lsuper();
578 if (sb.s_magic == SUPER_MAGIC) fatal("Cannot handle V1 file systems");
579 if (sb.s_magic == SUPER_V2) {
580 fs_version = 2;
581 block_size = /* STATIC_BLOCK_SIZE */ 8192;
582 } else if(sb.s_magic == SUPER_V3) {
583 fs_version = 3;
584 block_size = sb.s_block_size;
585 } else {
586 fatal("bad magic number in super block");
588 if (sb.s_ninodes <= 0) fatal("no inodes");
589 if (sb.s_zones <= 0) fatal("no zones");
590 if (sb.s_imap_blocks <= 0) fatal("no imap");
591 if (sb.s_zmap_blocks <= 0) fatal("no zmap");
592 if (sb.s_firstdatazone != 0 && sb.s_firstdatazone <= 4)
593 fatal("first data zone too small");
594 if (sb.s_log_zone_size < 0) fatal("zone size < block size");
595 if (sb.s_max_size <= 0) {
596 printf("warning: invalid max file size %d\n", sb.s_max_size);
597 sb.s_max_size = LONG_MAX;
601 /* Check the super block for reasonable contents. */
602 void chksuper()
604 register int n;
605 register off_t maxsize;
607 n = bitmapsize((bit_t) sb.s_ninodes + 1, block_size);
608 if (sb.s_magic != SUPER_V2 && sb.s_magic != SUPER_V3)
609 fatal("bad magic number in super block");
610 if (sb.s_imap_blocks < n) {
611 printf("need %d bocks for inode bitmap; only have %d\n",
612 n, sb.s_imap_blocks);
613 fatal("too few imap blocks");
615 if (sb.s_imap_blocks != n) {
616 pr("warning: expected %d imap_block%s", n, "", "s");
617 printf(" instead of %d\n", sb.s_imap_blocks);
619 n = bitmapsize((bit_t) sb.s_zones, block_size);
620 if (sb.s_zmap_blocks < n) fatal("too few zmap blocks");
621 if (sb.s_zmap_blocks != n) {
622 pr("warning: expected %d zmap_block%s", n, "", "s");
623 printf(" instead of %d\n", sb.s_zmap_blocks);
625 if (sb.s_log_zone_size >= 8 * sizeof(block_nr))
626 fatal("log_zone_size too large");
627 if (sb.s_log_zone_size > 8) printf("warning: large log_zone_size (%d)\n",
628 sb.s_log_zone_size);
629 sb.s_firstdatazone = (BLK_ILIST + N_ILIST + SCALE - 1) >> sb.s_log_zone_size;
630 if (sb.s_firstdatazone_old != 0) {
631 if (sb.s_firstdatazone_old >= sb.s_zones)
632 fatal("first data zone too large");
633 if (sb.s_firstdatazone_old < sb.s_firstdatazone)
634 fatal("first data zone too small");
635 if (sb.s_firstdatazone_old != sb.s_firstdatazone) {
636 printf("warning: expected first data zone to be %u ",
637 sb.s_firstdatazone);
638 printf("instead of %u\n", sb.s_firstdatazone_old);
639 sb.s_firstdatazone = sb.s_firstdatazone_old;
642 maxsize = MAX_FILE_POS;
643 if (((maxsize - 1) >> sb.s_log_zone_size) / block_size >= MAX_ZONES)
644 maxsize = ((long) MAX_ZONES * block_size) << sb.s_log_zone_size;
645 if(maxsize <= 0)
646 maxsize = LONG_MAX;
647 if (sb.s_max_size != maxsize) {
648 printf("warning: expected max size to be %lld ", maxsize);
649 printf("instead of %d\n", sb.s_max_size);
652 if(sb.s_flags & MFSFLAG_MANDATORY_MASK) {
653 fatal("unsupported feature bits - newer fsck needed");
657 int inoblock(int inn)
659 return (int)(((u64_t)(inn - 1) * INODE_SIZE) / block_size) + BLK_ILIST;
662 int inooff(int inn)
664 return (int)(((u64_t)(inn - 1) * INODE_SIZE) % block_size);
667 /* Make a listing of the inodes given by `clist'. If `repair' is set, ask
668 * the user for changes.
670 void lsi(clist)
671 char **clist;
673 register bit_nr bit;
674 register ino_t ino;
675 d_inode inode, *ip = &inode;
676 char buf[80];
678 if (clist == 0) return;
679 while ((bit = getnumber(*clist++)) != NO_BIT) {
680 setbit(spec_imap, bit);
681 ino = bit;
682 do {
683 devread(inoblock(ino), inooff(ino), (char *) ip, INODE_SIZE);
684 printf("inode %llu:\n", ino);
685 printf(" mode = %6o", ip->i_mode);
686 if (input(buf, 80)) ip->i_mode = atoo(buf);
687 printf(" nlinks = %6u", ip->i_nlinks);
688 if (input(buf, 80)) ip->i_nlinks = atol(buf);
689 printf(" size = %6d", ip->i_size);
690 if (input(buf, 80)) ip->i_size = atol(buf);
691 if (yes("Write this back")) {
692 devwrite(inoblock(ino), inooff(ino), (char *) ip,
693 INODE_SIZE);
694 break;
696 } while (yes("Do you want to change it again"));
700 /* Allocate `nblk' blocks worth of bitmap. */
701 bitchunk_t *allocbitmap(nblk)
702 int nblk;
704 register bitchunk_t *bitmap;
706 bitmap = (bitchunk_t *) alloc((unsigned) nblk, block_size);
707 *bitmap |= 1;
708 return(bitmap);
711 /* Load the bitmap starting at block `bno' from disk. */
712 void loadbitmap(bitmap, bno, nblk)
713 bitchunk_t *bitmap;
714 block_nr bno;
715 int nblk;
717 register int i;
718 register bitchunk_t *p;
720 p = bitmap;
721 for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
722 devread(bno, 0, (char *) p, block_size);
723 *bitmap |= 1;
726 /* Write the bitmap starting at block `bno' to disk. */
727 void dumpbitmap(bitmap, bno, nblk)
728 bitchunk_t *bitmap;
729 block_nr bno;
730 int nblk;
732 register int i;
733 register bitchunk_t *p = bitmap;
735 for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
736 devwrite(bno, 0, (char *) p, block_size);
739 /* Set the bits given by `list' in the bitmap. */
740 void fillbitmap(bitmap, lwb, upb, list)
741 bitchunk_t *bitmap;
742 bit_nr lwb, upb;
743 char **list;
745 register bit_nr bit;
747 if (list == 0) return;
748 while ((bit = getnumber(*list++)) != NO_BIT)
749 if (bit < lwb || bit >= upb) {
750 if (bitmap == spec_imap)
751 printf("inode number %d ", bit);
752 else
753 printf("zone number %d ", bit);
754 printf("out of range (ignored)\n");
755 } else
756 setbit(bitmap, bit - lwb + 1);
759 /* Deallocate the bitmap `p'. */
760 void freebitmap(p)
761 bitchunk_t *p;
763 free((char *) p);
766 /* Get all the bitmaps used by this program. */
767 void getbitmaps()
769 imap = allocbitmap(N_IMAP);
770 zmap = allocbitmap(N_ZMAP);
771 spec_imap = allocbitmap(N_IMAP);
772 spec_zmap = allocbitmap(N_ZMAP);
773 dirmap = allocbitmap(N_IMAP);
776 /* Release all the space taken by the bitmaps. */
777 void putbitmaps()
779 freebitmap(imap);
780 freebitmap(zmap);
781 freebitmap(spec_imap);
782 freebitmap(spec_zmap);
783 freebitmap(dirmap);
786 /* `w1' and `w2' are differing words from two bitmaps that should be
787 * identical. Print what's the matter with them.
789 void chkword(w1, w2, bit, type, n, report, phys)
790 unsigned w1, w2;
791 char *type;
792 bit_nr bit;
793 int *n, *report;
794 bit_nr phys;
796 for (; (w1 | w2); w1 >>= 1, w2 >>= 1, bit++, phys++)
797 if ((w1 ^ w2) & 1 && ++(*n) % MAXPRINT == 0 && *report &&
798 (!repair || automatic || yes("stop this listing")))
799 *report = 0;
800 else {
801 if (*report) {
802 if ((w1 & 1) && !(w2 & 1))
803 printf("%s %d is missing\n", type, bit);
804 else if (!(w1 & 1) && (w2 & 1))
805 printf("%s %d is not free\n", type, bit);
810 /* Check if the given (correct) bitmap is identical with the one that is
811 * on the disk. If not, ask if the disk should be repaired.
813 void chkmap(cmap, dmap, bit, blkno, nblk, type)
814 bitchunk_t *cmap, *dmap;
815 bit_nr bit;
816 block_nr blkno;
817 int nblk;
818 char *type;
820 register bitchunk_t *p = dmap, *q = cmap;
821 int report = 1, nerr = 0;
822 int w = nblk * WORDS_PER_BLOCK;
823 bit_nr phys = 0;
825 printf("Checking %s map. ", type);
826 if(!preen) printf("\n");
827 fflush(stdout);
828 loadbitmap(dmap, blkno, nblk);
829 do {
830 if (*p != *q) chkword(*p, *q, bit, type, &nerr, &report, phys);
831 p++;
832 q++;
833 bit += 8 * sizeof(bitchunk_t);
834 phys += 8 * sizeof(bitchunk_t);
835 } while (--w > 0);
837 if ((!repair || automatic) && !report) printf("etc. ");
838 if (nerr > MAXPRINT || nerr > 10) printf("%d errors found. ", nerr);
839 if (nerr != 0 && yes("install a new map")) dumpbitmap(cmap, blkno, nblk);
840 if (nerr > 0) printf("\n");
843 /* See if the inodes that aren't allocated are cleared. */
844 void chkilist()
846 register ino_t ino = 1;
847 mode_t mode;
849 printf("Checking inode list. ");
850 if(!preen) printf("\n");
851 fflush(stdout);
853 if (!bitset(imap, (bit_nr) ino)) {
854 devread(inoblock(ino), inooff(ino), (char *) &mode,
855 sizeof(mode));
856 if (mode != I_NOT_ALLOC) {
857 printf("mode inode %llu not cleared", ino);
858 if (yes(". clear")) devwrite(inoblock(ino),
859 inooff(ino), nullbuf, INODE_SIZE);
862 while (++ino <= sb.s_ninodes && ino != 0);
863 if(!preen) printf("\n");
866 /* Allocate an array to maintain the inode reference counts in. */
867 void getcount()
869 count = (nlink_t *) alloc((unsigned) (sb.s_ninodes + 1), sizeof(nlink_t));
872 /* The reference count for inode `ino' is wrong. Ask if it should be adjusted. */
873 void counterror(ino_t ino)
875 d_inode inode;
877 if (firstcnterr) {
878 printf("INODE NLINK COUNT\n");
879 firstcnterr = 0;
881 devread(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
882 count[ino] += inode.i_nlinks; /* it was already subtracted; add it back */
883 printf("%5llu %5u %5u", ino, (unsigned) inode.i_nlinks, count[ino]);
884 if (yes(" adjust")) {
885 if ((inode.i_nlinks = count[ino]) == 0) {
886 fatal("internal error (counterror)");
887 inode.i_mode = I_NOT_ALLOC;
888 clrbit(imap, (bit_nr) ino);
890 devwrite(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
894 /* Check if the reference count of the inodes are correct. The array `count'
895 * is maintained as follows: an entry indexed by the inode number is
896 * incremented each time a link is found; when the inode is read the link
897 * count in there is substracted from the corresponding entry in `count'.
898 * Thus, when the whole file system has been traversed, all the entries
899 * should be zero.
901 void chkcount()
903 register ino_t ino;
905 for (ino = 1; ino <= sb.s_ninodes && ino != 0; ino++)
906 if (count[ino] != 0) counterror(ino);
907 if (!firstcnterr) printf("\n");
910 /* Deallocate the `count' array. */
911 void freecount()
913 free((char *) count);
916 /* Print the inode permission bits given by mode and shift. */
917 void printperm(mode_t mode, int shift, int special, int overlay)
919 if (mode >> shift & R_BIT)
920 putchar('r');
921 else
922 putchar('-');
923 if (mode >> shift & W_BIT)
924 putchar('w');
925 else
926 putchar('-');
927 if (mode & special)
928 putchar(overlay);
929 else
930 if (mode >> shift & X_BIT)
931 putchar('x');
932 else
933 putchar('-');
936 /* List the given inode. */
937 void list(ino_t ino, d_inode *ip)
939 if (firstlist) {
940 firstlist = 0;
941 printf(" inode permission link size name\n");
943 printf("%6llu ", ino);
944 switch (ip->i_mode & I_TYPE) {
945 case I_REGULAR: putchar('-'); break;
946 case I_DIRECTORY: putchar('d'); break;
947 case I_CHAR_SPECIAL: putchar('c'); break;
948 case I_BLOCK_SPECIAL: putchar('b'); break;
949 case I_NAMED_PIPE: putchar('p'); break;
950 case I_UNIX_SOCKET: putchar('s'); break;
951 #ifdef I_SYMBOLIC_LINK
952 case I_SYMBOLIC_LINK: putchar('l'); break;
953 #endif
954 default: putchar('?');
956 printperm(ip->i_mode, 6, I_SET_UID_BIT, 's');
957 printperm(ip->i_mode, 3, I_SET_GID_BIT, 's');
958 printperm(ip->i_mode, 0, STICKY_BIT, 't');
959 printf(" %3u ", ip->i_nlinks);
960 switch (ip->i_mode & I_TYPE) {
961 case I_CHAR_SPECIAL:
962 case I_BLOCK_SPECIAL:
963 printf(" %2x,%2x ", major(ip->i_zone[0]), minor(ip->i_zone[0]));
964 break;
965 default: printf("%7d ", ip->i_size);
967 printpath(0, 1);
970 /* Remove an entry from a directory if ok with the user.
971 * Don't name the function remove() - that is owned by ANSI, and chaos results
972 * when it is a macro.
974 int Remove(dir_struct *dp)
976 setbit(spec_imap, (bit_nr) dp->d_inum);
977 if (yes(". remove entry")) {
978 count[dp->d_inum]--;
979 memset((void *) dp, 0, sizeof(dir_struct));
980 return(1);
982 return(0);
985 /* Convert string so that embedded control characters are printable. */
986 void make_printable_name(dst, src, n)
987 register char *dst;
988 register char *src;
989 register int n;
991 register int c;
993 while (--n >= 0 && (c = *src++) != '\0') {
994 if (isprint(c) && c != '\\')
995 *dst++ = c;
996 else {
997 *dst++ = '\\';
998 switch (c) {
999 case '\\':
1000 *dst++ = '\\'; break;
1001 case '\b':
1002 *dst++ = 'b'; break;
1003 case '\f':
1004 *dst++ = 'f'; break;
1005 case '\n':
1006 *dst++ = 'n'; break;
1007 case '\r':
1008 *dst++ = 'r'; break;
1009 case '\t':
1010 *dst++ = 't'; break;
1011 default:
1012 *dst++ = '0' + ((c >> 6) & 03);
1013 *dst++ = '0' + ((c >> 3) & 07);
1014 *dst++ = '0' + (c & 07);
1018 *dst = '\0';
1021 /* See if the `.' or `..' entry is as expected. */
1022 int chkdots(ino_t ino, off_t pos, dir_struct *dp, ino_t exp)
1024 char printable_name[4 * MFS_NAME_MAX + 1];
1026 if (dp->d_inum != exp) {
1027 make_printable_name(printable_name, dp->mfs_d_name,
1028 sizeof(dp->mfs_d_name));
1029 printf("bad %s in ", printable_name);
1030 printpath(1, 0);
1031 printf("%s is linked to %u ", printable_name, dp->d_inum);
1032 printf("instead of %llu)", exp);
1033 setbit(spec_imap, (bit_nr) ino);
1034 setbit(spec_imap, (bit_nr) dp->d_inum);
1035 setbit(spec_imap, (bit_nr) exp);
1036 if (yes(". repair")) {
1037 count[dp->d_inum]--;
1038 dp->d_inum = exp;
1039 count[exp]++;
1040 return(0);
1042 } else if (pos != (dp->mfs_d_name[1] ? DIR_ENTRY_SIZE : 0)) {
1043 make_printable_name(printable_name, dp->mfs_d_name,
1044 sizeof(dp->mfs_d_name));
1045 printf("warning: %s has offset %lld in ", printable_name, pos);
1046 printpath(1, 0);
1047 printf("%s is linked to %u)\n", printable_name, dp->d_inum);
1048 setbit(spec_imap, (bit_nr) ino);
1049 setbit(spec_imap, (bit_nr) dp->d_inum);
1050 setbit(spec_imap, (bit_nr) exp);
1052 return(1);
1055 /* Check the name in a directory entry. */
1056 int chkname(ino_t ino, dir_struct *dp)
1058 register int n = MFS_NAME_MAX + 1;
1059 register char *p = dp->mfs_d_name;
1061 if (*p == '\0') {
1062 printf("null name found in ");
1063 printpath(0, 0);
1064 setbit(spec_imap, (bit_nr) ino);
1065 if (Remove(dp)) return(0);
1067 while (*p != '\0' && --n != 0)
1068 if (*p++ == '/') {
1069 printf("found a '/' in entry of directory ");
1070 printpath(1, 0);
1071 setbit(spec_imap, (bit_nr) ino);
1072 printf("entry = '");
1073 printname(dp->mfs_d_name);
1074 printf("')");
1075 if (Remove(dp)) return(0);
1076 break;
1078 return(1);
1081 /* Check a directory entry. Here the routine `descendtree' is called
1082 * recursively to check the file or directory pointed to by the entry.
1084 int chkentry(ino_t ino, off_t pos, dir_struct *dp)
1086 if (dp->d_inum < ROOT_INODE || dp->d_inum > sb.s_ninodes) {
1087 printf("bad inode found in directory ");
1088 printpath(1, 0);
1089 printf("ino found = %u, ", dp->d_inum);
1090 printf("name = '");
1091 printname(dp->mfs_d_name);
1092 printf("')");
1093 if (yes(". remove entry")) {
1094 memset((void *) dp, 0, sizeof(dir_struct));
1095 return(0);
1097 return(1);
1099 if ((unsigned) count[dp->d_inum] == SHRT_MAX) {
1100 printf("too many links to ino %u\n", dp->d_inum);
1101 printf("discovered at entry '");
1102 printname(dp->mfs_d_name);
1103 printf("' in directory ");
1104 printpath(0, 1);
1105 if (Remove(dp)) return(0);
1107 count[dp->d_inum]++;
1108 if (strcmp(dp->mfs_d_name, ".") == 0) {
1109 ftop->st_presence |= DOT;
1110 return(chkdots(ino, pos, dp, ino));
1112 if (strcmp(dp->mfs_d_name, "..") == 0) {
1113 ftop->st_presence |= DOTDOT;
1114 return(chkdots(ino, pos, dp, ino == ROOT_INODE ? ino :
1115 ftop->st_next->st_dir->d_inum));
1117 if (!chkname(ino, dp)) return(0);
1118 if (bitset(dirmap, (bit_nr) dp->d_inum)) {
1119 printf("link to directory discovered in ");
1120 printpath(1, 0);
1121 printf("name = '");
1122 printname(dp->mfs_d_name);
1123 printf("', dir ino = %u)", dp->d_inum);
1124 return !Remove(dp);
1126 return(descendtree(dp));
1129 /* Check a zone of a directory by checking all the entries in the zone.
1130 * The zone is split up into chunks to not allocate too much stack.
1132 int chkdirzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno)
1134 dir_struct dirblk[CDIRECT];
1135 register dir_struct *dp;
1136 register int n, dirty;
1137 long block= ztob(zno);
1138 register long offset = 0;
1139 register off_t size = 0;
1140 n = SCALE * (NR_DIR_ENTRIES(block_size) / CDIRECT);
1142 do {
1143 devread(block, offset, (char *) dirblk, DIRCHUNK);
1144 dirty = 0;
1145 for (dp = dirblk; dp < &dirblk[CDIRECT]; dp++) {
1146 if (dp->d_inum != NO_ENTRY && !chkentry(ino, pos, dp))
1147 dirty = 1;
1148 pos += DIR_ENTRY_SIZE;
1149 if (dp->d_inum != NO_ENTRY) size = pos;
1151 if (dirty) devwrite(block, offset, (char *) dirblk, DIRCHUNK);
1152 offset += DIRCHUNK;
1153 n--;
1154 } while (n > 0);
1156 if (size > ip->i_size) {
1157 printf("size not updated of directory ");
1158 printpath(2, 0);
1159 if (yes(". extend")) {
1160 setbit(spec_imap, (bit_nr) ino);
1161 ip->i_size = size;
1162 devwrite(inoblock(ino), inooff(ino), (char *) ip, INODE_SIZE);
1165 return(1);
1169 int chksymlinkzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno)
1171 long block;
1172 size_t len;
1173 char target[PATH_MAX+1];
1175 if (ip->i_size > PATH_MAX)
1176 fatal("chksymlinkzone: fsck program inconsistency\n");
1177 block= ztob(zno);
1178 devread(block, 0, target, ip->i_size);
1179 target[ip->i_size]= '\0';
1180 len= strlen(target);
1181 if (len != ip->i_size)
1183 printf("bad size in symbolic link (%d instead of %d) ",
1184 ip->i_size, len);
1185 printpath(2, 0);
1186 if (yes(". update")) {
1187 setbit(spec_imap, (bit_nr) ino);
1188 ip->i_size = len;
1189 devwrite(inoblock(ino), inooff(ino),
1190 (char *) ip, INODE_SIZE);
1193 return 1;
1196 /* There is something wrong with the given zone. Print some details. */
1197 void errzone(mess, zno, level, pos)
1198 char *mess;
1199 zone_nr zno;
1200 int level;
1201 off_t pos;
1203 printf("%s zone in ", mess);
1204 printpath(1, 0);
1205 printf("zno = %d, type = ", zno);
1206 switch (level) {
1207 case 0: printf("DATA"); break;
1208 case 1: printf("SINGLE INDIRECT"); break;
1209 case 2: printf("DOUBLE INDIRECT"); break;
1210 default: printf("VERY INDIRECT");
1212 printf(", pos = %lld)\n", pos);
1215 /* Found the given zone in the given inode. Check it, and if ok, mark it
1216 * in the zone bitmap.
1218 int markzone(zno, level, pos)
1219 zone_nr zno;
1220 int level;
1221 off_t pos;
1223 register bit_nr bit = (bit_nr) zno - FIRST + 1;
1225 ztype[level]++;
1226 if (zno < FIRST || zno >= sb.s_zones) {
1227 errzone("out-of-range", zno, level, pos);
1228 return(0);
1230 if (bitset(zmap, bit)) {
1231 setbit(spec_zmap, bit);
1232 errzone("duplicate", zno, level, pos);
1233 return(0);
1235 nfreezone--;
1236 if (bitset(spec_zmap, bit)) errzone("found", zno, level, pos);
1237 setbit(zmap, bit);
1238 return(1);
1241 /* Check an indirect zone by checking all of its entries.
1242 * The zone is split up into chunks to not allocate too much stack.
1244 int chkindzone(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)
1246 zone_nr indirect[CINDIR];
1247 register int n = NR_INDIRECTS / CINDIR;
1248 long block= ztob(zno);
1249 register long offset = 0;
1251 do {
1252 devread(block, offset, (char *) indirect, INDCHUNK);
1253 if (!chkzones(ino, ip, pos, indirect, CINDIR, level - 1)) return(0);
1254 offset += INDCHUNK;
1255 } while (--n && *pos < ip->i_size);
1256 return(1);
1259 /* Return the size of a gap in the file, represented by a null zone number
1260 * at some level of indirection.
1262 off_t jump(level)
1263 int level;
1265 off_t power = ZONE_SIZE;
1267 if (level != 0) do
1268 power *= NR_INDIRECTS;
1269 while (--level);
1270 return(power);
1273 /* Check a zone, which may be either a normal data zone, a directory zone,
1274 * or an indirect zone.
1276 int zonechk(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)
1278 if (level == 0) {
1279 if ((ip->i_mode & I_TYPE) == I_DIRECTORY &&
1280 !chkdirzone(ino, ip, *pos, zno))
1281 return(0);
1282 if ((ip->i_mode & I_TYPE) == I_SYMBOLIC_LINK &&
1283 !chksymlinkzone(ino, ip, *pos, zno))
1284 return(0);
1285 *pos += ZONE_SIZE;
1286 return(1);
1287 } else
1288 return chkindzone(ino, ip, pos, zno, level);
1291 /* Check a list of zones given by `zlist'. */
1292 int chkzones(ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist,
1293 int len, int level)
1295 register int ok = 1, i;
1297 /* The check on the position in the next loop is commented out, since FS
1298 * now requires valid zone numbers in each level that is necessary and FS
1299 * always deleted all the zones in the double indirect block.
1301 for (i = 0; i < len /* && *pos < ip->i_size */ ; i++)
1302 if (zlist[i] == NO_ZONE)
1303 *pos += jump(level);
1304 else if (!markzone(zlist[i], level, *pos)) {
1305 *pos += jump(level);
1306 ok = 0;
1307 } else if (!zonechk(ino, ip, pos, zlist[i], level))
1308 ok = 0;
1309 return(ok);
1312 /* Check a file or a directory. */
1313 int chkfile(ino_t ino, d_inode *ip)
1315 register int ok, i, level;
1316 off_t pos = 0;
1318 ok = chkzones(ino, ip, &pos, &ip->i_zone[0], NR_DZONE_NUM, 0);
1319 for (i = NR_DZONE_NUM, level = 1; i < NR_ZONE_NUMS; i++, level++)
1320 ok &= chkzones(ino, ip, &pos, &ip->i_zone[i], 1, level);
1321 return(ok);
1324 /* Check a directory by checking the contents. Check if . and .. are present. */
1325 int chkdirectory(ino_t ino, d_inode *ip)
1327 register int ok;
1329 setbit(dirmap, (bit_nr) ino);
1330 ok = chkfile(ino, ip);
1331 if (!(ftop->st_presence & DOT)) {
1332 printf(". missing in ");
1333 printpath(2, 1);
1334 ok = 0;
1336 if (!(ftop->st_presence & DOTDOT)) {
1337 printf(".. missing in ");
1338 printpath(2, 1);
1339 ok = 0;
1341 return(ok);
1344 #ifdef I_SYMBOLIC_LINK
1346 /* Check the validity of a symbolic link. */
1347 int chklink(ino_t ino, d_inode *ip)
1349 int ok;
1351 ok = chkfile(ino, ip);
1352 if (ip->i_size <= 0 || ip->i_size > block_size) {
1353 if (ip->i_size == 0)
1354 printf("empty symbolic link ");
1355 else
1356 printf("symbolic link too large (size %d) ", ip->i_size);
1357 printpath(2, 1);
1358 ok = 0;
1360 return(ok);
1363 #endif
1365 /* Check the validity of a special file. */
1366 int chkspecial(ino_t ino, d_inode *ip)
1368 int i, ok;
1370 ok = 1;
1371 if ((dev_t) ip->i_zone[0] == NO_DEV) {
1372 printf("illegal device number %d for special file ", ip->i_zone[0]);
1373 printpath(2, 1);
1374 ok = 0;
1377 /* FS will not use the remaining "zone numbers" but 1.6.11++ will panic if
1378 * they are nonzero, since this should not happen.
1380 for (i = 1; i < NR_ZONE_NUMS; i++)
1381 if (ip->i_zone[i] != NO_ZONE) {
1382 printf("nonzero zone number %d for special file ",
1383 ip->i_zone[i]);
1384 printpath(2, 1);
1385 ok = 0;
1387 return(ok);
1390 /* Check the mode and contents of an inode. */
1391 int chkmode(ino_t ino, d_inode *ip)
1393 switch (ip->i_mode & I_TYPE) {
1394 case I_REGULAR:
1395 nregular++;
1396 return chkfile(ino, ip);
1397 case I_DIRECTORY:
1398 ndirectory++;
1399 return chkdirectory(ino, ip);
1400 case I_BLOCK_SPECIAL:
1401 nblkspec++;
1402 return chkspecial(ino, ip);
1403 case I_CHAR_SPECIAL:
1404 ncharspec++;
1405 return chkspecial(ino, ip);
1406 case I_NAMED_PIPE:
1407 npipe++;
1408 return chkfile(ino, ip);
1409 case I_UNIX_SOCKET:
1410 nsock++;
1411 return chkfile(ino, ip);
1412 #ifdef I_SYMBOLIC_LINK
1413 case I_SYMBOLIC_LINK:
1414 nsyml++;
1415 return chklink(ino, ip);
1416 #endif
1417 default:
1418 nbadinode++;
1419 printf("bad mode of ");
1420 printpath(1, 0);
1421 printf("mode = %o)", ip->i_mode);
1422 return(0);
1426 /* Check an inode. */
1427 int chkinode(ino_t ino, d_inode *ip)
1429 if (ino == ROOT_INODE && (ip->i_mode & I_TYPE) != I_DIRECTORY) {
1430 printf("root inode is not a directory ");
1431 printf("(ino = %llu, mode = %o)\n", ino, ip->i_mode);
1432 fatal("");
1434 if (ip->i_nlinks == 0) {
1435 printf("link count zero of ");
1436 printpath(2, 0);
1437 return(0);
1439 nfreeinode--;
1440 setbit(imap, (bit_nr) ino);
1441 if ((unsigned) ip->i_nlinks > SHRT_MAX) {
1442 printf("link count too big in ");
1443 printpath(1, 0);
1444 printf("cnt = %u)\n", (unsigned) ip->i_nlinks);
1445 count[ino] -= SHRT_MAX;
1446 setbit(spec_imap, (bit_nr) ino);
1447 } else {
1448 count[ino] -= (unsigned) ip->i_nlinks;
1450 return chkmode(ino, ip);
1453 /* Check the directory entry pointed to by dp, by checking the inode. */
1454 int descendtree(dp)
1455 dir_struct *dp;
1457 d_inode inode;
1458 register ino_t ino = dp->d_inum;
1459 register int visited;
1460 struct stack stk;
1462 stk.st_dir = dp;
1463 stk.st_next = ftop;
1464 ftop = &stk;
1465 if (bitset(spec_imap, (bit_nr) ino)) {
1466 printf("found inode %llu: ", ino);
1467 printpath(0, 1);
1469 visited = bitset(imap, (bit_nr) ino);
1470 if (!visited || listing) {
1471 devread(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
1472 if (listing) list(ino, &inode);
1473 if (!visited && !chkinode(ino, &inode)) {
1474 setbit(spec_imap, (bit_nr) ino);
1475 if (yes("remove")) {
1476 count[ino] += inode.i_nlinks - 1;
1477 clrbit(imap, (bit_nr) ino);
1478 devwrite(inoblock(ino), inooff(ino),
1479 nullbuf, INODE_SIZE);
1480 memset((void *) dp, 0, sizeof(dir_struct));
1481 ftop = ftop->st_next;
1482 return(0);
1486 ftop = ftop->st_next;
1487 return(1);
1490 /* Check the file system tree. */
1491 void chktree()
1493 dir_struct dir;
1495 nfreeinode = sb.s_ninodes;
1496 nfreezone = N_DATA;
1497 dir.d_inum = ROOT_INODE;
1498 dir.mfs_d_name[0] = 0;
1499 if (!descendtree(&dir)) fatal("bad root inode");
1500 putchar('\n');
1503 /* Print the totals of all the objects found. */
1504 void printtotal()
1506 if(preen) {
1507 printf("%d files, %d directories, %d free inodes, %ld free zones\n",
1508 nregular, ndirectory, nfreeinode, nfreezone);
1509 return;
1512 printf("blocksize = %5d ", block_size);
1513 printf("zonesize = %5d\n", ZONE_SIZE);
1514 printf("\n");
1515 pr("%8u Regular file%s\n", nregular, "", "s");
1516 pr("%8u Director%s\n", ndirectory, "y", "ies");
1517 pr("%8u Block special file%s\n", nblkspec, "", "s");
1518 pr("%8u Character special file%s\n", ncharspec, "", "s");
1519 if (nbadinode != 0) pr("%6u Bad inode%s\n", nbadinode, "", "s");
1520 pr("%8u Free inode%s\n", nfreeinode, "", "s");
1521 pr("%8u Named pipe%s\n", npipe, "", "s");
1522 pr("%8u Unix socket%s\n", nsock, "", "s");
1523 pr("%8u Symbolic link%s\n", nsyml, "", "s");
1524 /* Don't print some fields.
1525 printf("\n");
1526 pr("%8u Data zone%s\n", ztype[0], "", "s");
1527 pr("%8u Single indirect zone%s\n", ztype[1], "", "s");
1528 pr("%8u Double indirect zone%s\n", ztype[2], "", "s");
1530 lpr("%8ld Free zone%s\n", nfreezone, "", "s");
1533 /* Check the device which name is given by `f'. The inodes listed by `clist'
1534 * should be listed separately, and the inodes listed by `ilist' and the zones
1535 * listed by `zlist' should be watched for while checking the file system.
1538 void chkdev(f, clist, ilist, zlist)
1539 char *f, **clist, **ilist, **zlist;
1541 if (automatic) repair = 1;
1542 fsck_device = f;
1543 initvars();
1545 devopen();
1547 rw_super(SUPER_GET);
1549 if(block_size < SUPER_BLOCK_BYTES)
1550 fatal("funny block size");
1552 if(!(rwbuf = malloc(block_size))) fatal("couldn't allocate fs buf (1)");
1553 if(!(nullbuf = malloc(block_size))) fatal("couldn't allocate fs buf (2)");
1554 memset(nullbuf, 0, block_size);
1556 chksuper();
1558 if(markdirty) {
1559 if(sb.s_flags & MFSFLAG_CLEAN) {
1560 sb.s_flags &= ~MFSFLAG_CLEAN;
1561 rw_super(SUPER_PUT);
1562 printf("\n----- FILE SYSTEM MARKED DIRTY -----\n\n");
1563 } else {
1564 printf("Filesystem is already dirty.\n");
1568 /* If preening, skip fsck if clean flag is on. */
1569 if(preen) {
1570 if(sb.s_flags & MFSFLAG_CLEAN) {
1571 printf("%s: clean\n", f);
1572 return;
1574 printf("%s: dirty, performing fsck\n", f);
1577 lsi(clist);
1579 getbitmaps();
1581 fillbitmap(spec_imap, (bit_nr) 1, (bit_nr) sb.s_ninodes + 1, ilist);
1582 fillbitmap(spec_zmap, (bit_nr) FIRST, (bit_nr) sb.s_zones, zlist);
1584 getcount();
1585 chktree();
1586 chkmap(zmap, spec_zmap, (bit_nr) FIRST - 1, BLK_ZMAP, N_ZMAP, "zone");
1587 chkcount();
1588 chkmap(imap, spec_imap, (bit_nr) 0, BLK_IMAP, N_IMAP, "inode");
1589 chkilist();
1590 if(preen) printf("\n");
1591 printtotal();
1593 putbitmaps();
1594 freecount();
1596 if (changed) printf("\n----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n");
1598 /* If we were told to repair the FS, and the user never stopped us from
1599 * doing it, and the FS wasn't marked clean, we can mark the FS as clean.
1600 * If we were stopped from repairing, tell user about it.
1602 if(repair && !(sb.s_flags & MFSFLAG_CLEAN)) {
1603 if(notrepaired) {
1604 printf("\n----- FILE SYSTEM STILL DIRTY -----\n\n");
1605 } else {
1606 sync(); /* update FS on disk before clean flag */
1607 sb.s_flags |= MFSFLAG_CLEAN;
1608 rw_super(SUPER_PUT);
1609 printf("\n----- FILE SYSTEM MARKED CLEAN -----\n\n");
1613 devclose();
1616 int main(argc, argv)
1617 int argc;
1618 char **argv;
1620 register char **clist = 0, **ilist = 0, **zlist = 0;
1621 int badflag = 0;
1623 register int devgiven = 0;
1624 register char *arg;
1626 if ((1 << BITSHIFT) != 8 * sizeof(bitchunk_t)) {
1627 printf("Fsck was compiled with the wrong BITSHIFT!\n");
1628 exit(FSCK_EXIT_CHECK_FAILED);
1631 sync();
1632 prog = *argv++;
1633 while ((arg = *argv++) != 0)
1634 if (arg[0] == '-' && arg[1] != 0 && arg[2] == 0) switch (arg[1]) {
1635 case 'd': markdirty = 1; break;
1636 case 'p': preen = repair = automatic = 1; break;
1637 case 'y':
1638 case 'a': automatic ^= 1; break;
1639 case 'c':
1640 clist = getlist(&argv, "inode");
1641 break;
1642 case 'i':
1643 ilist = getlist(&argv, "inode");
1644 break;
1645 case 'z':
1646 zlist = getlist(&argv, "zone");
1647 break;
1648 case 'r': repair ^= 1; break;
1649 case 'l': listing ^= 1; break;
1650 case 's': listsuper ^= 1; break;
1651 case 'f': break;
1652 default:
1653 printf("%s: unknown flag '%s'\n", prog, arg);
1654 badflag = 1;
1656 else {
1657 chkdev(arg, clist, ilist, zlist);
1658 clist = 0;
1659 ilist = 0;
1660 zlist = 0;
1661 devgiven = 1;
1663 if (!devgiven || badflag) {
1664 printf("Usage: fsck [-dyfpacilrsz] file\n");
1665 exit(FSCK_EXIT_USAGE);
1667 return(0);
1670 void panic(char *fmt, ...)
1672 fprintf(stderr, "%s\n", fmt);
1673 exit(1);