coverity appeasement
[minix.git] / commands / fsck.mfs / fsck.c
blobfaa23099094bd76ed3eb32e42124743f4610db4f
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/u64.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 <a.out.h>
58 #include <tools.h>
59 #include <dirent.h>
61 #include "exitvalues.h"
63 #undef N_DATA
65 unsigned int fs_version = 2, block_size = 0;
67 #define BITSHIFT 5 /* = log2(#bits(int)) */
69 #define MAXPRINT 80 /* max. number of error lines in chkmap */
70 #define CINDIR 128 /* number of indirect zno's read at a time */
71 #define CDIRECT 1 /* number of dir entries read at a time */
73 /* Macros for handling bitmaps. Now bit_t is long, these are bulky and the
74 * type demotions produce a lot of lint. The explicit demotion in POWEROFBIT
75 * is for efficiency and assumes 2's complement ints. Lint should be clever
76 * enough not to warn about it since BITMASK is small, but isn't. (It would
77 * be easier to get right if bit_t was was unsigned (long) since then there
78 * would be no danger from wierd sign representations. Lint doesn't know
79 * we only use non-negative bit numbers.) There will usually be an implicit
80 * demotion when WORDOFBIT is used as an array index. This should be safe
81 * since memory for bitmaps will run out first.
83 #define BITMASK ((1 << BITSHIFT) - 1)
84 #define WORDOFBIT(b) ((b) >> BITSHIFT)
85 #define POWEROFBIT(b) (1 << ((int) (b) & BITMASK))
86 #define setbit(w, b) (w[WORDOFBIT(b)] |= POWEROFBIT(b))
87 #define clrbit(w, b) (w[WORDOFBIT(b)] &= ~POWEROFBIT(b))
88 #define bitset(w, b) (w[WORDOFBIT(b)] & POWEROFBIT(b))
90 #define ZONE_CT 360 /* default zones (when making file system) */
91 #define INODE_CT 95 /* default inodes (when making file system) */
93 #include "mfs/super.h"
94 static struct super_block sb;
96 #define STICKY_BIT 01000 /* not defined anywhere else */
98 /* Ztob gives the block address of a zone
99 * btoa64 gives the byte address of a block
101 #define ztob(z) ((block_nr) (z) << sb.s_log_zone_size)
102 #define btoa64(b) (mul64u(b, block_size))
103 #define SCALE ((int) ztob(1)) /* # blocks in a zone */
104 #define FIRST ((zone_nr) sb.s_firstdatazone) /* as the name says */
106 /* # blocks of each type */
107 #define N_IMAP (sb.s_imap_blocks)
108 #define N_ZMAP (sb.s_zmap_blocks)
109 #define N_ILIST ((sb.s_ninodes+INODES_PER_BLOCK-1) / INODES_PER_BLOCK)
110 #define N_DATA (sb.s_zones - FIRST)
112 /* Block address of each type */
113 #define OFFSET_SUPER_BLOCK SUPER_BLOCK_BYTES
114 #define BLK_IMAP 2
115 #define BLK_ZMAP (BLK_IMAP + N_IMAP)
116 #define BLK_ILIST (BLK_ZMAP + N_ZMAP)
117 #define BLK_FIRST ztob(FIRST)
118 #define ZONE_SIZE ((int) ztob(block_size))
119 #define NLEVEL (NR_ZONE_NUMS - NR_DZONE_NUM + 1)
121 /* Byte address of a zone */
122 #define INDCHUNK ((int) (CINDIR * ZONE_NUM_SIZE))
123 #define DIRCHUNK ((int) (CDIRECT * DIR_ENTRY_SIZE))
125 char *prog, *fsck_device; /* program name (fsck), device name */
126 int firstcnterr; /* is this the first inode ref cnt error? */
127 bitchunk_t *imap, *spec_imap; /* inode bit maps */
128 bitchunk_t *zmap, *spec_zmap; /* zone bit maps */
129 bitchunk_t *dirmap; /* directory (inode) bit map */
130 char *rwbuf; /* one block buffer cache */
131 block_nr thisblk; /* block in buffer cache */
132 char *nullbuf; /* null buffer */
133 nlink_t *count; /* inode count */
134 int changed; /* has the diskette been written to? */
135 struct stack {
136 dir_struct *st_dir;
137 struct stack *st_next;
138 char st_presence;
139 } *ftop;
141 int dev; /* file descriptor of the device */
143 #define DOT 1
144 #define DOTDOT 2
146 /* Counters for each type of inode/zone. */
147 int nfreeinode, nregular, ndirectory, nblkspec, ncharspec, nbadinode;
148 int nsock, npipe, nsyml, ztype[NLEVEL];
149 long nfreezone;
151 int repair, notrepaired = 0, automatic, listing, listsuper; /* flags */
152 int preen = 0, markdirty = 0;
153 int firstlist; /* has the listing header been printed? */
154 unsigned part_offset; /* sector offset for this partition */
155 char answer[] = "Answer questions with y or n. Then hit RETURN";
157 int main(int argc, char **argv);
158 void initvars(void);
159 void fatal(char *s);
160 int eoln(int c);
161 int yes(char *question);
162 int atoo(char *s);
163 int input(char *buf, int size);
164 char *alloc(unsigned nelem, unsigned elsize);
165 void printname(char *s);
166 void printrec(struct stack *sp);
167 void printpath(int mode, int nlcr);
168 void devopen(void);
169 void devclose(void);
170 void devio(block_nr bno, int dir);
171 void devread(long block, long offset, char *buf, int size);
172 void devwrite(long block, long offset, char *buf, int size);
173 void pr(char *fmt, int cnt, char *s, char *p);
174 void lpr(char *fmt, long cnt, char *s, char *p);
175 bit_nr getnumber(char *s);
176 char **getlist(char ***argv, char *type);
177 void lsuper(void);
178 #define SUPER_GET 0
179 #define SUPER_PUT 1
180 void rw_super(int mode);
181 void chksuper(void);
182 void lsi(char **clist);
183 bitchunk_t *allocbitmap(int nblk);
184 void loadbitmap(bitchunk_t *bitmap, block_nr bno, int nblk);
185 void dumpbitmap(bitchunk_t *bitmap, block_nr bno, int nblk);
186 void fillbitmap(bitchunk_t *bitmap, bit_nr lwb, bit_nr upb, char
187 **list);
188 void freebitmap(bitchunk_t *p);
189 void getbitmaps(void);
190 void putbitmaps(void);
191 void chkword(unsigned w1, unsigned w2, bit_nr bit, char *type, int *n,
192 int *report, bit_t);
193 void chkmap(bitchunk_t *cmap, bitchunk_t *dmap, bit_nr bit, block_nr
194 blkno, int nblk, char *type);
195 void chkilist(void);
196 void getcount(void);
197 void counterror(ino_t ino);
198 void chkcount(void);
199 void freecount(void);
200 void printperm(mode_t mode, int shift, int special, int overlay);
201 void list(ino_t ino, d_inode *ip);
202 int Remove(dir_struct *dp);
203 void make_printable_name(char *dst, char *src, int n);
204 int chkdots(ino_t ino, off_t pos, dir_struct *dp, ino_t exp);
205 int chkname(ino_t ino, dir_struct *dp);
206 int chkentry(ino_t ino, off_t pos, dir_struct *dp);
207 int chkdirzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno);
208 int chksymlinkzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno);
209 void errzone(char *mess, zone_nr zno, int level, off_t pos);
210 int markzone(zone_nr zno, int level, off_t pos);
211 int chkindzone(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int
212 level);
213 off_t jump(int level);
214 int zonechk(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level);
215 int chkzones(ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist, int
216 len, int level);
217 int chkfile(ino_t ino, d_inode *ip);
218 int chkdirectory(ino_t ino, d_inode *ip);
219 int chklink(ino_t ino, d_inode *ip);
220 int chkspecial(ino_t ino, d_inode *ip);
221 int chkmode(ino_t ino, d_inode *ip);
222 int chkinode(ino_t ino, d_inode *ip);
223 int descendtree(dir_struct *dp);
224 void chktree(void);
225 void printtotal(void);
226 void chkdev(char *f, char **clist, char **ilist, char **zlist);
228 /* Initialize the variables used by this program. */
229 void initvars()
231 register level;
233 nregular = ndirectory = nblkspec = ncharspec =
234 nbadinode = nsock = npipe = nsyml = 0;
235 for (level = 0; level < NLEVEL; level++) ztype[level] = 0;
236 changed = 0;
237 thisblk = NO_BLOCK;
238 firstlist = 1;
239 firstcnterr = 1;
242 /* Print the string `s' and exit. */
243 void fatal(s)
244 char *s;
246 printf("%s\nfatal\n", s);
247 exit(FSCK_EXIT_CHECK_FAILED);
250 /* Test for end of line. */
251 int eoln(c)
252 int c;
254 return(c == EOF || c == '\n' || c == '\r');
257 /* Ask a question and get the answer unless automatic is set. */
258 int yes(question)
259 char *question;
261 register int c, answerchar;
262 static int note = 0;
263 int yes;
265 if (!repair) {
266 printf("\n");
267 return(0);
269 printf("%s? ", question);
270 if(!note) { printf("(y=yes, n=no, q=quit, A=for yes to all) "); note = 1; }
271 if (automatic) {
272 printf("yes\n");
273 return(1);
275 fflush(stdout);
276 if ((c = answerchar = getchar()) == 'q' || c == 'Q') exit(FSCK_EXIT_CHECK_FAILED);
277 if(c == 'A') { automatic = 1; c = 'y'; }
278 while (!eoln(c)) c = getchar();
279 yes = !(answerchar == 'n' || answerchar == 'N');
280 if(!yes) notrepaired = 1;
281 return yes;
284 /* Convert string to integer. Representation is octal. */
285 int atoo(s)
286 register char *s;
288 register int n = 0;
290 while ('0' <= *s && *s < '8') {
291 n <<= 3;
292 n += *s++ - '0';
294 return n;
297 /* If repairing the file system, print a prompt and get a string from user. */
298 int input(buf, size)
299 char *buf;
300 int size;
302 register char *p = buf;
304 printf("\n");
305 if (repair) {
306 printf("--> ");
307 fflush(stdout);
308 while (--size) {
309 *p = getchar();
310 if (eoln(*p)) {
311 *p = 0;
312 return(p > buf);
314 p++;
316 *p = 0;
317 while (!eoln(getchar()));
318 return(1);
320 return(0);
323 /* Allocate some memory and zero it. */
324 char *alloc(nelem, elsize)
325 unsigned nelem, elsize;
327 char *p;
329 if ((p = (char *)malloc((size_t)nelem * elsize)) == 0) {
330 fprintf(stderr, "Tried to allocate %dkB\n",
331 nelem*elsize/1024);
332 fatal("out of memory");
334 memset((void *) p, 0, (size_t)nelem * elsize);
335 return(p);
338 /* Print the name in a directory entry. */
339 void printname(s)
340 char *s;
342 register n = MFS_NAME_MAX;
343 int c;
345 do {
346 if ((c = *s) == 0) break;
347 if (!isprint(c)) c = '?';
348 putchar(c);
349 s++;
350 } while (--n);
353 /* Print the pathname given by a linked list pointed to by `sp'. The
354 * names are in reverse order.
356 void printrec(struct stack *sp)
358 if (sp->st_next != 0) {
359 printrec(sp->st_next);
360 putchar('/');
361 printname(sp->st_dir->mfs_d_name);
365 /* Print the current pathname. */
366 void printpath(mode, nlcr)
367 int mode;
368 int nlcr;
370 if (ftop->st_next == 0)
371 putchar('/');
372 else
373 printrec(ftop);
374 switch (mode) {
375 case 1:
376 printf(" (ino = %u, ", ftop->st_dir->d_inum);
377 break;
378 case 2:
379 printf(" (ino = %u)", ftop->st_dir->d_inum);
380 break;
382 if (nlcr) printf("\n");
385 /* Open the device. */
386 void devopen()
388 if ((dev = open(fsck_device,
389 (repair || markdirty) ? O_RDWR : O_RDONLY)) < 0) {
390 perror(fsck_device);
391 fatal("couldn't open device to fsck");
395 /* Close the device. */
396 void devclose()
398 if (close(dev) != 0) {
399 perror("close");
400 fatal("");
404 /* Read or write a block. */
405 void devio(bno, dir)
406 block_nr bno;
407 int dir;
409 int r;
411 if(!block_size) fatal("devio() with unknown block size");
412 if (dir == READING && bno == thisblk) return;
413 thisblk = bno;
415 #if 0
416 printf("%s at block %5d\n", dir == READING ? "reading " : "writing", bno);
417 #endif
418 r= lseek64(dev, btoa64(bno), SEEK_SET, NULL);
419 if (r != 0)
420 fatal("lseek64 failed");
421 if (dir == READING) {
422 if (read(dev, rwbuf, block_size) == block_size)
423 return;
424 } else {
425 if (write(dev, rwbuf, block_size) == block_size)
426 return;
429 printf("%s: can't %s block %ld (error = 0x%x)\n", prog,
430 dir == READING ? "read" : "write", (long) bno, errno);
431 if (dir == READING) {
432 printf("Continuing with a zero-filled block.\n");
433 memset(rwbuf, 0, block_size);
434 return;
436 fatal("");
439 /* Read `size' bytes from the disk starting at block 'block' and
440 * byte `offset'.
442 void devread(block, offset, buf, size)
443 long block;
444 long offset;
445 char *buf;
446 int size;
448 if(!block_size) fatal("devread() with unknown block size");
449 if (offset >= block_size)
451 block += offset/block_size;
452 offset %= block_size;
454 devio(block, READING);
455 memmove(buf, &rwbuf[offset], size);
458 /* Write `size' bytes to the disk starting at block 'block' and
459 * byte `offset'.
461 void devwrite(block, offset, buf, size)
462 long block;
463 long offset;
464 char *buf;
465 int size;
467 if(!block_size) fatal("devwrite() with unknown block size");
468 if (!repair) fatal("internal error (devwrite)");
469 if (offset >= block_size)
471 block += offset/block_size;
472 offset %= block_size;
474 if (size != block_size) devio(block, READING);
475 memmove(&rwbuf[offset], buf, size);
476 devio(block, WRITING);
477 changed = 1;
480 /* Print a string with either a singular or a plural pronoun. */
481 void pr(fmt, cnt, s, p)
482 char *fmt, *s, *p;
483 int cnt;
485 printf(fmt, cnt, cnt == 1 ? s : p);
488 /* Same as above, but with a long argument */
489 void lpr(fmt, cnt, s, p)
490 char *fmt, *s, *p;
491 long cnt;
493 printf(fmt, cnt, cnt == 1 ? s : p);
496 /* Convert string to number. */
497 bit_nr getnumber(s)
498 register char *s;
500 register bit_nr n = 0;
502 if (s == NULL)
503 return NO_BIT;
504 while (isdigit(*s))
505 n = (n << 1) + (n << 3) + *s++ - '0';
506 return (*s == '\0') ? n : NO_BIT;
509 /* See if the list pointed to by `argv' contains numbers. */
510 char **getlist(argv, type)
511 char ***argv, *type;
513 register char **list = *argv;
514 register empty = 1;
516 while (getnumber(**argv) != NO_BIT) {
517 (*argv)++;
518 empty = 0;
520 if (empty) {
521 printf("warning: no %s numbers given\n", type);
522 return(NULL);
524 return(list);
527 /* Make a listing of the super block. If `repair' is set, ask the user
528 * for changes.
530 void lsuper()
532 char buf[80];
534 do {
535 /* Most of the following atol's enrage lint, for good reason. */
536 printf("ninodes = %u", sb.s_ninodes);
537 if (input(buf, 80)) sb.s_ninodes = atol(buf);
538 printf("nzones = %ld", sb.s_zones);
539 if (input(buf, 80)) sb.s_zones = atol(buf);
540 printf("imap_blocks = %u", sb.s_imap_blocks);
541 if (input(buf, 80)) sb.s_imap_blocks = atol(buf);
542 printf("zmap_blocks = %u", sb.s_zmap_blocks);
543 if (input(buf, 80)) sb.s_zmap_blocks = atol(buf);
544 printf("firstdatazone = %u", sb.s_firstdatazone_old);
545 if (input(buf, 80)) sb.s_firstdatazone_old = atol(buf);
546 printf("log_zone_size = %u", sb.s_log_zone_size);
547 if (input(buf, 80)) sb.s_log_zone_size = atol(buf);
548 printf("maxsize = %ld", sb.s_max_size);
549 if (input(buf, 80)) sb.s_max_size = atol(buf);
550 printf("block size = %ld", sb.s_block_size);
551 if (input(buf, 80)) sb.s_block_size = atol(buf);
552 if (yes("ok now")) {
553 devwrite(0, OFFSET_SUPER_BLOCK, (char *) &sb, sizeof(sb));
554 return;
556 printf("flags = ");
557 if(sb.s_flags & MFSFLAG_CLEAN) printf("CLEAN "); else printf("DIRTY ");
558 printf("\n");
559 } while (yes("Do you want to try again"));
560 if (repair) exit(FSCK_EXIT_OK);
563 /* Get the super block from either disk or user. Do some initial checks. */
564 void rw_super(int put)
566 if(lseek(dev, OFFSET_SUPER_BLOCK, SEEK_SET) < 0) {
567 perror("lseek");
568 fatal("couldn't seek to super block.");
570 if(put == SUPER_PUT) {
571 if(write(dev, &sb, sizeof(sb)) != sizeof(sb)) {
572 fatal("couldn't write super block.");
574 return;
576 if(read(dev, &sb, sizeof(sb)) != sizeof(sb)) {
577 fatal("couldn't read super block.");
579 if (listsuper) lsuper();
580 if (sb.s_magic == SUPER_MAGIC) fatal("Cannot handle V1 file systems");
581 if (sb.s_magic == SUPER_V2) {
582 fs_version = 2;
583 block_size = /* STATIC_BLOCK_SIZE */ 8192;
584 } else if(sb.s_magic == SUPER_V3) {
585 fs_version = 3;
586 block_size = sb.s_block_size;
587 } else {
588 fatal("bad magic number in super block");
590 if (sb.s_ninodes <= 0) fatal("no inodes");
591 if (sb.s_zones <= 0) fatal("no zones");
592 if (sb.s_imap_blocks <= 0) fatal("no imap");
593 if (sb.s_zmap_blocks <= 0) fatal("no zmap");
594 if (sb.s_firstdatazone != 0 && sb.s_firstdatazone <= 4)
595 fatal("first data zone too small");
596 if (sb.s_log_zone_size < 0) fatal("zone size < block size");
597 if (sb.s_max_size <= 0) {
598 printf("warning: invalid max file size %ld\n", sb.s_max_size);
599 sb.s_max_size = LONG_MAX;
603 /* Check the super block for reasonable contents. */
604 void chksuper()
606 register n;
607 register off_t maxsize;
609 n = bitmapsize((bit_t) sb.s_ninodes + 1, block_size);
610 if (sb.s_magic != SUPER_V2 && sb.s_magic != SUPER_V3)
611 fatal("bad magic number in super block");
612 if (sb.s_imap_blocks < n) {
613 printf("need %d bocks for inode bitmap; only have %d\n",
614 n, sb.s_imap_blocks);
615 fatal("too few imap blocks");
617 if (sb.s_imap_blocks != n) {
618 pr("warning: expected %d imap_block%s", n, "", "s");
619 printf(" instead of %d\n", sb.s_imap_blocks);
621 n = bitmapsize((bit_t) sb.s_zones, block_size);
622 if (sb.s_zmap_blocks < n) fatal("too few zmap blocks");
623 if (sb.s_zmap_blocks != n) {
624 pr("warning: expected %d zmap_block%s", n, "", "s");
625 printf(" instead of %d\n", sb.s_zmap_blocks);
627 if (sb.s_log_zone_size >= 8 * sizeof(block_nr))
628 fatal("log_zone_size too large");
629 if (sb.s_log_zone_size > 8) printf("warning: large log_zone_size (%d)\n",
630 sb.s_log_zone_size);
631 sb.s_firstdatazone = (BLK_ILIST + N_ILIST + SCALE - 1) >> sb.s_log_zone_size;
632 if (sb.s_firstdatazone_old != 0) {
633 if (sb.s_firstdatazone_old >= sb.s_zones)
634 fatal("first data zone too large");
635 if (sb.s_firstdatazone_old < sb.s_firstdatazone)
636 fatal("first data zone too small");
637 if (sb.s_firstdatazone_old != sb.s_firstdatazone) {
638 printf("warning: expected first data zone to be %u ",
639 sb.s_firstdatazone);
640 printf("instead of %u\n", sb.s_firstdatazone_old);
641 sb.s_firstdatazone = sb.s_firstdatazone_old;
644 maxsize = MAX_FILE_POS;
645 if (((maxsize - 1) >> sb.s_log_zone_size) / block_size >= MAX_ZONES)
646 maxsize = ((long) MAX_ZONES * block_size) << sb.s_log_zone_size;
647 if(maxsize <= 0)
648 maxsize = LONG_MAX;
649 if (sb.s_max_size != maxsize) {
650 printf("warning: expected max size to be %ld ", maxsize);
651 printf("instead of %ld\n", sb.s_max_size);
654 if(sb.s_flags & MFSFLAG_MANDATORY_MASK) {
655 fatal("unsupported feature bits - newer fsck needed");
659 int inoblock(int inn)
661 return div64u(mul64u(inn - 1, INODE_SIZE), block_size) + BLK_ILIST;
664 int inooff(int inn)
666 return rem64u(mul64u(inn - 1, INODE_SIZE), block_size);
669 /* Make a listing of the inodes given by `clist'. If `repair' is set, ask
670 * the user for changes.
672 void lsi(clist)
673 char **clist;
675 register bit_nr bit;
676 register ino_t ino;
677 d_inode inode, *ip = &inode;
678 char buf[80];
680 if (clist == 0) return;
681 while ((bit = getnumber(*clist++)) != NO_BIT) {
682 setbit(spec_imap, bit);
683 ino = bit;
684 do {
685 devread(inoblock(ino), inooff(ino), (char *) ip, INODE_SIZE);
686 printf("inode %u:\n", ino);
687 printf(" mode = %6o", ip->i_mode);
688 if (input(buf, 80)) ip->i_mode = atoo(buf);
689 printf(" nlinks = %6u", ip->i_nlinks);
690 if (input(buf, 80)) ip->i_nlinks = atol(buf);
691 printf(" size = %6ld", ip->i_size);
692 if (input(buf, 80)) ip->i_size = atol(buf);
693 if (yes("Write this back")) {
694 devwrite(inoblock(ino), inooff(ino), (char *) ip,
695 INODE_SIZE);
696 break;
698 } while (yes("Do you want to change it again"));
702 /* Allocate `nblk' blocks worth of bitmap. */
703 bitchunk_t *allocbitmap(nblk)
704 int nblk;
706 register bitchunk_t *bitmap;
708 bitmap = (bitchunk_t *) alloc((unsigned) nblk, block_size);
709 *bitmap |= 1;
710 return(bitmap);
713 /* Load the bitmap starting at block `bno' from disk. */
714 void loadbitmap(bitmap, bno, nblk)
715 bitchunk_t *bitmap;
716 block_nr bno;
717 int nblk;
719 register i;
720 register bitchunk_t *p;
722 p = bitmap;
723 for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
724 devread(bno, 0, (char *) p, block_size);
725 *bitmap |= 1;
728 /* Write the bitmap starting at block `bno' to disk. */
729 void dumpbitmap(bitmap, bno, nblk)
730 bitchunk_t *bitmap;
731 block_nr bno;
732 int nblk;
734 register i;
735 register bitchunk_t *p = bitmap;
737 for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
738 devwrite(bno, 0, (char *) p, block_size);
741 /* Set the bits given by `list' in the bitmap. */
742 void fillbitmap(bitmap, lwb, upb, list)
743 bitchunk_t *bitmap;
744 bit_nr lwb, upb;
745 char **list;
747 register bit_nr bit;
749 if (list == 0) return;
750 while ((bit = getnumber(*list++)) != NO_BIT)
751 if (bit < lwb || bit >= upb) {
752 if (bitmap == spec_imap)
753 printf("inode number %ld ", bit);
754 else
755 printf("zone number %ld ", bit);
756 printf("out of range (ignored)\n");
757 } else
758 setbit(bitmap, bit - lwb + 1);
761 /* Deallocate the bitmap `p'. */
762 void freebitmap(p)
763 bitchunk_t *p;
765 free((char *) p);
768 /* Get all the bitmaps used by this program. */
769 void getbitmaps()
771 imap = allocbitmap(N_IMAP);
772 zmap = allocbitmap(N_ZMAP);
773 spec_imap = allocbitmap(N_IMAP);
774 spec_zmap = allocbitmap(N_ZMAP);
775 dirmap = allocbitmap(N_IMAP);
778 /* Release all the space taken by the bitmaps. */
779 void putbitmaps()
781 freebitmap(imap);
782 freebitmap(zmap);
783 freebitmap(spec_imap);
784 freebitmap(spec_zmap);
785 freebitmap(dirmap);
788 /* `w1' and `w2' are differing words from two bitmaps that should be
789 * identical. Print what's the matter with them.
791 void chkword(w1, w2, bit, type, n, report, phys)
792 unsigned w1, w2;
793 char *type;
794 bit_nr bit;
795 int *n, *report;
796 bit_nr phys;
798 for (; (w1 | w2); w1 >>= 1, w2 >>= 1, bit++, phys++)
799 if ((w1 ^ w2) & 1 && ++(*n) % MAXPRINT == 0 && *report &&
800 (!repair || automatic || yes("stop this listing")))
801 *report = 0;
802 else if (*report)
803 if ((w1 & 1) && !(w2 & 1))
804 printf("%s %ld is missing\n", type, bit);
805 else if (!(w1 & 1) && (w2 & 1))
806 printf("%s %ld is not free\n", type, bit);
809 /* Check if the given (correct) bitmap is identical with the one that is
810 * on the disk. If not, ask if the disk should be repaired.
812 void chkmap(cmap, dmap, bit, blkno, nblk, type)
813 bitchunk_t *cmap, *dmap;
814 bit_nr bit;
815 block_nr blkno;
816 int nblk;
817 char *type;
819 register bitchunk_t *p = dmap, *q = cmap;
820 int report = 1, nerr = 0;
821 int w = nblk * WORDS_PER_BLOCK;
822 bit_nr phys = 0;
824 printf("Checking %s map. ", type);
825 if(!preen) printf("\n");
826 fflush(stdout);
827 loadbitmap(dmap, blkno, nblk);
828 do {
829 if (*p != *q) chkword(*p, *q, bit, type, &nerr, &report, phys);
830 p++;
831 q++;
832 bit += 8 * sizeof(bitchunk_t);
833 phys += 8 * sizeof(bitchunk_t);
834 } while (--w > 0);
836 if ((!repair || automatic) && !report) printf("etc. ");
837 if (nerr > MAXPRINT || nerr > 10) printf("%d errors found. ", nerr);
838 if (nerr != 0 && yes("install a new map")) dumpbitmap(cmap, blkno, nblk);
839 if (nerr > 0) printf("\n");
842 /* See if the inodes that aren't allocated are cleared. */
843 void chkilist()
845 register ino_t ino = 1;
846 mode_t mode;
848 printf("Checking inode list. ");
849 if(!preen) printf("\n");
850 fflush(stdout);
852 if (!bitset(imap, (bit_nr) ino)) {
853 devread(inoblock(ino), inooff(ino), (char *) &mode,
854 sizeof(mode));
855 if (mode != I_NOT_ALLOC) {
856 printf("mode inode %u not cleared", ino);
857 if (yes(". clear")) devwrite(inoblock(ino),
858 inooff(ino), nullbuf, INODE_SIZE);
861 while (++ino <= sb.s_ninodes && ino != 0);
862 if(!preen) printf("\n");
865 /* Allocate an array to maintain the inode reference counts in. */
866 void getcount()
868 count = (nlink_t *) alloc((unsigned) (sb.s_ninodes + 1), sizeof(nlink_t));
871 /* The reference count for inode `ino' is wrong. Ask if it should be adjusted. */
872 void counterror(ino_t ino)
874 d_inode inode;
876 if (firstcnterr) {
877 printf("INODE NLINK COUNT\n");
878 firstcnterr = 0;
880 devread(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
881 count[ino] += inode.i_nlinks; /* it was already subtracted; add it back */
882 printf("%5u %5u %5u", ino, (unsigned) inode.i_nlinks, count[ino]);
883 if (yes(" adjust")) {
884 if ((inode.i_nlinks = count[ino]) == 0) {
885 fatal("internal error (counterror)");
886 inode.i_mode = I_NOT_ALLOC;
887 clrbit(imap, (bit_nr) ino);
889 devwrite(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
893 /* Check if the reference count of the inodes are correct. The array `count'
894 * is maintained as follows: an entry indexed by the inode number is
895 * incremented each time a link is found; when the inode is read the link
896 * count in there is substracted from the corresponding entry in `count'.
897 * Thus, when the whole file system has been traversed, all the entries
898 * should be zero.
900 void chkcount()
902 register ino_t ino;
904 for (ino = 1; ino <= sb.s_ninodes && ino != 0; ino++)
905 if (count[ino] != 0) counterror(ino);
906 if (!firstcnterr) printf("\n");
909 /* Deallocate the `count' array. */
910 void freecount()
912 free((char *) count);
915 /* Print the inode permission bits given by mode and shift. */
916 void printperm(mode_t mode, int shift, int special, int overlay)
918 if (mode >> shift & R_BIT)
919 putchar('r');
920 else
921 putchar('-');
922 if (mode >> shift & W_BIT)
923 putchar('w');
924 else
925 putchar('-');
926 if (mode & special)
927 putchar(overlay);
928 else
929 if (mode >> shift & X_BIT)
930 putchar('x');
931 else
932 putchar('-');
935 /* List the given inode. */
936 void list(ino_t ino, d_inode *ip)
938 if (firstlist) {
939 firstlist = 0;
940 printf(" inode permission link size name\n");
942 printf("%6u ", ino);
943 switch (ip->i_mode & I_TYPE) {
944 case I_REGULAR: putchar('-'); break;
945 case I_DIRECTORY: putchar('d'); break;
946 case I_CHAR_SPECIAL: putchar('c'); break;
947 case I_BLOCK_SPECIAL: putchar('b'); break;
948 case I_NAMED_PIPE: putchar('p'); break;
949 case I_UNIX_SOCKET: putchar('s'); break;
950 #ifdef I_SYMBOLIC_LINK
951 case I_SYMBOLIC_LINK: putchar('l'); break;
952 #endif
953 default: putchar('?');
955 printperm(ip->i_mode, 6, I_SET_UID_BIT, 's');
956 printperm(ip->i_mode, 3, I_SET_GID_BIT, 's');
957 printperm(ip->i_mode, 0, STICKY_BIT, 't');
958 printf(" %3u ", ip->i_nlinks);
959 switch (ip->i_mode & I_TYPE) {
960 case I_CHAR_SPECIAL:
961 case I_BLOCK_SPECIAL:
962 printf(" %2x,%2x ", (dev_t) ip->i_zone[0] >> MAJOR & 0xFF,
963 (dev_t) ip->i_zone[0] >> MINOR & 0xFF);
964 break;
965 default: printf("%7ld ", 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, sizeof(dp->mfs_d_name));
1028 printf("bad %s in ", printable_name);
1029 printpath(1, 0);
1030 printf("%s is linked to %u ", printable_name, dp->d_inum);
1031 printf("instead of %u)", exp);
1032 setbit(spec_imap, (bit_nr) ino);
1033 setbit(spec_imap, (bit_nr) dp->d_inum);
1034 setbit(spec_imap, (bit_nr) exp);
1035 if (yes(". repair")) {
1036 count[dp->d_inum]--;
1037 dp->d_inum = exp;
1038 count[exp]++;
1039 return(0);
1041 } else if (pos != (dp->mfs_d_name[1] ? DIR_ENTRY_SIZE : 0)) {
1042 make_printable_name(printable_name, dp->mfs_d_name, sizeof(dp->mfs_d_name));
1043 printf("warning: %s has offset %ld in ", printable_name, pos);
1044 printpath(1, 0);
1045 printf("%s is linked to %u)\n", printable_name, dp->d_inum);
1046 setbit(spec_imap, (bit_nr) ino);
1047 setbit(spec_imap, (bit_nr) dp->d_inum);
1048 setbit(spec_imap, (bit_nr) exp);
1050 return(1);
1053 /* Check the name in a directory entry. */
1054 int chkname(ino_t ino, dir_struct *dp)
1056 register n = MFS_NAME_MAX + 1;
1057 register char *p = dp->mfs_d_name;
1059 if (*p == '\0') {
1060 printf("null name found in ");
1061 printpath(0, 0);
1062 setbit(spec_imap, (bit_nr) ino);
1063 if (Remove(dp)) return(0);
1065 while (*p != '\0' && --n != 0)
1066 if (*p++ == '/') {
1067 printf("found a '/' in entry of directory ");
1068 printpath(1, 0);
1069 setbit(spec_imap, (bit_nr) ino);
1070 printf("entry = '");
1071 printname(dp->mfs_d_name);
1072 printf("')");
1073 if (Remove(dp)) return(0);
1074 break;
1076 return(1);
1079 /* Check a directory entry. Here the routine `descendtree' is called
1080 * recursively to check the file or directory pointed to by the entry.
1082 int chkentry(ino_t ino, off_t pos, dir_struct *dp)
1084 if (dp->d_inum < ROOT_INODE || dp->d_inum > sb.s_ninodes) {
1085 printf("bad inode found in directory ");
1086 printpath(1, 0);
1087 printf("ino found = %u, ", dp->d_inum);
1088 printf("name = '");
1089 printname(dp->mfs_d_name);
1090 printf("')");
1091 if (yes(". remove entry")) {
1092 memset((void *) dp, 0, sizeof(dir_struct));
1093 return(0);
1095 return(1);
1097 if ((unsigned) count[dp->d_inum] == SHRT_MAX) {
1098 printf("too many links to ino %u\n", dp->d_inum);
1099 printf("discovered at entry '");
1100 printname(dp->mfs_d_name);
1101 printf("' in directory ");
1102 printpath(0, 1);
1103 if (Remove(dp)) return(0);
1105 count[dp->d_inum]++;
1106 if (strcmp(dp->mfs_d_name, ".") == 0) {
1107 ftop->st_presence |= DOT;
1108 return(chkdots(ino, pos, dp, ino));
1110 if (strcmp(dp->mfs_d_name, "..") == 0) {
1111 ftop->st_presence |= DOTDOT;
1112 return(chkdots(ino, pos, dp, ino == ROOT_INODE ? ino :
1113 ftop->st_next->st_dir->d_inum));
1115 if (!chkname(ino, dp)) return(0);
1116 if (bitset(dirmap, (bit_nr) dp->d_inum)) {
1117 printf("link to directory discovered in ");
1118 printpath(1, 0);
1119 printf("name = '");
1120 printname(dp->mfs_d_name);
1121 printf("', dir ino = %u)", dp->d_inum);
1122 return !Remove(dp);
1124 return(descendtree(dp));
1127 /* Check a zone of a directory by checking all the entries in the zone.
1128 * The zone is split up into chunks to not allocate too much stack.
1130 int chkdirzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno)
1132 dir_struct dirblk[CDIRECT];
1133 register dir_struct *dp;
1134 register n, dirty;
1135 long block= ztob(zno);
1136 register long offset = 0;
1137 register off_t size = 0;
1138 n = SCALE * (NR_DIR_ENTRIES(block_size) / CDIRECT);
1140 do {
1141 devread(block, offset, (char *) dirblk, DIRCHUNK);
1142 dirty = 0;
1143 for (dp = dirblk; dp < &dirblk[CDIRECT]; dp++) {
1144 if (dp->d_inum != NO_ENTRY && !chkentry(ino, pos, dp))
1145 dirty = 1;
1146 pos += DIR_ENTRY_SIZE;
1147 if (dp->d_inum != NO_ENTRY) size = pos;
1149 if (dirty) devwrite(block, offset, (char *) dirblk, DIRCHUNK);
1150 offset += DIRCHUNK;
1151 n--;
1152 } while (n > 0);
1154 if (size > ip->i_size) {
1155 printf("size not updated of directory ");
1156 printpath(2, 0);
1157 if (yes(". extend")) {
1158 setbit(spec_imap, (bit_nr) ino);
1159 ip->i_size = size;
1160 devwrite(inoblock(ino), inooff(ino), (char *) ip, INODE_SIZE);
1163 return(1);
1167 int chksymlinkzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno)
1169 long block;
1170 size_t len;
1171 char target[PATH_MAX+1];
1173 if (ip->i_size > PATH_MAX)
1174 fatal("chksymlinkzone: fsck program inconsistency\n");
1175 block= ztob(zno);
1176 devread(block, 0, target, ip->i_size);
1177 target[ip->i_size]= '\0';
1178 len= strlen(target);
1179 if (len != ip->i_size)
1181 printf("bad size in symbolic link (%d instead of %d) ",
1182 ip->i_size, len);
1183 printpath(2, 0);
1184 if (yes(". update")) {
1185 setbit(spec_imap, (bit_nr) ino);
1186 ip->i_size = len;
1187 devwrite(inoblock(ino), inooff(ino),
1188 (char *) ip, INODE_SIZE);
1191 return 1;
1194 /* There is something wrong with the given zone. Print some details. */
1195 void errzone(mess, zno, level, pos)
1196 char *mess;
1197 zone_nr zno;
1198 int level;
1199 off_t pos;
1201 printf("%s zone in ", mess);
1202 printpath(1, 0);
1203 printf("zno = %ld, type = ", zno);
1204 switch (level) {
1205 case 0: printf("DATA"); break;
1206 case 1: printf("SINGLE INDIRECT"); break;
1207 case 2: printf("DOUBLE INDIRECT"); break;
1208 default: printf("VERY INDIRECT");
1210 printf(", pos = %ld)\n", pos);
1213 /* Found the given zone in the given inode. Check it, and if ok, mark it
1214 * in the zone bitmap.
1216 int markzone(zno, level, pos)
1217 zone_nr zno;
1218 int level;
1219 off_t pos;
1221 register bit_nr bit = (bit_nr) zno - FIRST + 1;
1223 ztype[level]++;
1224 if (zno < FIRST || zno >= sb.s_zones) {
1225 errzone("out-of-range", zno, level, pos);
1226 return(0);
1228 if (bitset(zmap, bit)) {
1229 setbit(spec_zmap, bit);
1230 errzone("duplicate", zno, level, pos);
1231 return(0);
1233 nfreezone--;
1234 if (bitset(spec_zmap, bit)) errzone("found", zno, level, pos);
1235 setbit(zmap, bit);
1236 return(1);
1239 /* Check an indirect zone by checking all of its entries.
1240 * The zone is split up into chunks to not allocate too much stack.
1242 int chkindzone(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)
1244 zone_nr indirect[CINDIR];
1245 register n = NR_INDIRECTS / CINDIR;
1246 long block= ztob(zno);
1247 register long offset = 0;
1249 do {
1250 devread(block, offset, (char *) indirect, INDCHUNK);
1251 if (!chkzones(ino, ip, pos, indirect, CINDIR, level - 1)) return(0);
1252 offset += INDCHUNK;
1253 } while (--n && *pos < ip->i_size);
1254 return(1);
1257 /* Return the size of a gap in the file, represented by a null zone number
1258 * at some level of indirection.
1260 off_t jump(level)
1261 int level;
1263 off_t power = ZONE_SIZE;
1265 if (level != 0) do
1266 power *= NR_INDIRECTS;
1267 while (--level);
1268 return(power);
1271 /* Check a zone, which may be either a normal data zone, a directory zone,
1272 * or an indirect zone.
1274 int zonechk(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)
1276 if (level == 0) {
1277 if ((ip->i_mode & I_TYPE) == I_DIRECTORY &&
1278 !chkdirzone(ino, ip, *pos, zno))
1279 return(0);
1280 if ((ip->i_mode & I_TYPE) == I_SYMBOLIC_LINK &&
1281 !chksymlinkzone(ino, ip, *pos, zno))
1282 return(0);
1283 *pos += ZONE_SIZE;
1284 return(1);
1285 } else
1286 return chkindzone(ino, ip, pos, zno, level);
1289 /* Check a list of zones given by `zlist'. */
1290 int chkzones(ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist,
1291 int len, int level)
1293 register ok = 1, i;
1295 /* The check on the position in the next loop is commented out, since FS
1296 * now requires valid zone numbers in each level that is necessary and FS
1297 * always deleted all the zones in the double indirect block.
1299 for (i = 0; i < len /* && *pos < ip->i_size */ ; i++)
1300 if (zlist[i] == NO_ZONE)
1301 *pos += jump(level);
1302 else if (!markzone(zlist[i], level, *pos)) {
1303 *pos += jump(level);
1304 ok = 0;
1305 } else if (!zonechk(ino, ip, pos, zlist[i], level))
1306 ok = 0;
1307 return(ok);
1310 /* Check a file or a directory. */
1311 int chkfile(ino_t ino, d_inode *ip)
1313 register ok, i, level;
1314 off_t pos = 0;
1316 ok = chkzones(ino, ip, &pos, &ip->i_zone[0], NR_DZONE_NUM, 0);
1317 for (i = NR_DZONE_NUM, level = 1; i < NR_ZONE_NUMS; i++, level++)
1318 ok &= chkzones(ino, ip, &pos, &ip->i_zone[i], 1, level);
1319 return(ok);
1322 /* Check a directory by checking the contents. Check if . and .. are present. */
1323 int chkdirectory(ino_t ino, d_inode *ip)
1325 register ok;
1327 setbit(dirmap, (bit_nr) ino);
1328 ok = chkfile(ino, ip);
1329 if (!(ftop->st_presence & DOT)) {
1330 printf(". missing in ");
1331 printpath(2, 1);
1332 ok = 0;
1334 if (!(ftop->st_presence & DOTDOT)) {
1335 printf(".. missing in ");
1336 printpath(2, 1);
1337 ok = 0;
1339 return(ok);
1342 #ifdef I_SYMBOLIC_LINK
1344 /* Check the validity of a symbolic link. */
1345 int chklink(ino_t ino, d_inode *ip)
1347 int ok;
1349 ok = chkfile(ino, ip);
1350 if (ip->i_size <= 0 || ip->i_size > block_size) {
1351 if (ip->i_size == 0)
1352 printf("empty symbolic link ");
1353 else
1354 printf("symbolic link too large (size %ld) ", ip->i_size);
1355 printpath(2, 1);
1356 ok = 0;
1358 return(ok);
1361 #endif
1363 /* Check the validity of a special file. */
1364 int chkspecial(ino_t ino, d_inode *ip)
1366 int i, ok;
1368 ok = 1;
1369 if ((dev_t) ip->i_zone[0] == NO_DEV) {
1370 printf("illegal device number %ld for special file ", ip->i_zone[0]);
1371 printpath(2, 1);
1372 ok = 0;
1375 /* FS will not use the remaining "zone numbers" but 1.6.11++ will panic if
1376 * they are nonzero, since this should not happen.
1378 for (i = 1; i < NR_ZONE_NUMS; i++)
1379 if (ip->i_zone[i] != NO_ZONE) {
1380 printf("nonzero zone number %ld for special file ",
1381 ip->i_zone[i]);
1382 printpath(2, 1);
1383 ok = 0;
1385 return(ok);
1388 /* Check the mode and contents of an inode. */
1389 int chkmode(ino_t ino, d_inode *ip)
1391 switch (ip->i_mode & I_TYPE) {
1392 case I_REGULAR:
1393 nregular++;
1394 return chkfile(ino, ip);
1395 case I_DIRECTORY:
1396 ndirectory++;
1397 return chkdirectory(ino, ip);
1398 case I_BLOCK_SPECIAL:
1399 nblkspec++;
1400 return chkspecial(ino, ip);
1401 case I_CHAR_SPECIAL:
1402 ncharspec++;
1403 return chkspecial(ino, ip);
1404 case I_NAMED_PIPE:
1405 npipe++;
1406 return chkfile(ino, ip);
1407 case I_UNIX_SOCKET:
1408 nsock++;
1409 return chkfile(ino, ip);
1410 #ifdef I_SYMBOLIC_LINK
1411 case I_SYMBOLIC_LINK:
1412 nsyml++;
1413 return chklink(ino, ip);
1414 #endif
1415 default:
1416 nbadinode++;
1417 printf("bad mode of ");
1418 printpath(1, 0);
1419 printf("mode = %o)", ip->i_mode);
1420 return(0);
1424 /* Check an inode. */
1425 int chkinode(ino_t ino, d_inode *ip)
1427 if (ino == ROOT_INODE && (ip->i_mode & I_TYPE) != I_DIRECTORY) {
1428 printf("root inode is not a directory ");
1429 printf("(ino = %u, mode = %o)\n", ino, ip->i_mode);
1430 fatal("");
1432 if (ip->i_nlinks == 0) {
1433 printf("link count zero of ");
1434 printpath(2, 0);
1435 return(0);
1437 nfreeinode--;
1438 setbit(imap, (bit_nr) ino);
1439 if ((unsigned) ip->i_nlinks > SHRT_MAX) {
1440 printf("link count too big in ");
1441 printpath(1, 0);
1442 printf("cnt = %u)\n", (unsigned) ip->i_nlinks);
1443 count[ino] -= SHRT_MAX;
1444 setbit(spec_imap, (bit_nr) ino);
1445 } else {
1446 count[ino] -= (unsigned) ip->i_nlinks;
1448 return chkmode(ino, ip);
1451 /* Check the directory entry pointed to by dp, by checking the inode. */
1452 int descendtree(dp)
1453 dir_struct *dp;
1455 d_inode inode;
1456 register ino_t ino = dp->d_inum;
1457 register visited;
1458 struct stack stk;
1460 stk.st_dir = dp;
1461 stk.st_next = ftop;
1462 ftop = &stk;
1463 if (bitset(spec_imap, (bit_nr) ino)) {
1464 printf("found inode %u: ", ino);
1465 printpath(0, 1);
1467 visited = bitset(imap, (bit_nr) ino);
1468 if (!visited || listing) {
1469 devread(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
1470 if (listing) list(ino, &inode);
1471 if (!visited && !chkinode(ino, &inode)) {
1472 setbit(spec_imap, (bit_nr) ino);
1473 if (yes("remove")) {
1474 count[ino] += inode.i_nlinks - 1;
1475 clrbit(imap, (bit_nr) ino);
1476 devwrite(inoblock(ino), inooff(ino),
1477 nullbuf, INODE_SIZE);
1478 memset((void *) dp, 0, sizeof(dir_struct));
1479 ftop = ftop->st_next;
1480 return(0);
1484 ftop = ftop->st_next;
1485 return(1);
1488 /* Check the file system tree. */
1489 void chktree()
1491 dir_struct dir;
1493 nfreeinode = sb.s_ninodes;
1494 nfreezone = N_DATA;
1495 dir.d_inum = ROOT_INODE;
1496 dir.mfs_d_name[0] = 0;
1497 if (!descendtree(&dir)) fatal("bad root inode");
1498 putchar('\n');
1501 /* Print the totals of all the objects found. */
1502 void printtotal()
1504 if(preen) {
1505 printf("%d files, %d directories, %d free inodes, %d free zones\n",
1506 nregular, ndirectory, nfreeinode, nfreezone);
1507 return;
1510 printf("blocksize = %5d ", block_size);
1511 printf("zonesize = %5d\n", ZONE_SIZE);
1512 printf("\n");
1513 pr("%8u Regular file%s\n", nregular, "", "s");
1514 pr("%8u Director%s\n", ndirectory, "y", "ies");
1515 pr("%8u Block special file%s\n", nblkspec, "", "s");
1516 pr("%8u Character special file%s\n", ncharspec, "", "s");
1517 if (nbadinode != 0) pr("%6u Bad inode%s\n", nbadinode, "", "s");
1518 pr("%8u Free inode%s\n", nfreeinode, "", "s");
1519 pr("%8u Named pipe%s\n", npipe, "", "s");
1520 pr("%8u Unix socket%s\n", nsock, "", "s");
1521 pr("%8u Symbolic link%s\n", nsyml, "", "s");
1522 /* Don't print some fields.
1523 printf("\n");
1524 pr("%8u Data zone%s\n", ztype[0], "", "s");
1525 pr("%8u Single indirect zone%s\n", ztype[1], "", "s");
1526 pr("%8u Double indirect zone%s\n", ztype[2], "", "s");
1528 lpr("%8ld Free zone%s\n", nfreezone, "", "s");
1531 /* Check the device which name is given by `f'. The inodes listed by `clist'
1532 * should be listed separately, and the inodes listed by `ilist' and the zones
1533 * listed by `zlist' should be watched for while checking the file system.
1536 void chkdev(f, clist, ilist, zlist)
1537 char *f, **clist, **ilist, **zlist;
1539 if (automatic) repair = 1;
1540 fsck_device = f;
1541 initvars();
1543 devopen();
1545 rw_super(SUPER_GET);
1547 if(block_size < _MIN_BLOCK_SIZE)
1548 fatal("funny block size");
1550 if(!(rwbuf = malloc(block_size))) fatal("couldn't allocate fs buf (1)");
1551 if(!(nullbuf = malloc(block_size))) fatal("couldn't allocate fs buf (2)");
1552 memset(nullbuf, 0, block_size);
1554 chksuper();
1556 if(markdirty) {
1557 if(sb.s_flags & MFSFLAG_CLEAN) {
1558 sb.s_flags &= ~MFSFLAG_CLEAN;
1559 rw_super(SUPER_PUT);
1560 printf("\n----- FILE SYSTEM MARKED DIRTY -----\n\n");
1561 } else {
1562 printf("Filesystem is already dirty.\n");
1566 /* If preening, skip fsck if clean flag is on. */
1567 if(preen) {
1568 if(sb.s_flags & MFSFLAG_CLEAN) {
1569 printf("%s: clean\n", f);
1570 return;
1572 printf("%s: dirty, performing fsck\n", f);
1575 lsi(clist);
1577 getbitmaps();
1579 fillbitmap(spec_imap, (bit_nr) 1, (bit_nr) sb.s_ninodes + 1, ilist);
1580 fillbitmap(spec_zmap, (bit_nr) FIRST, (bit_nr) sb.s_zones, zlist);
1582 getcount();
1583 chktree();
1584 chkmap(zmap, spec_zmap, (bit_nr) FIRST - 1, BLK_ZMAP, N_ZMAP, "zone");
1585 chkcount();
1586 chkmap(imap, spec_imap, (bit_nr) 0, BLK_IMAP, N_IMAP, "inode");
1587 chkilist();
1588 if(preen) printf("\n");
1589 printtotal();
1591 putbitmaps();
1592 freecount();
1594 if (changed) printf("\n----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n");
1596 /* If we were told to repair the FS, and the user never stopped us from
1597 * doing it, and the FS wasn't marked clean, we can mark the FS as clean.
1598 * If we were stopped from repairing, tell user about it.
1600 if(repair && !(sb.s_flags & MFSFLAG_CLEAN)) {
1601 if(notrepaired) {
1602 printf("\n----- FILE SYSTEM STILL DIRTY -----\n\n");
1603 } else {
1604 sync(); /* update FS on disk before clean flag */
1605 sb.s_flags |= MFSFLAG_CLEAN;
1606 rw_super(SUPER_PUT);
1607 printf("\n----- FILE SYSTEM MARKED CLEAN -----\n\n");
1611 devclose();
1614 int main(argc, argv)
1615 int argc;
1616 char **argv;
1618 register char **clist = 0, **ilist = 0, **zlist = 0;
1619 int badflag = 0;
1621 register devgiven = 0;
1622 register char *arg;
1624 if ((1 << BITSHIFT) != 8 * sizeof(bitchunk_t)) {
1625 printf("Fsck was compiled with the wrong BITSHIFT!\n");
1626 exit(FSCK_EXIT_CHECK_FAILED);
1629 sync();
1630 prog = *argv++;
1631 while ((arg = *argv++) != 0)
1632 if (arg[0] == '-' && arg[1] != 0 && arg[2] == 0) switch (arg[1]) {
1633 case 'd': markdirty = 1; break;
1634 case 'p': preen = repair = automatic = 1; break;
1635 case 'y':
1636 case 'a': automatic ^= 1; break;
1637 case 'c':
1638 clist = getlist(&argv, "inode");
1639 break;
1640 case 'i':
1641 ilist = getlist(&argv, "inode");
1642 break;
1643 case 'z':
1644 zlist = getlist(&argv, "zone");
1645 break;
1646 case 'r': repair ^= 1; break;
1647 case 'l': listing ^= 1; break;
1648 case 's': listsuper ^= 1; break;
1649 case 'f': break;
1650 default:
1651 printf("%s: unknown flag '%s'\n", prog, arg);
1652 badflag = 1;
1654 else {
1655 chkdev(arg, clist, ilist, zlist);
1656 clist = 0;
1657 ilist = 0;
1658 zlist = 0;
1659 devgiven = 1;
1661 if (!devgiven || badflag) {
1662 printf("Usage: fsck [-dyfpacilrsz] file\n");
1663 exit(FSCK_EXIT_USAGE);
1665 return(0);